diff options
author | AlexSm <alex@ydb.tech> | 2023-12-22 17:10:22 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-22 17:10:22 +0100 |
commit | 148f920350c60c0ca2d89b637a5aea9093eee450 (patch) | |
tree | 6314b1433dac833398c333731e83f0ad77e81a0b | |
parent | 7116d46ae7c0259b5f9d489de263f8701e432b1c (diff) | |
download | ydb-148f920350c60c0ca2d89b637a5aea9093eee450.tar.gz |
Library import 2 (#639)
588 files changed, 21493 insertions, 33328 deletions
diff --git a/build/conf/java.conf b/build/conf/java.conf index 5b926fba7f..d161c0d4c0 100644 --- a/build/conf/java.conf +++ b/build/conf/java.conf @@ -272,7 +272,7 @@ module EXTERNAL_JAVA_LIBRARY: _BASE_UNIT { .ALIASES=SRCS=_SRCS_NO_GLOBAL .ALLOWED=EMBED_JAVA_VCS_INFO .RESTRICTED=EXTERNAL_JAR - .GLOBAL=MAVEN_EXPORT_COORDS EXPORT_GRADLE_CLASSPATH + .GLOBAL=MAVEN_EXPORT_COORDS PEERDIR(build/platform/java/jdk) PEERDIR+=$JDK_RESOURCE_PEERDIR $EXTERNAL_JAVA_EXTRA_PEERDIR @@ -440,7 +440,6 @@ module _JAR_BASE: _BARE_UNIT { .ALLOWED=EMBED_JAVA_VCS_INFO DEPENDENCY_MANAGEMENT EXCLUDE .DEFAULT_NAME_GENERATOR=TwoDirNames .RESTRICTED=WITH_JDK RESOURCE RESOURCE_FILES - .GLOBAL=EXPORT_GRADLE_CLASSPATH PEERDIR_TAGS=JAVA_PROTO JAVA_FBS JAVA_IDL DLL JAR_COMPILATION __EMPTY__ @@ -506,8 +505,6 @@ JAVA_CONTRIB_SEM= \ consumer-classpath $EXPORT_GRADLE_CLASSPATH \ && consumer-jar ${MODDIR}/${REALPRJNAME}.jar \ && consumer-type contrib \ - && peers_closure $MANAGED_PEERS_CLOSURE \ - && peers_closure_coords $EXPORT_GRADLE_CLASSPATH_GLOBAL \ && IGNORED # tag:java-specific @@ -516,7 +513,7 @@ module JAVA_CONTRIB: _JAR_BASE { .PEERDIR_POLICY=as_include .SEM=JAVA_CONTRIB_SEM .FINAL_TARGET=yes - .GLOBAL=MAVEN_EXPORT_COORDS EXPORT_GRADLE_CLASSPATH + .GLOBAL=MAVEN_EXPORT_COORDS when ($JAR_RESOURCE_ID) { FETCH_TARGET_JAR= && $_FETCH_CONTRIB($JAR_RESOURCE_ID ${BINDIR}/${MODULE_PREFIX}${REALPRJNAME}${MODULE_SUFFIX}) @@ -868,7 +865,6 @@ BUILD_JAR_SEM= \ && consumer-classpath $EXPORT_GRADLE_CLASSPATH \ && consumer-jar ${MODDIR}/${REALPRJNAME}.jar \ && consumer-type library \ - && excludes_rules $EXCLUDE_VALUE \ $_KOTLIN_SEM \ $_JAR_MAIN_SEM \ $_GRADLE_EXPORT_PUBLISHING_SEM \ @@ -894,7 +890,7 @@ module JAR_LIBRARY: _COMPILABLE_JAR_BASE { .FINAL_TARGET=yes .ALIASES=JAVA_SRCS=FULL_JAVA_SRCS ANNOTATION_PROCESSOR=JAR_ANNOTATION_PROCESSOR .RESTRICTED=EXTERNAL_JAR - .GLOBAL=MAVEN_EXPORT_COORDS EXPORT_GRADLE_CLASSPATH + .GLOBAL=MAVEN_EXPORT_COORDS MODULE_SUFFIX=.jar PEERDIR(build/platform/java/jdk) diff --git a/build/conf/proto.conf b/build/conf/proto.conf index d1863c8f71..ef6cf8ee15 100644 --- a/build/conf/proto.conf +++ b/build/conf/proto.conf @@ -30,6 +30,7 @@ CPP_PROTO_OUTS= CPP_PROTO_OUTS_SEM= CPP_PROTO_SUFFIXES=.pb.h .pb.cc CPP_PROTO_PLUGINS= +CPP_PROTO_NO_DBGINFO=no # tag:proto tag:cpp-specific CPP_EV_OPTS=--plugin=protoc-gen-event2cpp=${tool:"tools/event2cpp"} --event2cpp_out=$ARCADIA_BUILD_ROOT -I=$ARCADIA_ROOT/library/cpp/eventlog @@ -50,6 +51,14 @@ JAVA_PROTO_ARGS= # tag:proto tag:python-specific OPTIMIZE_PY_PROTOS_FLAG=no +# tag:proto +### @usage: CPP_PROTOLIBS_DEBUG_INFO() +### +### Eqvivalent to NO_DEBUG_INFO() macro if the flag CPP_PROTO_NO_DBGINFO=yes +macro CPP_PROTOLIBS_DEBUG_INFO() { + SET(NO_DEBUGINFO $CPP_PROTO_NO_DBGINFO) +} + # tag:internal ### @usage: _ORDER_ADDINCL([BUILD ...] [SOURCE ...] Args...) # internal ### @@ -638,6 +647,7 @@ multimodule PROTO_LIBRARY { ENABLE(CPP_PROTO) ENABLE(GEN_PROTO) NO_CLANG_TIDY() + CPP_PROTOLIBS_DEBUG_INFO() SET(PEERDIR_TAGS CPP_PROTO) when ($BUILD_PROTO_AS_EVLOG == "yes" && $USE_VANILLA_PROTOC == "yes") { diff --git a/build/export_generators/gradle/build.gradle.kts.jinja b/build/export_generators/gradle/build.gradle.kts.jinja index 327b8fc8de..648c298ae5 100644 --- a/build/export_generators/gradle/build.gradle.kts.jinja +++ b/build/export_generators/gradle/build.gradle.kts.jinja @@ -44,10 +44,11 @@ dependencies { {% for library in target.consumer -%} {% set classpath = library.classpath -%} {% if targets|selectattr("app_main_class") -%} -{% if target.lib_excludes is defined and target.lib_excludes[classpath]|length > 0 -%} +{% if library.excludes.consumer is defined %} implementation({{ classpath }}) { -{% for exclude in target.lib_excludes[classpath] -%} - exclude group: '{{ exclude[0] }}', module: '{{ exclude[1] }}' +{% for exclude in library.excludes.consumer -%} +{% set classpath_parts = exclude.classpath.split(':') -%} + exclude group: '{{ classpath_parts[0] }}', module: '{{ classpath_parts[1] }}' {% endfor -%} } {% else -%} diff --git a/build/export_generators/gradle/generator.toml b/build/export_generators/gradle/generator.toml index a06a684a5e..ba289b8db0 100644 --- a/build/export_generators/gradle/generator.toml +++ b/build/export_generators/gradle/generator.toml @@ -20,9 +20,6 @@ add_vcs_info_to_mf="bool" junit4_test="flag" junit5_test="flag" app_main_class="str" -peers_closure="list" -peers_closure_coords="list" -excludes_rules="list" publish="flag" publish_group="str" diff --git a/build/export_generators/ide-gradle/build.gradle.kts.jinja b/build/export_generators/ide-gradle/build.gradle.kts.jinja index 3d629c89d8..de22a0f850 100644 --- a/build/export_generators/ide-gradle/build.gradle.kts.jinja +++ b/build/export_generators/ide-gradle/build.gradle.kts.jinja @@ -68,10 +68,11 @@ dependencies { {% for library in target.consumer -%} {% set classpath = library.classpath -%} {% if targets|selectattr("app_main_class") -%} -{% if target.lib_excludes is defined and target.lib_excludes[classpath]|length > 0 -%} +{% if library.excludes.consumer is defined %} implementation({{ classpath }}) { -{% for exclude in target.lib_excludes[classpath] -%} - exclude group: '{{ exclude[0] }}', module: '{{ exclude[1] }}' +{% for exclude in library.excludes.consumer -%} +{% set classpath_parts = exclude.classpath.split(':') -%} + exclude group: '{{ classpath_parts[0] }}', module: '{{ classpath_parts[1] }}' {% endfor -%} } {% else -%} diff --git a/build/export_generators/ide-gradle/generator.toml b/build/export_generators/ide-gradle/generator.toml index 9728685a12..409d32a05a 100644 --- a/build/export_generators/ide-gradle/generator.toml +++ b/build/export_generators/ide-gradle/generator.toml @@ -19,9 +19,6 @@ add_vcs_info_to_mf="bool" junit4_test="flag" junit5_test="flag" app_main_class="str" -peers_closure="list" -peers_closure_coords="list" -excludes_rules="list" jar_source_set="list" publish="flag" diff --git a/build/external_resources/yexport/resources.json b/build/external_resources/yexport/resources.json index a4d49b6e1e..837dc6a653 100644 --- a/build/external_resources/yexport/resources.json +++ b/build/external_resources/yexport/resources.json @@ -1,13 +1,13 @@ { "by_platform": { "darwin": { - "uri": "sbr:5534004877" + "uri": "sbr:5562968267" }, "darwin-arm64": { - "uri": "sbr:5534041285" + "uri": "sbr:5562965675" }, "linux": { - "uri": "sbr:5534030737" + "uri": "sbr:5562947428" } } } diff --git a/build/mapping.conf.json b/build/mapping.conf.json index 4dba652eb3..160a3bb151 100644 --- a/build/mapping.conf.json +++ b/build/mapping.conf.json @@ -37,6 +37,7 @@ "5424038053": "https://devtools-registry.s3.yandex.net/5424038053", "5424051723": "https://devtools-registry.s3.yandex.net/5424051723", "5543659225": "https://devtools-registry.s3.yandex.net/5543659225", + "5560184603": "https://devtools-registry.s3.yandex.net/5560184603", "5553311553": "https://devtools-registry.s3.yandex.net/5553311553", "5554110935": "https://devtools-registry.s3.yandex.net/5554110935", "5554115224": "https://devtools-registry.s3.yandex.net/5554115224", @@ -94,6 +95,7 @@ "5530946535": "https://devtools-registry.s3.yandex.net/5530946535", "5545693767": "https://devtools-registry.s3.yandex.net/5545693767", "5545709989": "https://devtools-registry.s3.yandex.net/5545709989", + "5559549864": "https://devtools-registry.s3.yandex.net/5559549864", "4307890075": "https://devtools-registry.s3.yandex.net/4307890075", "5517245192": "https://devtools-registry.s3.yandex.net/5517245192", "4307901240": "https://devtools-registry.s3.yandex.net/4307901240", @@ -111,12 +113,15 @@ "5486584798": "https://devtools-registry.s3.yandex.net/5486584798", "5498749381": "https://devtools-registry.s3.yandex.net/5498749381", "5534092912": "https://devtools-registry.s3.yandex.net/5534092912", + "5562971772": "https://devtools-registry.s3.yandex.net/5562971772", "5486590469": "https://devtools-registry.s3.yandex.net/5486590469", "5498750509": "https://devtools-registry.s3.yandex.net/5498750509", "5534043120": "https://devtools-registry.s3.yandex.net/5534043120", + "5562958333": "https://devtools-registry.s3.yandex.net/5562958333", "5486590393": "https://devtools-registry.s3.yandex.net/5486590393", "5498735180": "https://devtools-registry.s3.yandex.net/5498735180", "5534059422": "https://devtools-registry.s3.yandex.net/5534059422", + "5562961825": "https://devtools-registry.s3.yandex.net/5562961825", "5476908047": "https://devtools-registry.s3.yandex.net/5476908047", "5509380757": "https://devtools-registry.s3.yandex.net/5509380757", "5550834592": "https://devtools-registry.s3.yandex.net/5550834592", @@ -132,7 +137,8 @@ "5476896707": "https://devtools-registry.s3.yandex.net/5476896707", "5509388614": "https://devtools-registry.s3.yandex.net/5509388614", "5550838970": "https://devtools-registry.s3.yandex.net/5550838970", - "2980468199": "https://devtools-registry.s3.yandex.net/2980468199" + "2980468199": "https://devtools-registry.s3.yandex.net/2980468199", + "5562224408": "https://devtools-registry.s3.yandex.net/5562224408" }, "resources_descriptions": { "2214720943": "Clang 11.0.0 for darwin-arm64", @@ -171,6 +177,7 @@ "5424038053": "OTHER_RESOURCE-none-1.21.3-y_go1.21.3.linux-arm64.tar.gz", "5424051723": "OTHER_RESOURCE-none-1.21.3-y_go1.21.3.windows-amd64.tar.gz", "5543659225": "OTHER_RESOURCE-none-none-clang-darwin-arm64.tgz", + "5560184603": "OTHER_RESOURCE-none-none-clang-mingw64.tgz", "5553311553": "OTHER_RESOURCE-none-none-clang-new-darwin-arm64.tgz", "5554110935": "OTHER_RESOURCE-none-none-clang-new-darwin-x86_64.tgz", "5554115224": "OTHER_RESOURCE-none-none-clang-new-linux-aarch64.tgz", @@ -228,6 +235,7 @@ "5530946535": "devtools/ya/test/programs/test_tool/bin3/test_tool3 for linux", "5545693767": "devtools/ya/test/programs/test_tool/bin3/test_tool3 for linux", "5545709989": "devtools/ya/test/programs/test_tool/bin3/test_tool3 for linux", + "5559549864": "devtools/ya/test/programs/test_tool/bin3/test_tool3 for linux", "4307890075": "flake8_linter for linux", "5517245192": "flake8_linter for linux", "4307901240": "flake8_linter for linux-aarch64", @@ -245,12 +253,15 @@ "5486584798": "yexport for darwin", "5498749381": "yexport for darwin", "5534092912": "yexport for darwin", + "5562971772": "yexport for darwin", "5486590469": "yexport for darwin-arm64", "5498750509": "yexport for darwin-arm64", "5534043120": "yexport for darwin-arm64", + "5562958333": "yexport for darwin-arm64", "5486590393": "yexport for linux", "5498735180": "yexport for linux", "5534059422": "yexport for linux", + "5562961825": "yexport for linux", "5476908047": "ymake for darwin", "5509380757": "ymake for darwin", "5550834592": "ymake for darwin", @@ -266,8 +277,9 @@ "5476896707": "ymake.exe for win32-clang-cl", "5509388614": "ymake.exe for win32-clang-cl", "5550838970": "ymake.exe for win32-clang-cl", - "2980468199": "ytexec for linux" + "2980468199": "ytexec for linux", + "5562224408": "ytexec for linux" }, "resources_info": {}, "tasks": {} -}
\ No newline at end of file +} diff --git a/build/platform/clang/clang16.json b/build/platform/clang/clang16.json index 02608fadf7..2a413673d7 100644 --- a/build/platform/clang/clang16.json +++ b/build/platform/clang/clang16.json @@ -13,7 +13,7 @@ "uri": "sbr:5550376885" }, "win32-x86_64": { - "uri": "sbr:4597657641" + "uri": "sbr:5560184603" } } } diff --git a/build/platform/test_tool/host.ya.make.inc b/build/platform/test_tool/host.ya.make.inc index 3c1b17179a..410298c40c 100644 --- a/build/platform/test_tool/host.ya.make.inc +++ b/build/platform/test_tool/host.ya.make.inc @@ -1,17 +1,17 @@ IF (HOST_OS_DARWIN AND HOST_ARCH_X86_64) - DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:5530946966) - DECLARE_EXTERNAL_RESOURCE(TEST_TOOL3_HOST sbr:5530945085) + DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:5559522450) + DECLARE_EXTERNAL_RESOURCE(TEST_TOOL3_HOST sbr:5559535346) ELSEIF (HOST_OS_DARWIN AND HOST_ARCH_ARM64) - DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:5530946287) - DECLARE_EXTERNAL_RESOURCE(TEST_TOOL3_HOST sbr:5530944412) + DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:5559521542) + DECLARE_EXTERNAL_RESOURCE(TEST_TOOL3_HOST sbr:5559534336) ELSEIF (HOST_OS_LINUX AND HOST_ARCH_X86_64) - DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:5530948376) - DECLARE_EXTERNAL_RESOURCE(TEST_TOOL3_HOST sbr:5530946535) + DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:5559524010) + DECLARE_EXTERNAL_RESOURCE(TEST_TOOL3_HOST sbr:5559537477) ELSEIF (HOST_OS_LINUX AND HOST_ARCH_AARCH64) - DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:5530945697) - DECLARE_EXTERNAL_RESOURCE(TEST_TOOL3_HOST sbr:5530943651) + DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:5559520978) + DECLARE_EXTERNAL_RESOURCE(TEST_TOOL3_HOST sbr:5559533477) ELSEIF (HOST_OS_WINDOWS AND HOST_ARCH_X86_64) - DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:5530947626) - DECLARE_EXTERNAL_RESOURCE(TEST_TOOL3_HOST sbr:5530945871) + DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:5559523230) + DECLARE_EXTERNAL_RESOURCE(TEST_TOOL3_HOST sbr:5559536195) ENDIF() diff --git a/build/platform/test_tool/host_os.ya.make.inc b/build/platform/test_tool/host_os.ya.make.inc index 6255a24e93..6041feb963 100644 --- a/build/platform/test_tool/host_os.ya.make.inc +++ b/build/platform/test_tool/host_os.ya.make.inc @@ -1,17 +1,17 @@ IF (HOST_OS_DARWIN AND HOST_ARCH_X86_64) - DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:5545693048) - DECLARE_EXTERNAL_RESOURCE(TEST_TOOL3_HOST sbr:5545693048) + DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:5559548658) + DECLARE_EXTERNAL_RESOURCE(TEST_TOOL3_HOST sbr:5559548658) ELSEIF (HOST_OS_DARWIN AND HOST_ARCH_ARM64) - DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:5545692674) - DECLARE_EXTERNAL_RESOURCE(TEST_TOOL3_HOST sbr:5545692674) + DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:5559548099) + DECLARE_EXTERNAL_RESOURCE(TEST_TOOL3_HOST sbr:5559548099) ELSEIF (HOST_OS_LINUX AND HOST_ARCH_X86_64) - DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:5545693767) - DECLARE_EXTERNAL_RESOURCE(TEST_TOOL3_HOST sbr:5545693767) + DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:5559549864) + DECLARE_EXTERNAL_RESOURCE(TEST_TOOL3_HOST sbr:5559549864) ELSEIF (HOST_OS_LINUX AND HOST_ARCH_AARCH64) - DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:5545692313) - DECLARE_EXTERNAL_RESOURCE(TEST_TOOL3_HOST sbr:5545692313) + DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:5559547357) + DECLARE_EXTERNAL_RESOURCE(TEST_TOOL3_HOST sbr:5559547357) ELSEIF (HOST_OS_WINDOWS AND HOST_ARCH_X86_64) - DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:5545693355) - DECLARE_EXTERNAL_RESOURCE(TEST_TOOL3_HOST sbr:5545693355) + DECLARE_EXTERNAL_RESOURCE(TEST_TOOL_HOST sbr:5559549352) + DECLARE_EXTERNAL_RESOURCE(TEST_TOOL3_HOST sbr:5559549352) ENDIF() diff --git a/build/plugins/java.py b/build/plugins/java.py index a567b2e029..69da60361d 100644 --- a/build/plugins/java.py +++ b/build/plugins/java.py @@ -483,4 +483,3 @@ def on_setup_project_coords_if_needed(unit, *args): else: value = 'project(\\":{}\\")'.format(project_dir.replace('/', ':')) unit.set(['EXPORT_GRADLE_CLASSPATH', value]) - unit.set(['EXPORT_GRADLE_CLASSPATH_GLOBAL', value]) diff --git a/build/plugins/res.py b/build/plugins/res.py index bd3d3abe23..24c183ad7f 100644 --- a/build/plugins/res.py +++ b/build/plugins/res.py @@ -56,6 +56,11 @@ def onresource_files(unit, *args): key = 'resfs/file/' + ( dest or (prefix + (path if not prefix_to_strip else remove_prefix(path, prefix_to_strip))) ) + if key in res: + unit.message( + ['warn', "Dublicated resource file {} in RESOURCE_FILES() macro. Skipped it.".format(path)] + ) + continue src = 'resfs/src/{}={}'.format(key, rootrel_arc_src(path, unit)) res += ['-', src, path, key] diff --git a/build/ya.conf.json b/build/ya.conf.json index aa6173b973..bfcc4b1c9e 100644 --- a/build/ya.conf.json +++ b/build/ya.conf.json @@ -84,6 +84,26 @@ }, "formula": "build/external_resources/gdb/resources.json" }, + "gdbnew": { + "executable": { + "gcore": [ + "gdb", + "bin", + "gcore" + ], + "gdb": [ + "gdb", + "bin", + "gdb" + ], + "gdbserver": [ + "gdb", + "bin", + "gdbserver" + ] + }, + "formula": "build/external_resources/gdb/resources.json" + }, "golang": { "executable": { "go": [ @@ -740,7 +760,7 @@ }, "platforms": [ { - "default": false, + "default": true, "host": { "os": "LINUX" }, @@ -750,7 +770,7 @@ } }, { - "default": false, + "default": true, "host": { "os": "LINUX" }, @@ -760,7 +780,7 @@ } }, { - "default": false, + "default": true, "host": { "os": "WIN" }, @@ -770,7 +790,7 @@ } }, { - "default": false, + "default": true, "host": { "os": "WIN" }, @@ -877,6 +897,40 @@ } } }, + "gdbnew": { + "env": { + "TERMINFO": [ + "$(ROOT)/gdb/lib/terminfo" + ] + }, + "platforms": [ + { + "default": true, + "host": { + "os": "LINUX" + } + }, + { + "default": true, + "host": { + "os": "DARWIN" + } + }, + { + "default": true, + "host": { + "arch": "aarch64", + "os": "LINUX" + } + } + ], + "tools": { + "gdbnew": { + "bottle": "gdbnew", + "executable": "gdb" + } + } + }, "golang": { "params": { "type": "golang", @@ -1132,13 +1186,55 @@ "atop": { "description": "Advanced System & Process Monitor" }, + "c++": { + "description": "Run C++ compiler" + }, + "c++filt": { + "description": "Run c++filt" + }, + "cc": { + "description": "Run C compiler" + }, + "clang-rename": { + "description": "Run Clang-Rename refactoring tool" + }, + "gcov": { + "description": "Run gcov" + }, "gdb": { "description": "Run gdb" }, + "gdbnew": { + "description": "Run gdb for Ubuntu 16.04 or later" + }, + "go": { + "description": "Run go tool" + }, + "gofmt": { + "description": "Run gofmt tool" + }, + "llvm-cov": { + "description": "Run llvm-cov Clang utility" + }, + "llvm-profdata": { + "description": "Run llvm-profdata Clang utility" + }, + "llvm-symbolizer": { + "description": "Run llvm-symbolizer Clang utility" + }, + "nm": { + "description": "Run nm" + }, + "objcopy": { + "description": "Run objcopy" + }, "python": { "description": "Run pseudo-python", "visible": false }, + "strip": { + "description": "Run strip utility" + }, "ya-tc": { "description": "Run ya-tc tool", "visible": false diff --git a/build/ymake.core.conf b/build/ymake.core.conf index 432ddb2dc3..5161f03424 100644 --- a/build/ymake.core.conf +++ b/build/ymake.core.conf @@ -3222,9 +3222,12 @@ macro _SRC_py3src(SRC, SRCFLAGS...) { when ($PYTHON3 == "yes") { _SRC_PYSRC_CMDLINE=$_SRC_py3src($SRC $SRCFLAGS) } -otherwise { +elsewhen ($PYTHON2 == "yes") { _SRC_PYSRC_CMDLINE=$_SRC_py2src($SRC $SRCFLAGS) } +otherwise { + _SRC_PYSRC_CMDLINE= +} # tag:src-processing tag:python-specific macro _SRC_PYSRC(SRC, SRCFLAGS...) { diff --git a/build/ymake_conf.py b/build/ymake_conf.py index 58fd7da668..fd6b6f01f9 100755 --- a/build/ymake_conf.py +++ b/build/ymake_conf.py @@ -745,10 +745,7 @@ class YMake(object): if presets: print('# Variables set from command line by -D options') for key in sorted(presets): - if key in ('MY_YMAKE_BIN', 'REAL_YMAKE_BIN'): - emit_with_ignore_comment(key, opts().presets[key]) - else: - emit(key, opts().presets[key]) + emit(key, opts().presets[key]) @staticmethod def _print_conf_content(path): @@ -759,7 +756,7 @@ class YMake(object): print('@import "${CONF_ROOT}/ymake.core.conf"') def print_settings(self): - emit_with_ignore_comment('ARCADIA_ROOT', self.arcadia.root) + pass class System(object): @@ -2501,8 +2498,6 @@ def main(): custom_conf.print_epilogue() - emit_with_ignore_comment('CONF_SCRIPT_DEPENDS', __file__) - if __name__ == '__main__': main() diff --git a/contrib/clickhouse/programs/local/LocalServer.cpp b/contrib/clickhouse/programs/local/LocalServer.cpp index 8c124e3f26..75b1aa7fda 100644 --- a/contrib/clickhouse/programs/local/LocalServer.cpp +++ b/contrib/clickhouse/programs/local/LocalServer.cpp @@ -40,7 +40,7 @@ #include <Parsers/IAST.h> #include <Parsers/ASTInsertQuery.h> #include <Common/ErrorHandlers.h> -#include <Functions/UserDefined/IUserDefinedSQLObjectsLoader.h> +#include <Functions/UserDefined/IUserDefinedSQLObjectsStorage.h> #include <Functions/registerFunctions.h> #include <AggregateFunctions/registerAggregateFunctions.h> #include <TableFunctions/registerTableFunctions.h> @@ -763,7 +763,7 @@ void LocalServer::processConfig() } /// For ClickHouse local if path is not set the loader will be disabled. - global_context->getUserDefinedSQLObjectsLoader().loadObjects(); + global_context->getUserDefinedSQLObjectsStorage().loadObjects(); LOG_DEBUG(log, "Loaded metadata."); } diff --git a/contrib/clickhouse/programs/server/Server.cpp b/contrib/clickhouse/programs/server/Server.cpp index ec6c499e5a..52078e9037 100644 --- a/contrib/clickhouse/programs/server/Server.cpp +++ b/contrib/clickhouse/programs/server/Server.cpp @@ -64,7 +64,7 @@ #include <Storages/Cache/registerRemoteFileMetadatas.h> #include <Common/NamedCollections/NamedCollectionUtils.h> #include <AggregateFunctions/registerAggregateFunctions.h> -#include <Functions/UserDefined/IUserDefinedSQLObjectsLoader.h> +#include <Functions/UserDefined/IUserDefinedSQLObjectsStorage.h> #include <Functions/registerFunctions.h> #include <TableFunctions/registerTableFunctions.h> #include <Formats/registerFormats.h> @@ -1715,7 +1715,7 @@ try /// After loading validate that default database exists database_catalog.assertDatabaseExists(default_database); /// Load user-defined SQL functions. - global_context->getUserDefinedSQLObjectsLoader().loadObjects(); + global_context->getUserDefinedSQLObjectsStorage().loadObjects(); } catch (...) { diff --git a/contrib/clickhouse/src/Functions/UserDefined/IUserDefinedSQLObjectsLoader.h b/contrib/clickhouse/src/Functions/UserDefined/IUserDefinedSQLObjectsLoader.h deleted file mode 100644 index 4c7850951b..0000000000 --- a/contrib/clickhouse/src/Functions/UserDefined/IUserDefinedSQLObjectsLoader.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include <base/types.h> - - -namespace DB -{ -class IAST; -struct Settings; -enum class UserDefinedSQLObjectType; - -/// Interface for a loader of user-defined SQL objects. -/// Implementations: UserDefinedSQLLoaderFromDisk, UserDefinedSQLLoaderFromZooKeeper -class IUserDefinedSQLObjectsLoader -{ -public: - virtual ~IUserDefinedSQLObjectsLoader() = default; - - /// Whether this loader can replicate SQL objects to another node. - virtual bool isReplicated() const { return false; } - virtual String getReplicationID() const { return ""; } - - /// Loads all objects. Can be called once - if objects are already loaded the function does nothing. - virtual void loadObjects() = 0; - - /// Stops watching. - virtual void stopWatching() {} - - /// Immediately reloads all objects, throws an exception if failed. - virtual void reloadObjects() = 0; - - /// Immediately reloads a specified object only. - virtual void reloadObject(UserDefinedSQLObjectType object_type, const String & object_name) = 0; - - /// Stores an object (must be called only by UserDefinedSQLFunctionFactory::registerFunction). - virtual bool storeObject( - UserDefinedSQLObjectType object_type, - const String & object_name, - const IAST & create_object_query, - bool throw_if_exists, - bool replace_if_exists, - const Settings & settings) = 0; - - /// Removes an object (must be called only by UserDefinedSQLFunctionFactory::unregisterFunction). - virtual bool removeObject(UserDefinedSQLObjectType object_type, const String & object_name, bool throw_if_not_exists) = 0; -}; -} diff --git a/contrib/clickhouse/src/Functions/UserDefined/IUserDefinedSQLObjectsStorage.h b/contrib/clickhouse/src/Functions/UserDefined/IUserDefinedSQLObjectsStorage.h new file mode 100644 index 0000000000..345ff8c595 --- /dev/null +++ b/contrib/clickhouse/src/Functions/UserDefined/IUserDefinedSQLObjectsStorage.h @@ -0,0 +1,74 @@ +#pragma once + +#include <base/types.h> + +#include <Interpreters/Context_fwd.h> + +#include <Parsers/IAST_fwd.h> + + +namespace DB +{ +class IAST; +struct Settings; +enum class UserDefinedSQLObjectType; + +/// Interface for a storage of user-defined SQL objects. +/// Implementations: UserDefinedSQLObjectsDiskStorage, UserDefinedSQLObjectsZooKeeperStorage +class IUserDefinedSQLObjectsStorage +{ +public: + virtual ~IUserDefinedSQLObjectsStorage() = default; + + /// Whether this loader can replicate SQL objects to another node. + virtual bool isReplicated() const { return false; } + virtual String getReplicationID() const { return ""; } + + /// Loads all objects. Can be called once - if objects are already loaded the function does nothing. + virtual void loadObjects() = 0; + + /// Get object by name. If no object stored with object_name throws exception. + virtual ASTPtr get(const String & object_name) const = 0; + + /// Get object by name. If no object stored with object_name return nullptr. + virtual ASTPtr tryGet(const String & object_name) const = 0; + + /// Check if object with object_name is stored. + virtual bool has(const String & object_name) const = 0; + + /// Get all user defined object names. + virtual std::vector<String> getAllObjectNames() const = 0; + + /// Get all user defined objects. + virtual std::vector<std::pair<String, ASTPtr>> getAllObjects() const = 0; + + /// Check whether any UDFs have been stored. + virtual bool empty() const = 0; + + /// Stops watching. + virtual void stopWatching() {} + + /// Immediately reloads all objects, throws an exception if failed. + virtual void reloadObjects() = 0; + + /// Immediately reloads a specified object only. + virtual void reloadObject(UserDefinedSQLObjectType object_type, const String & object_name) = 0; + + /// Stores an object (must be called only by UserDefinedSQLFunctionFactory::registerFunction). + virtual bool storeObject( + const ContextPtr & current_context, + UserDefinedSQLObjectType object_type, + const String & object_name, + ASTPtr create_object_query, + bool throw_if_exists, + bool replace_if_exists, + const Settings & settings) = 0; + + /// Removes an object (must be called only by UserDefinedSQLFunctionFactory::unregisterFunction). + virtual bool removeObject( + const ContextPtr & current_context, + UserDefinedSQLObjectType object_type, + const String & object_name, + bool throw_if_not_exists) = 0; +}; +} diff --git a/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLFunctionFactory.cpp b/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLFunctionFactory.cpp index 622854b350..1cee137ba1 100644 --- a/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLFunctionFactory.cpp +++ b/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLFunctionFactory.cpp @@ -3,7 +3,7 @@ #include <AggregateFunctions/AggregateFunctionFactory.h> #include <Backups/RestorerFromBackup.h> #include <Functions/FunctionFactory.h> -#include <Functions/UserDefined/IUserDefinedSQLObjectsLoader.h> +#include <Functions/UserDefined/IUserDefinedSQLObjectsStorage.h> #include <Functions/UserDefined/UserDefinedExecutableFunctionFactory.h> #include <Functions/UserDefined/UserDefinedSQLObjectType.h> #include <Functions/UserDefined/UserDefinedSQLObjectsBackup.h> @@ -128,20 +128,17 @@ bool UserDefinedSQLFunctionFactory::registerFunction(const ContextMutablePtr & c checkCanBeRegistered(context, function_name, *create_function_query); create_function_query = normalizeCreateFunctionQuery(*create_function_query); - std::lock_guard lock{mutex}; - auto it = function_name_to_create_query_map.find(function_name); - if (it != function_name_to_create_query_map.end()) - { - if (throw_if_exists) - throw Exception(ErrorCodes::FUNCTION_ALREADY_EXISTS, "User-defined function '{}' already exists", function_name); - else if (!replace_if_exists) - return false; - } - try { - auto & loader = context->getUserDefinedSQLObjectsLoader(); - bool stored = loader.storeObject(UserDefinedSQLObjectType::Function, function_name, *create_function_query, throw_if_exists, replace_if_exists, context->getSettingsRef()); + auto & loader = context->getUserDefinedSQLObjectsStorage(); + bool stored = loader.storeObject( + context, + UserDefinedSQLObjectType::Function, + function_name, + create_function_query, + throw_if_exists, + replace_if_exists, + context->getSettingsRef()); if (!stored) return false; } @@ -151,7 +148,6 @@ bool UserDefinedSQLFunctionFactory::registerFunction(const ContextMutablePtr & c throw; } - function_name_to_create_query_map[function_name] = create_function_query; return true; } @@ -159,20 +155,14 @@ bool UserDefinedSQLFunctionFactory::unregisterFunction(const ContextMutablePtr & { checkCanBeUnregistered(context, function_name); - std::lock_guard lock(mutex); - auto it = function_name_to_create_query_map.find(function_name); - if (it == function_name_to_create_query_map.end()) - { - if (throw_if_not_exists) - throw Exception(ErrorCodes::UNKNOWN_FUNCTION, "User-defined function '{}' doesn't exist", function_name); - else - return false; - } - try { - auto & loader = context->getUserDefinedSQLObjectsLoader(); - bool removed = loader.removeObject(UserDefinedSQLObjectType::Function, function_name, throw_if_not_exists); + auto & storage = context->getUserDefinedSQLObjectsStorage(); + bool removed = storage.removeObject( + context, + UserDefinedSQLObjectType::Function, + function_name, + throw_if_not_exists); if (!removed) return false; } @@ -182,61 +172,41 @@ bool UserDefinedSQLFunctionFactory::unregisterFunction(const ContextMutablePtr & throw; } - function_name_to_create_query_map.erase(function_name); return true; } ASTPtr UserDefinedSQLFunctionFactory::get(const String & function_name) const { - std::lock_guard lock(mutex); - - auto it = function_name_to_create_query_map.find(function_name); - if (it == function_name_to_create_query_map.end()) - throw Exception(ErrorCodes::UNKNOWN_FUNCTION, - "The function name '{}' is not registered", - function_name); - - return it->second; + return global_context->getUserDefinedSQLObjectsStorage().get(function_name); } ASTPtr UserDefinedSQLFunctionFactory::tryGet(const std::string & function_name) const { - std::lock_guard lock(mutex); - - auto it = function_name_to_create_query_map.find(function_name); - if (it == function_name_to_create_query_map.end()) - return nullptr; - - return it->second; + return global_context->getUserDefinedSQLObjectsStorage().tryGet(function_name); } bool UserDefinedSQLFunctionFactory::has(const String & function_name) const { - return tryGet(function_name) != nullptr; + return global_context->getUserDefinedSQLObjectsStorage().has(function_name); } std::vector<std::string> UserDefinedSQLFunctionFactory::getAllRegisteredNames() const { - std::vector<std::string> registered_names; - - std::lock_guard lock(mutex); - registered_names.reserve(function_name_to_create_query_map.size()); - - for (const auto & [name, _] : function_name_to_create_query_map) - registered_names.emplace_back(name); - - return registered_names; + return global_context->getUserDefinedSQLObjectsStorage().getAllObjectNames(); } bool UserDefinedSQLFunctionFactory::empty() const { - std::lock_guard lock(mutex); - return function_name_to_create_query_map.empty(); + return global_context->getUserDefinedSQLObjectsStorage().empty(); } void UserDefinedSQLFunctionFactory::backup(BackupEntriesCollector & backup_entries_collector, const String & data_path_in_backup) const { - backupUserDefinedSQLObjects(backup_entries_collector, data_path_in_backup, UserDefinedSQLObjectType::Function, getAllFunctions()); + backupUserDefinedSQLObjects( + backup_entries_collector, + data_path_in_backup, + UserDefinedSQLObjectType::Function, + global_context->getUserDefinedSQLObjectsStorage().getAllObjects()); } void UserDefinedSQLFunctionFactory::restore(RestorerFromBackup & restorer, const String & data_path_in_backup) @@ -250,52 +220,4 @@ void UserDefinedSQLFunctionFactory::restore(RestorerFromBackup & restorer, const registerFunction(context, function_name, create_function_query, throw_if_exists, replace_if_exists); } -void UserDefinedSQLFunctionFactory::setAllFunctions(const std::vector<std::pair<String, ASTPtr>> & new_functions) -{ - std::unordered_map<String, ASTPtr> normalized_functions; - for (const auto & [function_name, create_query] : new_functions) - normalized_functions[function_name] = normalizeCreateFunctionQuery(*create_query); - - std::lock_guard lock(mutex); - function_name_to_create_query_map = std::move(normalized_functions); -} - -std::vector<std::pair<String, ASTPtr>> UserDefinedSQLFunctionFactory::getAllFunctions() const -{ - std::lock_guard lock{mutex}; - std::vector<std::pair<String, ASTPtr>> all_functions; - all_functions.reserve(function_name_to_create_query_map.size()); - std::copy(function_name_to_create_query_map.begin(), function_name_to_create_query_map.end(), std::back_inserter(all_functions)); - return all_functions; -} - -void UserDefinedSQLFunctionFactory::setFunction(const String & function_name, const IAST & create_function_query) -{ - std::lock_guard lock(mutex); - function_name_to_create_query_map[function_name] = normalizeCreateFunctionQuery(create_function_query); -} - -void UserDefinedSQLFunctionFactory::removeFunction(const String & function_name) -{ - std::lock_guard lock(mutex); - function_name_to_create_query_map.erase(function_name); -} - -void UserDefinedSQLFunctionFactory::removeAllFunctionsExcept(const Strings & function_names_to_keep) -{ - boost::container::flat_set<std::string_view> names_set_to_keep{function_names_to_keep.begin(), function_names_to_keep.end()}; - std::lock_guard lock(mutex); - for (auto it = function_name_to_create_query_map.begin(); it != function_name_to_create_query_map.end();) - { - auto current = it++; - if (!names_set_to_keep.contains(current->first)) - function_name_to_create_query_map.erase(current); - } -} - -std::unique_lock<std::recursive_mutex> UserDefinedSQLFunctionFactory::getLock() const -{ - return std::unique_lock{mutex}; -} - } diff --git a/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLFunctionFactory.h b/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLFunctionFactory.h index 45196759d3..489feeae6f 100644 --- a/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLFunctionFactory.h +++ b/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLFunctionFactory.h @@ -6,7 +6,7 @@ #include <Common/NamePrompter.h> #include <Parsers/ASTCreateFunctionQuery.h> -#include <Interpreters/Context_fwd.h> +#include <Interpreters/Context.h> namespace DB @@ -48,23 +48,11 @@ public: void restore(RestorerFromBackup & restorer, const String & data_path_in_backup); private: - friend class UserDefinedSQLObjectsLoaderFromDisk; - friend class UserDefinedSQLObjectsLoaderFromZooKeeper; - /// Checks that a specified function can be registered, throws an exception if not. static void checkCanBeRegistered(const ContextPtr & context, const String & function_name, const IAST & create_function_query); static void checkCanBeUnregistered(const ContextPtr & context, const String & function_name); - /// The following functions must be called only by the loader. - void setAllFunctions(const std::vector<std::pair<String, ASTPtr>> & new_functions); - std::vector<std::pair<String, ASTPtr>> getAllFunctions() const; - void setFunction(const String & function_name, const IAST & create_function_query); - void removeFunction(const String & function_name); - void removeAllFunctionsExcept(const Strings & function_names_to_keep); - std::unique_lock<std::recursive_mutex> getLock() const; - - std::unordered_map<String, ASTPtr> function_name_to_create_query_map; - mutable std::recursive_mutex mutex; + ContextPtr global_context = Context::getGlobalContextInstance(); }; } diff --git a/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsBackup.cpp b/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsBackup.cpp index 6920e8ce2c..3ec5393fa6 100644 --- a/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsBackup.cpp +++ b/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsBackup.cpp @@ -6,7 +6,7 @@ #include <Backups/IBackupCoordination.h> #include <Backups/IRestoreCoordination.h> #include <Backups/RestorerFromBackup.h> -#include <Functions/UserDefined/IUserDefinedSQLObjectsLoader.h> +#include <Functions/UserDefined/IUserDefinedSQLObjectsStorage.h> #include <Functions/UserDefined/UserDefinedSQLObjectType.h> #include <Interpreters/Context.h> #include <Parsers/ParserCreateFunctionQuery.h> @@ -37,9 +37,9 @@ void backupUserDefinedSQLObjects( escapeForFileName(object_name) + ".sql", std::make_shared<BackupEntryFromMemory>(queryToString(create_object_query))); auto context = backup_entries_collector.getContext(); - const auto & loader = context->getUserDefinedSQLObjectsLoader(); + const auto & storage = context->getUserDefinedSQLObjectsStorage(); - if (!loader.isReplicated()) + if (!storage.isReplicated()) { fs::path data_path_in_backup_fs{data_path_in_backup}; for (const auto & [file_name, entry] : backup_entries) @@ -47,7 +47,7 @@ void backupUserDefinedSQLObjects( return; } - String replication_id = loader.getReplicationID(); + String replication_id = storage.getReplicationID(); auto backup_coordination = backup_entries_collector.getBackupCoordination(); backup_coordination->addReplicatedSQLObjectsDir(replication_id, object_type, data_path_in_backup); @@ -80,9 +80,9 @@ std::vector<std::pair<String, ASTPtr>> restoreUserDefinedSQLObjects(RestorerFromBackup & restorer, const String & data_path_in_backup, UserDefinedSQLObjectType object_type) { auto context = restorer.getContext(); - const auto & loader = context->getUserDefinedSQLObjectsLoader(); + const auto & storage = context->getUserDefinedSQLObjectsStorage(); - if (loader.isReplicated() && !restorer.getRestoreCoordination()->acquireReplicatedSQLObjects(loader.getReplicationID(), object_type)) + if (storage.isReplicated() && !restorer.getRestoreCoordination()->acquireReplicatedSQLObjects(storage.getReplicationID(), object_type)) return {}; /// Other replica is already restoring user-defined SQL objects. auto backup = restorer.getBackup(); diff --git a/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsDiskStorage.cpp b/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsDiskStorage.cpp new file mode 100644 index 0000000000..271c464e79 --- /dev/null +++ b/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsDiskStorage.cpp @@ -0,0 +1,268 @@ +#include "Functions/UserDefined/UserDefinedSQLObjectsDiskStorage.h" + +#include "Functions/UserDefined/UserDefinedSQLFunctionFactory.h" +#include "Functions/UserDefined/UserDefinedSQLObjectType.h" + +#include <Common/StringUtils/StringUtils.h> +#include <Common/atomicRename.h> +#include <Common/escapeForFileName.h> +#include <Common/logger_useful.h> +#include <Common/quoteString.h> + +#include <IO/ReadBufferFromFile.h> +#include <IO/ReadHelpers.h> +#include <IO/WriteBufferFromFile.h> +#include <IO/WriteHelpers.h> + +#include <Interpreters/Context.h> + +#include <Parsers/parseQuery.h> +#include <Parsers/formatAST.h> +#include <Parsers/ParserCreateFunctionQuery.h> + +#include <Poco/DirectoryIterator.h> +#include <Poco/Logger.h> + +#include <filesystem> + +namespace fs = std::filesystem; + + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int DIRECTORY_DOESNT_EXIST; + extern const int FUNCTION_ALREADY_EXISTS; + extern const int UNKNOWN_FUNCTION; +} + + +namespace +{ + /// Converts a path to an absolute path and append it with a separator. + String makeDirectoryPathCanonical(const String & directory_path) + { + auto canonical_directory_path = std::filesystem::weakly_canonical(directory_path); + if (canonical_directory_path.has_filename()) + canonical_directory_path += std::filesystem::path::preferred_separator; + return canonical_directory_path; + } +} + +UserDefinedSQLObjectsDiskStorage::UserDefinedSQLObjectsDiskStorage(const ContextPtr & global_context_, const String & dir_path_) + : global_context(global_context_) + , dir_path{makeDirectoryPathCanonical(dir_path_)} + , log{&Poco::Logger::get("UserDefinedSQLObjectsLoaderFromDisk")} +{ + createDirectory(); +} + + +ASTPtr UserDefinedSQLObjectsDiskStorage::tryLoadObject(UserDefinedSQLObjectType object_type, const String & object_name) +{ + return tryLoadObject(object_type, object_name, getFilePath(object_type, object_name), /* check_file_exists= */ true); +} + + +ASTPtr UserDefinedSQLObjectsDiskStorage::tryLoadObject(UserDefinedSQLObjectType object_type, const String & object_name, const String & path, bool check_file_exists) +{ + LOG_DEBUG(log, "Loading user defined object {} from file {}", backQuote(object_name), path); + + try + { + if (check_file_exists && !fs::exists(path)) + return nullptr; + + /// There is .sql file with user defined object creation statement. + ReadBufferFromFile in(path); + + String object_create_query; + readStringUntilEOF(object_create_query, in); + + switch (object_type) + { + case UserDefinedSQLObjectType::Function: + { + ParserCreateFunctionQuery parser; + ASTPtr ast = parseQuery( + parser, + object_create_query.data(), + object_create_query.data() + object_create_query.size(), + "", + 0, + global_context->getSettingsRef().max_parser_depth); + return ast; + } + } + } + catch (...) + { + tryLogCurrentException(log, fmt::format("while loading user defined SQL object {} from path {}", backQuote(object_name), path)); + return nullptr; /// Failed to load this sql object, will ignore it + } +} + + +void UserDefinedSQLObjectsDiskStorage::loadObjects() +{ + if (!objects_loaded) + loadObjectsImpl(); +} + + +void UserDefinedSQLObjectsDiskStorage::reloadObjects() +{ + loadObjectsImpl(); +} + + +void UserDefinedSQLObjectsDiskStorage::loadObjectsImpl() +{ + LOG_INFO(log, "Loading user defined objects from {}", dir_path); + createDirectory(); + + std::vector<std::pair<String, ASTPtr>> function_names_and_queries; + + Poco::DirectoryIterator dir_end; + for (Poco::DirectoryIterator it(dir_path); it != dir_end; ++it) + { + if (it->isDirectory()) + continue; + + const String & file_name = it.name(); + if (!startsWith(file_name, "function_") || !endsWith(file_name, ".sql")) + continue; + + size_t prefix_length = strlen("function_"); + size_t suffix_length = strlen(".sql"); + String function_name = unescapeForFileName(file_name.substr(prefix_length, file_name.length() - prefix_length - suffix_length)); + + if (function_name.empty()) + continue; + + ASTPtr ast = tryLoadObject(UserDefinedSQLObjectType::Function, function_name, dir_path + it.name(), /* check_file_exists= */ false); + if (ast) + function_names_and_queries.emplace_back(function_name, ast); + } + + setAllObjects(function_names_and_queries); + objects_loaded = true; + + LOG_DEBUG(log, "User defined objects loaded"); +} + + +void UserDefinedSQLObjectsDiskStorage::reloadObject(UserDefinedSQLObjectType object_type, const String & object_name) +{ + createDirectory(); + auto ast = tryLoadObject(object_type, object_name); + if (ast) + setObject(object_name, *ast); + else + removeObject(object_name); +} + + +void UserDefinedSQLObjectsDiskStorage::createDirectory() +{ + std::error_code create_dir_error_code; + fs::create_directories(dir_path, create_dir_error_code); + if (!fs::exists(dir_path) || !fs::is_directory(dir_path) || create_dir_error_code) + throw Exception(ErrorCodes::DIRECTORY_DOESNT_EXIST, "Couldn't create directory {} reason: '{}'", + dir_path, create_dir_error_code.message()); +} + + +bool UserDefinedSQLObjectsDiskStorage::storeObjectImpl( + const ContextPtr & /*current_context*/, + UserDefinedSQLObjectType object_type, + const String & object_name, + ASTPtr create_object_query, + bool throw_if_exists, + bool replace_if_exists, + const Settings & settings) +{ + String file_path = getFilePath(object_type, object_name); + LOG_DEBUG(log, "Storing user-defined object {} to file {}", backQuote(object_name), file_path); + + if (fs::exists(file_path)) + { + if (throw_if_exists) + throw Exception(ErrorCodes::FUNCTION_ALREADY_EXISTS, "User-defined function '{}' already exists", object_name); + else if (!replace_if_exists) + return false; + } + + WriteBufferFromOwnString create_statement_buf; + formatAST(*create_object_query, create_statement_buf, false); + writeChar('\n', create_statement_buf); + String create_statement = create_statement_buf.str(); + + String temp_file_path = file_path + ".tmp"; + + try + { + WriteBufferFromFile out(temp_file_path, create_statement.size()); + writeString(create_statement, out); + out.next(); + if (settings.fsync_metadata) + out.sync(); + out.close(); + + if (replace_if_exists) + fs::rename(temp_file_path, file_path); + else + renameNoReplace(temp_file_path, file_path); + } + catch (...) + { + fs::remove(temp_file_path); + throw; + } + + LOG_TRACE(log, "Object {} stored", backQuote(object_name)); + return true; +} + + +bool UserDefinedSQLObjectsDiskStorage::removeObjectImpl( + const ContextPtr & /*current_context*/, + UserDefinedSQLObjectType object_type, + const String & object_name, + bool throw_if_not_exists) +{ + String file_path = getFilePath(object_type, object_name); + LOG_DEBUG(log, "Removing user defined object {} stored in file {}", backQuote(object_name), file_path); + + bool existed = fs::remove(file_path); + + if (!existed) + { + if (throw_if_not_exists) + throw Exception(ErrorCodes::UNKNOWN_FUNCTION, "User-defined function '{}' doesn't exist", object_name); + else + return false; + } + + LOG_TRACE(log, "Object {} removed", backQuote(object_name)); + return true; +} + + +String UserDefinedSQLObjectsDiskStorage::getFilePath(UserDefinedSQLObjectType object_type, const String & object_name) const +{ + String file_path; + switch (object_type) + { + case UserDefinedSQLObjectType::Function: + { + file_path = dir_path + "function_" + escapeForFileName(object_name) + ".sql"; + break; + } + } + return file_path; +} + +} diff --git a/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsLoaderFromDisk.h b/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsDiskStorage.h index 7b0bb291f4..f0986dbda7 100644 --- a/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsLoaderFromDisk.h +++ b/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsDiskStorage.h @@ -1,6 +1,6 @@ #pragma once -#include <Functions/UserDefined/IUserDefinedSQLObjectsLoader.h> +#include <Functions/UserDefined/UserDefinedSQLObjectsStorageBase.h> #include <Interpreters/Context_fwd.h> #include <Parsers/IAST_fwd.h> @@ -9,10 +9,10 @@ namespace DB { /// Loads user-defined sql objects from a specified folder. -class UserDefinedSQLObjectsLoaderFromDisk : public IUserDefinedSQLObjectsLoader +class UserDefinedSQLObjectsDiskStorage : public UserDefinedSQLObjectsStorageBase { public: - UserDefinedSQLObjectsLoaderFromDisk(const ContextPtr & global_context_, const String & dir_path_); + UserDefinedSQLObjectsDiskStorage(const ContextPtr & global_context_, const String & dir_path_); void loadObjects() override; @@ -20,17 +20,22 @@ public: void reloadObject(UserDefinedSQLObjectType object_type, const String & object_name) override; - bool storeObject( +private: + bool storeObjectImpl( + const ContextPtr & current_context, UserDefinedSQLObjectType object_type, const String & object_name, - const IAST & create_object_query, + ASTPtr create_object_query, bool throw_if_exists, bool replace_if_exists, const Settings & settings) override; - bool removeObject(UserDefinedSQLObjectType object_type, const String & object_name, bool throw_if_not_exists) override; + bool removeObjectImpl( + const ContextPtr & current_context, + UserDefinedSQLObjectType object_type, + const String & object_name, + bool throw_if_not_exists) override; -private: void createDirectory(); void loadObjectsImpl(); ASTPtr tryLoadObject(UserDefinedSQLObjectType object_type, const String & object_name); diff --git a/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsLoaderFromDisk.cpp b/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsLoaderFromDisk.cpp index d67c48f166..e69de29bb2 100644 --- a/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsLoaderFromDisk.cpp +++ b/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsLoaderFromDisk.cpp @@ -1,266 +0,0 @@ -#include "Functions/UserDefined/UserDefinedSQLObjectsLoaderFromDisk.h" - -#include "Functions/UserDefined/UserDefinedSQLFunctionFactory.h" -#include "Functions/UserDefined/UserDefinedSQLObjectType.h" - -#include <Common/StringUtils/StringUtils.h> -#include <Common/atomicRename.h> -#include <Common/escapeForFileName.h> -#include <Common/logger_useful.h> -#include <Common/quoteString.h> - -#include <IO/ReadBufferFromFile.h> -#include <IO/ReadHelpers.h> -#include <IO/WriteBufferFromFile.h> -#include <IO/WriteHelpers.h> - -#include <Interpreters/Context.h> - -#include <Parsers/parseQuery.h> -#include <Parsers/formatAST.h> -#include <Parsers/ParserCreateFunctionQuery.h> - -#include <Poco/DirectoryIterator.h> -#include <Poco/Logger.h> - -#include <filesystem> - -namespace fs = std::filesystem; - - -namespace DB -{ - -namespace ErrorCodes -{ - extern const int DIRECTORY_DOESNT_EXIST; - extern const int FUNCTION_ALREADY_EXISTS; - extern const int UNKNOWN_FUNCTION; -} - - -namespace -{ - /// Converts a path to an absolute path and append it with a separator. - String makeDirectoryPathCanonical(const String & directory_path) - { - auto canonical_directory_path = std::filesystem::weakly_canonical(directory_path); - if (canonical_directory_path.has_filename()) - canonical_directory_path += std::filesystem::path::preferred_separator; - return canonical_directory_path; - } -} - -UserDefinedSQLObjectsLoaderFromDisk::UserDefinedSQLObjectsLoaderFromDisk(const ContextPtr & global_context_, const String & dir_path_) - : global_context(global_context_) - , dir_path{makeDirectoryPathCanonical(dir_path_)} - , log{&Poco::Logger::get("UserDefinedSQLObjectsLoaderFromDisk")} -{ - createDirectory(); -} - - -ASTPtr UserDefinedSQLObjectsLoaderFromDisk::tryLoadObject(UserDefinedSQLObjectType object_type, const String & object_name) -{ - return tryLoadObject(object_type, object_name, getFilePath(object_type, object_name), /* check_file_exists= */ true); -} - - -ASTPtr UserDefinedSQLObjectsLoaderFromDisk::tryLoadObject(UserDefinedSQLObjectType object_type, const String & object_name, const String & path, bool check_file_exists) -{ - LOG_DEBUG(log, "Loading user defined object {} from file {}", backQuote(object_name), path); - - try - { - if (check_file_exists && !fs::exists(path)) - return nullptr; - - /// There is .sql file with user defined object creation statement. - ReadBufferFromFile in(path); - - String object_create_query; - readStringUntilEOF(object_create_query, in); - - switch (object_type) - { - case UserDefinedSQLObjectType::Function: - { - ParserCreateFunctionQuery parser; - ASTPtr ast = parseQuery( - parser, - object_create_query.data(), - object_create_query.data() + object_create_query.size(), - "", - 0, - global_context->getSettingsRef().max_parser_depth); - UserDefinedSQLFunctionFactory::checkCanBeRegistered(global_context, object_name, *ast); - return ast; - } - } - } - catch (...) - { - tryLogCurrentException(log, fmt::format("while loading user defined SQL object {} from path {}", backQuote(object_name), path)); - return nullptr; /// Failed to load this sql object, will ignore it - } -} - - -void UserDefinedSQLObjectsLoaderFromDisk::loadObjects() -{ - if (!objects_loaded) - loadObjectsImpl(); -} - - -void UserDefinedSQLObjectsLoaderFromDisk::reloadObjects() -{ - loadObjectsImpl(); -} - - -void UserDefinedSQLObjectsLoaderFromDisk::loadObjectsImpl() -{ - LOG_INFO(log, "Loading user defined objects from {}", dir_path); - createDirectory(); - - std::vector<std::pair<String, ASTPtr>> function_names_and_queries; - - Poco::DirectoryIterator dir_end; - for (Poco::DirectoryIterator it(dir_path); it != dir_end; ++it) - { - if (it->isDirectory()) - continue; - - const String & file_name = it.name(); - if (!startsWith(file_name, "function_") || !endsWith(file_name, ".sql")) - continue; - - size_t prefix_length = strlen("function_"); - size_t suffix_length = strlen(".sql"); - String function_name = unescapeForFileName(file_name.substr(prefix_length, file_name.length() - prefix_length - suffix_length)); - - if (function_name.empty()) - continue; - - ASTPtr ast = tryLoadObject(UserDefinedSQLObjectType::Function, function_name, dir_path + it.name(), /* check_file_exists= */ false); - if (ast) - function_names_and_queries.emplace_back(function_name, ast); - } - - UserDefinedSQLFunctionFactory::instance().setAllFunctions(function_names_and_queries); - objects_loaded = true; - - LOG_DEBUG(log, "User defined objects loaded"); -} - - -void UserDefinedSQLObjectsLoaderFromDisk::reloadObject(UserDefinedSQLObjectType object_type, const String & object_name) -{ - createDirectory(); - auto ast = tryLoadObject(object_type, object_name); - auto & factory = UserDefinedSQLFunctionFactory::instance(); - if (ast) - factory.setFunction(object_name, *ast); - else - factory.removeFunction(object_name); -} - - -void UserDefinedSQLObjectsLoaderFromDisk::createDirectory() -{ - std::error_code create_dir_error_code; - fs::create_directories(dir_path, create_dir_error_code); - if (!fs::exists(dir_path) || !fs::is_directory(dir_path) || create_dir_error_code) - throw Exception(ErrorCodes::DIRECTORY_DOESNT_EXIST, "Couldn't create directory {} reason: '{}'", - dir_path, create_dir_error_code.message()); -} - - -bool UserDefinedSQLObjectsLoaderFromDisk::storeObject( - UserDefinedSQLObjectType object_type, - const String & object_name, - const IAST & create_object_query, - bool throw_if_exists, - bool replace_if_exists, - const Settings & settings) -{ - String file_path = getFilePath(object_type, object_name); - LOG_DEBUG(log, "Storing user-defined object {} to file {}", backQuote(object_name), file_path); - - if (fs::exists(file_path)) - { - if (throw_if_exists) - throw Exception(ErrorCodes::FUNCTION_ALREADY_EXISTS, "User-defined function '{}' already exists", object_name); - else if (!replace_if_exists) - return false; - } - - WriteBufferFromOwnString create_statement_buf; - formatAST(create_object_query, create_statement_buf, false); - writeChar('\n', create_statement_buf); - String create_statement = create_statement_buf.str(); - - String temp_file_path = file_path + ".tmp"; - - try - { - WriteBufferFromFile out(temp_file_path, create_statement.size()); - writeString(create_statement, out); - out.next(); - if (settings.fsync_metadata) - out.sync(); - out.close(); - - if (replace_if_exists) - fs::rename(temp_file_path, file_path); - else - renameNoReplace(temp_file_path, file_path); - } - catch (...) - { - fs::remove(temp_file_path); - throw; - } - - LOG_TRACE(log, "Object {} stored", backQuote(object_name)); - return true; -} - - -bool UserDefinedSQLObjectsLoaderFromDisk::removeObject( - UserDefinedSQLObjectType object_type, const String & object_name, bool throw_if_not_exists) -{ - String file_path = getFilePath(object_type, object_name); - LOG_DEBUG(log, "Removing user defined object {} stored in file {}", backQuote(object_name), file_path); - - bool existed = fs::remove(file_path); - - if (!existed) - { - if (throw_if_not_exists) - throw Exception(ErrorCodes::UNKNOWN_FUNCTION, "User-defined function '{}' doesn't exist", object_name); - else - return false; - } - - LOG_TRACE(log, "Object {} removed", backQuote(object_name)); - return true; -} - - -String UserDefinedSQLObjectsLoaderFromDisk::getFilePath(UserDefinedSQLObjectType object_type, const String & object_name) const -{ - String file_path; - switch (object_type) - { - case UserDefinedSQLObjectType::Function: - { - file_path = dir_path + "function_" + escapeForFileName(object_name) + ".sql"; - break; - } - } - return file_path; -} - -} diff --git a/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsLoaderFromZooKeeper.cpp b/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsLoaderFromZooKeeper.cpp index 29aff666da..e69de29bb2 100644 --- a/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsLoaderFromZooKeeper.cpp +++ b/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsLoaderFromZooKeeper.cpp @@ -1,433 +0,0 @@ -#include <Functions/UserDefined/UserDefinedSQLObjectsLoaderFromZooKeeper.h> - -#include <Functions/UserDefined/UserDefinedSQLFunctionFactory.h> -#include <Functions/UserDefined/UserDefinedSQLObjectType.h> -#include <Interpreters/Context.h> -#include <Parsers/ParserCreateFunctionQuery.h> -#include <Parsers/formatAST.h> -#include <Parsers/parseQuery.h> -#include <base/sleep.h> -#include <Common/Exception.h> -#include <Common/ZooKeeper/KeeperException.h> -#include <Common/escapeForFileName.h> -#include <Common/logger_useful.h> -#include <Common/quoteString.h> -#include <Common/scope_guard_safe.h> -#include <Common/setThreadName.h> - - -namespace DB -{ - -namespace ErrorCodes -{ - extern const int FUNCTION_ALREADY_EXISTS; - extern const int UNKNOWN_FUNCTION; - extern const int BAD_ARGUMENTS; -} - -namespace -{ - std::string_view getNodePrefix(UserDefinedSQLObjectType object_type) - { - switch (object_type) - { - case UserDefinedSQLObjectType::Function: - return "function_"; - } - UNREACHABLE(); - } - - constexpr std::string_view sql_extension = ".sql"; - - String getNodePath(const String & root_path, UserDefinedSQLObjectType object_type, const String & object_name) - { - return root_path + "/" + String{getNodePrefix(object_type)} + escapeForFileName(object_name) + String{sql_extension}; - } -} - - -UserDefinedSQLObjectsLoaderFromZooKeeper::UserDefinedSQLObjectsLoaderFromZooKeeper( - const ContextPtr & global_context_, const String & zookeeper_path_) - : global_context{global_context_} - , zookeeper_getter{[global_context_]() { return global_context_->getZooKeeper(); }} - , zookeeper_path{zookeeper_path_} - , watch_queue{std::make_shared<ConcurrentBoundedQueue<std::pair<UserDefinedSQLObjectType, String>>>(std::numeric_limits<size_t>::max())} - , log{&Poco::Logger::get("UserDefinedSQLObjectsLoaderFromZooKeeper")} -{ - if (zookeeper_path.empty()) - throw Exception(ErrorCodes::BAD_ARGUMENTS, "ZooKeeper path must be non-empty"); - - if (zookeeper_path.back() == '/') - zookeeper_path.resize(zookeeper_path.size() - 1); - - /// If zookeeper chroot prefix is used, path should start with '/', because chroot concatenates without it. - if (zookeeper_path.front() != '/') - zookeeper_path = "/" + zookeeper_path; -} - -UserDefinedSQLObjectsLoaderFromZooKeeper::~UserDefinedSQLObjectsLoaderFromZooKeeper() -{ - SCOPE_EXIT_SAFE(stopWatchingThread()); -} - -void UserDefinedSQLObjectsLoaderFromZooKeeper::startWatchingThread() -{ - if (!watching_flag.exchange(true)) - { - watching_thread = ThreadFromGlobalPool(&UserDefinedSQLObjectsLoaderFromZooKeeper::processWatchQueue, this); - } -} - -void UserDefinedSQLObjectsLoaderFromZooKeeper::stopWatchingThread() -{ - if (watching_flag.exchange(false)) - { - watch_queue->finish(); - if (watching_thread.joinable()) - watching_thread.join(); - } -} - -zkutil::ZooKeeperPtr UserDefinedSQLObjectsLoaderFromZooKeeper::getZooKeeper() -{ - auto [zookeeper, session_status] = zookeeper_getter.getZooKeeper(); - - if (session_status == zkutil::ZooKeeperCachingGetter::SessionStatus::New) - { - /// It's possible that we connected to different [Zoo]Keeper instance - /// so we may read a bit stale state. - zookeeper->sync(zookeeper_path); - - createRootNodes(zookeeper); - refreshAllObjects(zookeeper); - } - - return zookeeper; -} - -void UserDefinedSQLObjectsLoaderFromZooKeeper::initZooKeeperIfNeeded() -{ - getZooKeeper(); -} - -void UserDefinedSQLObjectsLoaderFromZooKeeper::resetAfterError() -{ - zookeeper_getter.resetCache(); -} - - -void UserDefinedSQLObjectsLoaderFromZooKeeper::loadObjects() -{ - /// loadObjects() is called at start from Server::main(), so it's better not to stop here on no connection to ZooKeeper or any other error. - /// However the watching thread must be started anyway in case the connection will be established later. - if (!objects_loaded) - { - try - { - reloadObjects(); - } - catch (...) - { - tryLogCurrentException(log, "Failed to load user-defined objects"); - } - } - startWatchingThread(); -} - - -void UserDefinedSQLObjectsLoaderFromZooKeeper::processWatchQueue() -{ - LOG_DEBUG(log, "Started watching thread"); - setThreadName("UserDefObjWatch"); - - while (watching_flag) - { - try - { - UserDefinedSQLObjectTypeAndName watched_object; - - /// Re-initialize ZooKeeper session if expired and refresh objects - initZooKeeperIfNeeded(); - - if (!watch_queue->tryPop(watched_object, /* timeout_ms: */ 10000)) - continue; - - auto zookeeper = getZooKeeper(); - const auto & [object_type, object_name] = watched_object; - - if (object_name.empty()) - syncObjects(zookeeper, object_type); - else - refreshObject(zookeeper, object_type, object_name); - } - catch (...) - { - tryLogCurrentException(log, "Will try to restart watching thread after error"); - resetAfterError(); - sleepForSeconds(5); - } - } - - LOG_DEBUG(log, "Stopped watching thread"); -} - - -void UserDefinedSQLObjectsLoaderFromZooKeeper::stopWatching() -{ - stopWatchingThread(); -} - - -void UserDefinedSQLObjectsLoaderFromZooKeeper::reloadObjects() -{ - auto zookeeper = getZooKeeper(); - refreshAllObjects(zookeeper); - startWatchingThread(); -} - - -void UserDefinedSQLObjectsLoaderFromZooKeeper::reloadObject(UserDefinedSQLObjectType object_type, const String & object_name) -{ - auto zookeeper = getZooKeeper(); - refreshObject(zookeeper, object_type, object_name); -} - - -void UserDefinedSQLObjectsLoaderFromZooKeeper::createRootNodes(const zkutil::ZooKeeperPtr & zookeeper) -{ - zookeeper->createAncestors(zookeeper_path); - zookeeper->createIfNotExists(zookeeper_path, ""); -} - -bool UserDefinedSQLObjectsLoaderFromZooKeeper::storeObject( - UserDefinedSQLObjectType object_type, - const String & object_name, - const IAST & create_object_query, - bool throw_if_exists, - bool replace_if_exists, - const Settings &) -{ - String path = getNodePath(zookeeper_path, object_type, object_name); - LOG_DEBUG(log, "Storing user-defined object {} at zk path {}", backQuote(object_name), path); - - WriteBufferFromOwnString create_statement_buf; - formatAST(create_object_query, create_statement_buf, false); - writeChar('\n', create_statement_buf); - String create_statement = create_statement_buf.str(); - - auto zookeeper = getZooKeeper(); - - size_t num_attempts = 10; - while (true) - { - auto code = zookeeper->tryCreate(path, create_statement, zkutil::CreateMode::Persistent); - if ((code != Coordination::Error::ZOK) && (code != Coordination::Error::ZNODEEXISTS)) - throw zkutil::KeeperException::fromPath(code, path); - - if (code == Coordination::Error::ZNODEEXISTS) - { - if (throw_if_exists) - throw Exception(ErrorCodes::FUNCTION_ALREADY_EXISTS, "User-defined function '{}' already exists", object_name); - else if (!replace_if_exists) - return false; - - code = zookeeper->trySet(path, create_statement); - if ((code != Coordination::Error::ZOK) && (code != Coordination::Error::ZNONODE)) - throw zkutil::KeeperException::fromPath(code, path); - } - - if (code == Coordination::Error::ZOK) - break; - - if (!--num_attempts) - throw zkutil::KeeperException::fromPath(code, path); - } - LOG_DEBUG(log, "Object {} stored", backQuote(object_name)); - - /// Refresh object and set watch for it. Because it can be replaced by another node after creation. - refreshObject(zookeeper, object_type, object_name); - - return true; -} - - -bool UserDefinedSQLObjectsLoaderFromZooKeeper::removeObject( - UserDefinedSQLObjectType object_type, const String & object_name, bool throw_if_not_exists) -{ - String path = getNodePath(zookeeper_path, object_type, object_name); - LOG_DEBUG(log, "Removing user-defined object {} at zk path {}", backQuote(object_name), path); - - auto zookeeper = getZooKeeper(); - - auto code = zookeeper->tryRemove(path); - if ((code != Coordination::Error::ZOK) && (code != Coordination::Error::ZNONODE)) - throw zkutil::KeeperException::fromPath(code, path); - - if (code == Coordination::Error::ZNONODE) - { - if (throw_if_not_exists) - throw Exception(ErrorCodes::UNKNOWN_FUNCTION, "User-defined object '{}' doesn't exist", object_name); - else - return false; - } - - LOG_DEBUG(log, "Object {} removed", backQuote(object_name)); - return true; -} - -bool UserDefinedSQLObjectsLoaderFromZooKeeper::getObjectDataAndSetWatch( - const zkutil::ZooKeeperPtr & zookeeper, - String & data, - const String & path, - UserDefinedSQLObjectType object_type, - const String & object_name) -{ - const auto object_watcher = [my_watch_queue = watch_queue, object_type, object_name](const Coordination::WatchResponse & response) - { - if (response.type == Coordination::Event::CHANGED) - { - [[maybe_unused]] bool inserted = my_watch_queue->emplace(object_type, object_name); - /// `inserted` can be false if `watch_queue` was already finalized (which happens when stopWatching() is called). - } - /// Event::DELETED is processed as child event by getChildren watch - }; - - Coordination::Stat entity_stat; - String object_create_query; - return zookeeper->tryGetWatch(path, data, &entity_stat, object_watcher); -} - -ASTPtr UserDefinedSQLObjectsLoaderFromZooKeeper::parseObjectData(const String & object_data, UserDefinedSQLObjectType object_type) -{ - switch (object_type) - { - case UserDefinedSQLObjectType::Function: { - ParserCreateFunctionQuery parser; - ASTPtr ast = parseQuery( - parser, - object_data.data(), - object_data.data() + object_data.size(), - "", - 0, - global_context->getSettingsRef().max_parser_depth); - return ast; - } - } - UNREACHABLE(); -} - -ASTPtr UserDefinedSQLObjectsLoaderFromZooKeeper::tryLoadObject( - const zkutil::ZooKeeperPtr & zookeeper, UserDefinedSQLObjectType object_type, const String & object_name) -{ - String path = getNodePath(zookeeper_path, object_type, object_name); - LOG_DEBUG(log, "Loading user defined object {} from zk path {}", backQuote(object_name), path); - - try - { - String object_data; - bool exists = getObjectDataAndSetWatch(zookeeper, object_data, path, object_type, object_name); - - if (!exists) - { - LOG_INFO(log, "User-defined object '{}' can't be loaded from path {}, because it doesn't exist", backQuote(object_name), path); - return nullptr; - } - - return parseObjectData(object_data, object_type); - } - catch (...) - { - tryLogCurrentException(log, fmt::format("while loading user defined SQL object {}", backQuote(object_name))); - return nullptr; /// Failed to load this sql object, will ignore it - } -} - -Strings UserDefinedSQLObjectsLoaderFromZooKeeper::getObjectNamesAndSetWatch( - const zkutil::ZooKeeperPtr & zookeeper, UserDefinedSQLObjectType object_type) -{ - auto object_list_watcher = [my_watch_queue = watch_queue, object_type](const Coordination::WatchResponse &) - { - [[maybe_unused]] bool inserted = my_watch_queue->emplace(object_type, ""); - /// `inserted` can be false if `watch_queue` was already finalized (which happens when stopWatching() is called). - }; - - Coordination::Stat stat; - const auto node_names = zookeeper->getChildrenWatch(zookeeper_path, &stat, object_list_watcher); - const auto prefix = getNodePrefix(object_type); - - Strings object_names; - object_names.reserve(node_names.size()); - for (const auto & node_name : node_names) - { - if (node_name.starts_with(prefix) && node_name.ends_with(sql_extension)) - { - String object_name = unescapeForFileName(node_name.substr(prefix.length(), node_name.length() - prefix.length() - sql_extension.length())); - if (!object_name.empty()) - object_names.push_back(std::move(object_name)); - } - } - - return object_names; -} - -void UserDefinedSQLObjectsLoaderFromZooKeeper::refreshAllObjects(const zkutil::ZooKeeperPtr & zookeeper) -{ - /// It doesn't make sense to keep the old watch events because we will reread everything in this function. - watch_queue->clear(); - - refreshObjects(zookeeper, UserDefinedSQLObjectType::Function); - objects_loaded = true; -} - -void UserDefinedSQLObjectsLoaderFromZooKeeper::refreshObjects(const zkutil::ZooKeeperPtr & zookeeper, UserDefinedSQLObjectType object_type) -{ - LOG_DEBUG(log, "Refreshing all user-defined {} objects", object_type); - Strings object_names = getObjectNamesAndSetWatch(zookeeper, object_type); - - /// Read & parse all SQL objects from ZooKeeper - std::vector<std::pair<String, ASTPtr>> function_names_and_asts; - for (const auto & function_name : object_names) - { - if (auto ast = tryLoadObject(zookeeper, UserDefinedSQLObjectType::Function, function_name)) - function_names_and_asts.emplace_back(function_name, ast); - } - - UserDefinedSQLFunctionFactory::instance().setAllFunctions(function_names_and_asts); - - LOG_DEBUG(log, "All user-defined {} objects refreshed", object_type); -} - -void UserDefinedSQLObjectsLoaderFromZooKeeper::syncObjects(const zkutil::ZooKeeperPtr & zookeeper, UserDefinedSQLObjectType object_type) -{ - LOG_DEBUG(log, "Syncing user-defined {} objects", object_type); - Strings object_names = getObjectNamesAndSetWatch(zookeeper, object_type); - - auto & factory = UserDefinedSQLFunctionFactory::instance(); - auto lock = factory.getLock(); - - /// Remove stale objects - factory.removeAllFunctionsExcept(object_names); - /// Read & parse only new SQL objects from ZooKeeper - for (const auto & function_name : object_names) - { - if (!UserDefinedSQLFunctionFactory::instance().has(function_name)) - refreshObject(zookeeper, UserDefinedSQLObjectType::Function, function_name); - } - - LOG_DEBUG(log, "User-defined {} objects synced", object_type); -} - -void UserDefinedSQLObjectsLoaderFromZooKeeper::refreshObject( - const zkutil::ZooKeeperPtr & zookeeper, UserDefinedSQLObjectType object_type, const String & object_name) -{ - auto ast = tryLoadObject(zookeeper, object_type, object_name); - auto & factory = UserDefinedSQLFunctionFactory::instance(); - - if (ast) - factory.setFunction(object_name, *ast); - else - factory.removeFunction(object_name); -} - -} diff --git a/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsStorageBase.cpp b/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsStorageBase.cpp new file mode 100644 index 0000000000..8d7a18d93b --- /dev/null +++ b/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsStorageBase.cpp @@ -0,0 +1,190 @@ +#include "Functions/UserDefined/UserDefinedSQLObjectsStorageBase.h" + +#include <boost/container/flat_set.hpp> + +#include <Interpreters/FunctionNameNormalizer.h> +#include <Parsers/ASTCreateFunctionQuery.h> + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int FUNCTION_ALREADY_EXISTS; + extern const int UNKNOWN_FUNCTION; +} + +namespace +{ + +ASTPtr normalizeCreateFunctionQuery(const IAST & create_function_query) +{ + auto ptr = create_function_query.clone(); + auto & res = typeid_cast<ASTCreateFunctionQuery &>(*ptr); + res.if_not_exists = false; + res.or_replace = false; + FunctionNameNormalizer().visit(res.function_core.get()); + return ptr; +} + +} + +ASTPtr UserDefinedSQLObjectsStorageBase::get(const String & object_name) const +{ + std::lock_guard lock(mutex); + + auto it = object_name_to_create_object_map.find(object_name); + if (it == object_name_to_create_object_map.end()) + throw Exception(ErrorCodes::UNKNOWN_FUNCTION, + "The object name '{}' is not saved", + object_name); + + return it->second; +} + +ASTPtr UserDefinedSQLObjectsStorageBase::tryGet(const std::string & object_name) const +{ + std::lock_guard lock(mutex); + + auto it = object_name_to_create_object_map.find(object_name); + if (it == object_name_to_create_object_map.end()) + return nullptr; + + return it->second; +} + +bool UserDefinedSQLObjectsStorageBase::has(const String & object_name) const +{ + return tryGet(object_name) != nullptr; +} + +std::vector<std::string> UserDefinedSQLObjectsStorageBase::getAllObjectNames() const +{ + std::vector<std::string> object_names; + + std::lock_guard lock(mutex); + object_names.reserve(object_name_to_create_object_map.size()); + + for (const auto & [name, _] : object_name_to_create_object_map) + object_names.emplace_back(name); + + return object_names; +} + +bool UserDefinedSQLObjectsStorageBase::empty() const +{ + std::lock_guard lock(mutex); + return object_name_to_create_object_map.empty(); +} + +bool UserDefinedSQLObjectsStorageBase::storeObject( + const ContextPtr & current_context, + UserDefinedSQLObjectType object_type, + const String & object_name, + ASTPtr create_object_query, + bool throw_if_exists, + bool replace_if_exists, + const Settings & settings) +{ + std::lock_guard lock{mutex}; + auto it = object_name_to_create_object_map.find(object_name); + if (it != object_name_to_create_object_map.end()) + { + if (throw_if_exists) + throw Exception(ErrorCodes::FUNCTION_ALREADY_EXISTS, "User-defined object '{}' already exists", object_name); + else if (!replace_if_exists) + return false; + } + + bool stored = storeObjectImpl( + current_context, + object_type, + object_name, + create_object_query, + throw_if_exists, + replace_if_exists, + settings); + + if (stored) + object_name_to_create_object_map[object_name] = create_object_query; + + return stored; +} + +bool UserDefinedSQLObjectsStorageBase::removeObject( + const ContextPtr & current_context, + UserDefinedSQLObjectType object_type, + const String & object_name, + bool throw_if_not_exists) +{ + std::lock_guard lock(mutex); + auto it = object_name_to_create_object_map.find(object_name); + if (it == object_name_to_create_object_map.end()) + { + if (throw_if_not_exists) + throw Exception(ErrorCodes::UNKNOWN_FUNCTION, "User-defined object '{}' doesn't exist", object_name); + else + return false; + } + + bool removed = removeObjectImpl( + current_context, + object_type, + object_name, + throw_if_not_exists); + + if (removed) + object_name_to_create_object_map.erase(object_name); + + return removed; +} + +std::unique_lock<std::recursive_mutex> UserDefinedSQLObjectsStorageBase::getLock() const +{ + return std::unique_lock{mutex}; +} + +void UserDefinedSQLObjectsStorageBase::setAllObjects(const std::vector<std::pair<String, ASTPtr>> & new_objects) +{ + std::unordered_map<String, ASTPtr> normalized_functions; + for (const auto & [function_name, create_query] : new_objects) + normalized_functions[function_name] = normalizeCreateFunctionQuery(*create_query); + + std::lock_guard lock(mutex); + object_name_to_create_object_map = std::move(normalized_functions); +} + +std::vector<std::pair<String, ASTPtr>> UserDefinedSQLObjectsStorageBase::getAllObjects() const +{ + std::lock_guard lock{mutex}; + std::vector<std::pair<String, ASTPtr>> all_objects; + all_objects.reserve(object_name_to_create_object_map.size()); + std::copy(object_name_to_create_object_map.begin(), object_name_to_create_object_map.end(), std::back_inserter(all_objects)); + return all_objects; +} + +void UserDefinedSQLObjectsStorageBase::setObject(const String & object_name, const IAST & create_object_query) +{ + std::lock_guard lock(mutex); + object_name_to_create_object_map[object_name] = normalizeCreateFunctionQuery(create_object_query); +} + +void UserDefinedSQLObjectsStorageBase::removeObject(const String & object_name) +{ + std::lock_guard lock(mutex); + object_name_to_create_object_map.erase(object_name); +} + +void UserDefinedSQLObjectsStorageBase::removeAllObjectsExcept(const Strings & object_names_to_keep) +{ + boost::container::flat_set<std::string_view> names_set_to_keep{object_names_to_keep.begin(), object_names_to_keep.end()}; + std::lock_guard lock(mutex); + for (auto it = object_name_to_create_object_map.begin(); it != object_name_to_create_object_map.end();) + { + auto current = it++; + if (!names_set_to_keep.contains(current->first)) + object_name_to_create_object_map.erase(current); + } +} + +} diff --git a/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsStorageBase.h b/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsStorageBase.h new file mode 100644 index 0000000000..cab63a3bfc --- /dev/null +++ b/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsStorageBase.h @@ -0,0 +1,69 @@ +#pragma once + +#include <unordered_map> +#include <mutex> + +#include <Functions/UserDefined/IUserDefinedSQLObjectsStorage.h> + +#include <Parsers/IAST.h> + +namespace DB +{ + +class UserDefinedSQLObjectsStorageBase : public IUserDefinedSQLObjectsStorage +{ +public: + ASTPtr get(const String & object_name) const override; + + ASTPtr tryGet(const String & object_name) const override; + + bool has(const String & object_name) const override; + + std::vector<String> getAllObjectNames() const override; + + std::vector<std::pair<String, ASTPtr>> getAllObjects() const override; + + bool empty() const override; + + bool storeObject( + const ContextPtr & current_context, + UserDefinedSQLObjectType object_type, + const String & object_name, + ASTPtr create_object_query, + bool throw_if_exists, + bool replace_if_exists, + const Settings & settings) override; + + bool removeObject( + const ContextPtr & current_context, + UserDefinedSQLObjectType object_type, + const String & object_name, + bool throw_if_not_exists) override; + +protected: + virtual bool storeObjectImpl( + const ContextPtr & current_context, + UserDefinedSQLObjectType object_type, + const String & object_name, + ASTPtr create_object_query, + bool throw_if_exists, + bool replace_if_exists, + const Settings & settings) = 0; + + virtual bool removeObjectImpl( + const ContextPtr & current_context, + UserDefinedSQLObjectType object_type, + const String & object_name, + bool throw_if_not_exists) = 0; + + std::unique_lock<std::recursive_mutex> getLock() const; + void setAllObjects(const std::vector<std::pair<String, ASTPtr>> & new_objects); + void setObject(const String & object_name, const IAST & create_object_query); + void removeObject(const String & object_name); + void removeAllObjectsExcept(const Strings & object_names_to_keep); + + std::unordered_map<String, ASTPtr> object_name_to_create_object_map; + mutable std::recursive_mutex mutex; +}; + +} diff --git a/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsZooKeeperStorage.cpp b/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsZooKeeperStorage.cpp new file mode 100644 index 0000000000..6e5a533843 --- /dev/null +++ b/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsZooKeeperStorage.cpp @@ -0,0 +1,435 @@ +#include <Functions/UserDefined/UserDefinedSQLObjectsZooKeeperStorage.h> + +#include <Functions/UserDefined/UserDefinedSQLFunctionFactory.h> +#include <Functions/UserDefined/UserDefinedSQLObjectType.h> +#include <Interpreters/Context.h> +#include <Parsers/ParserCreateFunctionQuery.h> +#include <Parsers/formatAST.h> +#include <Parsers/parseQuery.h> +#include <base/sleep.h> +#include <Common/Exception.h> +#include <Common/ZooKeeper/KeeperException.h> +#include <Common/escapeForFileName.h> +#include <Common/logger_useful.h> +#include <Common/quoteString.h> +#include <Common/scope_guard_safe.h> +#include <Common/setThreadName.h> + + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int FUNCTION_ALREADY_EXISTS; + extern const int UNKNOWN_FUNCTION; + extern const int BAD_ARGUMENTS; +} + +namespace +{ + std::string_view getNodePrefix(UserDefinedSQLObjectType object_type) + { + switch (object_type) + { + case UserDefinedSQLObjectType::Function: + return "function_"; + } + UNREACHABLE(); + } + + constexpr std::string_view sql_extension = ".sql"; + + String getNodePath(const String & root_path, UserDefinedSQLObjectType object_type, const String & object_name) + { + return root_path + "/" + String{getNodePrefix(object_type)} + escapeForFileName(object_name) + String{sql_extension}; + } +} + + +UserDefinedSQLObjectsZooKeeperStorage::UserDefinedSQLObjectsZooKeeperStorage( + const ContextPtr & global_context_, const String & zookeeper_path_) + : global_context{global_context_} + , zookeeper_getter{[global_context_]() { return global_context_->getZooKeeper(); }} + , zookeeper_path{zookeeper_path_} + , watch_queue{std::make_shared<ConcurrentBoundedQueue<std::pair<UserDefinedSQLObjectType, String>>>(std::numeric_limits<size_t>::max())} + , log{&Poco::Logger::get("UserDefinedSQLObjectsLoaderFromZooKeeper")} +{ + if (zookeeper_path.empty()) + throw Exception(ErrorCodes::BAD_ARGUMENTS, "ZooKeeper path must be non-empty"); + + if (zookeeper_path.back() == '/') + zookeeper_path.resize(zookeeper_path.size() - 1); + + /// If zookeeper chroot prefix is used, path should start with '/', because chroot concatenates without it. + if (zookeeper_path.front() != '/') + zookeeper_path = "/" + zookeeper_path; +} + +UserDefinedSQLObjectsZooKeeperStorage::~UserDefinedSQLObjectsZooKeeperStorage() +{ + SCOPE_EXIT_SAFE(stopWatchingThread()); +} + +void UserDefinedSQLObjectsZooKeeperStorage::startWatchingThread() +{ + if (!watching_flag.exchange(true)) + { + watching_thread = ThreadFromGlobalPool(&UserDefinedSQLObjectsZooKeeperStorage::processWatchQueue, this); + } +} + +void UserDefinedSQLObjectsZooKeeperStorage::stopWatchingThread() +{ + if (watching_flag.exchange(false)) + { + watch_queue->finish(); + if (watching_thread.joinable()) + watching_thread.join(); + } +} + +zkutil::ZooKeeperPtr UserDefinedSQLObjectsZooKeeperStorage::getZooKeeper() +{ + auto [zookeeper, session_status] = zookeeper_getter.getZooKeeper(); + + if (session_status == zkutil::ZooKeeperCachingGetter::SessionStatus::New) + { + /// It's possible that we connected to different [Zoo]Keeper instance + /// so we may read a bit stale state. + zookeeper->sync(zookeeper_path); + + createRootNodes(zookeeper); + refreshAllObjects(zookeeper); + } + + return zookeeper; +} + +void UserDefinedSQLObjectsZooKeeperStorage::initZooKeeperIfNeeded() +{ + getZooKeeper(); +} + +void UserDefinedSQLObjectsZooKeeperStorage::resetAfterError() +{ + zookeeper_getter.resetCache(); +} + + +void UserDefinedSQLObjectsZooKeeperStorage::loadObjects() +{ + /// loadObjects() is called at start from Server::main(), so it's better not to stop here on no connection to ZooKeeper or any other error. + /// However the watching thread must be started anyway in case the connection will be established later. + if (!objects_loaded) + { + try + { + reloadObjects(); + } + catch (...) + { + tryLogCurrentException(log, "Failed to load user-defined objects"); + } + } + startWatchingThread(); +} + + +void UserDefinedSQLObjectsZooKeeperStorage::processWatchQueue() +{ + LOG_DEBUG(log, "Started watching thread"); + setThreadName("UserDefObjWatch"); + + while (watching_flag) + { + try + { + UserDefinedSQLObjectTypeAndName watched_object; + + /// Re-initialize ZooKeeper session if expired and refresh objects + initZooKeeperIfNeeded(); + + if (!watch_queue->tryPop(watched_object, /* timeout_ms: */ 10000)) + continue; + + auto zookeeper = getZooKeeper(); + const auto & [object_type, object_name] = watched_object; + + if (object_name.empty()) + syncObjects(zookeeper, object_type); + else + refreshObject(zookeeper, object_type, object_name); + } + catch (...) + { + tryLogCurrentException(log, "Will try to restart watching thread after error"); + resetAfterError(); + sleepForSeconds(5); + } + } + + LOG_DEBUG(log, "Stopped watching thread"); +} + + +void UserDefinedSQLObjectsZooKeeperStorage::stopWatching() +{ + stopWatchingThread(); +} + + +void UserDefinedSQLObjectsZooKeeperStorage::reloadObjects() +{ + auto zookeeper = getZooKeeper(); + refreshAllObjects(zookeeper); + startWatchingThread(); +} + + +void UserDefinedSQLObjectsZooKeeperStorage::reloadObject(UserDefinedSQLObjectType object_type, const String & object_name) +{ + auto zookeeper = getZooKeeper(); + refreshObject(zookeeper, object_type, object_name); +} + + +void UserDefinedSQLObjectsZooKeeperStorage::createRootNodes(const zkutil::ZooKeeperPtr & zookeeper) +{ + zookeeper->createAncestors(zookeeper_path); + zookeeper->createIfNotExists(zookeeper_path, ""); +} + +bool UserDefinedSQLObjectsZooKeeperStorage::storeObjectImpl( + const ContextPtr & /*current_context*/, + UserDefinedSQLObjectType object_type, + const String & object_name, + ASTPtr create_object_query, + bool throw_if_exists, + bool replace_if_exists, + const Settings &) +{ + String path = getNodePath(zookeeper_path, object_type, object_name); + LOG_DEBUG(log, "Storing user-defined object {} at zk path {}", backQuote(object_name), path); + + WriteBufferFromOwnString create_statement_buf; + formatAST(*create_object_query, create_statement_buf, false); + writeChar('\n', create_statement_buf); + String create_statement = create_statement_buf.str(); + + auto zookeeper = getZooKeeper(); + + size_t num_attempts = 10; + while (true) + { + auto code = zookeeper->tryCreate(path, create_statement, zkutil::CreateMode::Persistent); + if ((code != Coordination::Error::ZOK) && (code != Coordination::Error::ZNODEEXISTS)) + throw zkutil::KeeperException::fromPath(code, path); + + if (code == Coordination::Error::ZNODEEXISTS) + { + if (throw_if_exists) + throw Exception(ErrorCodes::FUNCTION_ALREADY_EXISTS, "User-defined function '{}' already exists", object_name); + else if (!replace_if_exists) + return false; + + code = zookeeper->trySet(path, create_statement); + if ((code != Coordination::Error::ZOK) && (code != Coordination::Error::ZNONODE)) + throw zkutil::KeeperException::fromPath(code, path); + } + + if (code == Coordination::Error::ZOK) + break; + + if (!--num_attempts) + throw zkutil::KeeperException::fromPath(code, path); + } + LOG_DEBUG(log, "Object {} stored", backQuote(object_name)); + + /// Refresh object and set watch for it. Because it can be replaced by another node after creation. + refreshObject(zookeeper, object_type, object_name); + + return true; +} + + +bool UserDefinedSQLObjectsZooKeeperStorage::removeObjectImpl( + const ContextPtr & /*current_context*/, + UserDefinedSQLObjectType object_type, + const String & object_name, + bool throw_if_not_exists) +{ + String path = getNodePath(zookeeper_path, object_type, object_name); + LOG_DEBUG(log, "Removing user-defined object {} at zk path {}", backQuote(object_name), path); + + auto zookeeper = getZooKeeper(); + + auto code = zookeeper->tryRemove(path); + if ((code != Coordination::Error::ZOK) && (code != Coordination::Error::ZNONODE)) + throw zkutil::KeeperException::fromPath(code, path); + + if (code == Coordination::Error::ZNONODE) + { + if (throw_if_not_exists) + throw Exception(ErrorCodes::UNKNOWN_FUNCTION, "User-defined object '{}' doesn't exist", object_name); + else + return false; + } + + LOG_DEBUG(log, "Object {} removed", backQuote(object_name)); + return true; +} + +bool UserDefinedSQLObjectsZooKeeperStorage::getObjectDataAndSetWatch( + const zkutil::ZooKeeperPtr & zookeeper, + String & data, + const String & path, + UserDefinedSQLObjectType object_type, + const String & object_name) +{ + const auto object_watcher = [my_watch_queue = watch_queue, object_type, object_name](const Coordination::WatchResponse & response) + { + if (response.type == Coordination::Event::CHANGED) + { + [[maybe_unused]] bool inserted = my_watch_queue->emplace(object_type, object_name); + /// `inserted` can be false if `watch_queue` was already finalized (which happens when stopWatching() is called). + } + /// Event::DELETED is processed as child event by getChildren watch + }; + + Coordination::Stat entity_stat; + String object_create_query; + return zookeeper->tryGetWatch(path, data, &entity_stat, object_watcher); +} + +ASTPtr UserDefinedSQLObjectsZooKeeperStorage::parseObjectData(const String & object_data, UserDefinedSQLObjectType object_type) +{ + switch (object_type) + { + case UserDefinedSQLObjectType::Function: { + ParserCreateFunctionQuery parser; + ASTPtr ast = parseQuery( + parser, + object_data.data(), + object_data.data() + object_data.size(), + "", + 0, + global_context->getSettingsRef().max_parser_depth); + return ast; + } + } + UNREACHABLE(); +} + +ASTPtr UserDefinedSQLObjectsZooKeeperStorage::tryLoadObject( + const zkutil::ZooKeeperPtr & zookeeper, UserDefinedSQLObjectType object_type, const String & object_name) +{ + String path = getNodePath(zookeeper_path, object_type, object_name); + LOG_DEBUG(log, "Loading user defined object {} from zk path {}", backQuote(object_name), path); + + try + { + String object_data; + bool exists = getObjectDataAndSetWatch(zookeeper, object_data, path, object_type, object_name); + + if (!exists) + { + LOG_INFO(log, "User-defined object '{}' can't be loaded from path {}, because it doesn't exist", backQuote(object_name), path); + return nullptr; + } + + return parseObjectData(object_data, object_type); + } + catch (...) + { + tryLogCurrentException(log, fmt::format("while loading user defined SQL object {}", backQuote(object_name))); + return nullptr; /// Failed to load this sql object, will ignore it + } +} + +Strings UserDefinedSQLObjectsZooKeeperStorage::getObjectNamesAndSetWatch( + const zkutil::ZooKeeperPtr & zookeeper, UserDefinedSQLObjectType object_type) +{ + auto object_list_watcher = [my_watch_queue = watch_queue, object_type](const Coordination::WatchResponse &) + { + [[maybe_unused]] bool inserted = my_watch_queue->emplace(object_type, ""); + /// `inserted` can be false if `watch_queue` was already finalized (which happens when stopWatching() is called). + }; + + Coordination::Stat stat; + const auto node_names = zookeeper->getChildrenWatch(zookeeper_path, &stat, object_list_watcher); + const auto prefix = getNodePrefix(object_type); + + Strings object_names; + object_names.reserve(node_names.size()); + for (const auto & node_name : node_names) + { + if (node_name.starts_with(prefix) && node_name.ends_with(sql_extension)) + { + String object_name = unescapeForFileName(node_name.substr(prefix.length(), node_name.length() - prefix.length() - sql_extension.length())); + if (!object_name.empty()) + object_names.push_back(std::move(object_name)); + } + } + + return object_names; +} + +void UserDefinedSQLObjectsZooKeeperStorage::refreshAllObjects(const zkutil::ZooKeeperPtr & zookeeper) +{ + /// It doesn't make sense to keep the old watch events because we will reread everything in this function. + watch_queue->clear(); + + refreshObjects(zookeeper, UserDefinedSQLObjectType::Function); + objects_loaded = true; +} + +void UserDefinedSQLObjectsZooKeeperStorage::refreshObjects(const zkutil::ZooKeeperPtr & zookeeper, UserDefinedSQLObjectType object_type) +{ + LOG_DEBUG(log, "Refreshing all user-defined {} objects", object_type); + Strings object_names = getObjectNamesAndSetWatch(zookeeper, object_type); + + /// Read & parse all SQL objects from ZooKeeper + std::vector<std::pair<String, ASTPtr>> function_names_and_asts; + for (const auto & function_name : object_names) + { + if (auto ast = tryLoadObject(zookeeper, UserDefinedSQLObjectType::Function, function_name)) + function_names_and_asts.emplace_back(function_name, ast); + } + + setAllObjects(function_names_and_asts); + + LOG_DEBUG(log, "All user-defined {} objects refreshed", object_type); +} + +void UserDefinedSQLObjectsZooKeeperStorage::syncObjects(const zkutil::ZooKeeperPtr & zookeeper, UserDefinedSQLObjectType object_type) +{ + LOG_DEBUG(log, "Syncing user-defined {} objects", object_type); + Strings object_names = getObjectNamesAndSetWatch(zookeeper, object_type); + + getLock(); + + /// Remove stale objects + removeAllObjectsExcept(object_names); + /// Read & parse only new SQL objects from ZooKeeper + for (const auto & function_name : object_names) + { + if (!UserDefinedSQLFunctionFactory::instance().has(function_name)) + refreshObject(zookeeper, UserDefinedSQLObjectType::Function, function_name); + } + + LOG_DEBUG(log, "User-defined {} objects synced", object_type); +} + +void UserDefinedSQLObjectsZooKeeperStorage::refreshObject( + const zkutil::ZooKeeperPtr & zookeeper, UserDefinedSQLObjectType object_type, const String & object_name) +{ + auto ast = tryLoadObject(zookeeper, object_type, object_name); + + if (ast) + setObject(object_name, *ast); + else + removeObject(object_name); +} + +} diff --git a/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsLoaderFromZooKeeper.h b/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsZooKeeperStorage.h index 38e061fd4d..9f41763c59 100644 --- a/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsLoaderFromZooKeeper.h +++ b/contrib/clickhouse/src/Functions/UserDefined/UserDefinedSQLObjectsZooKeeperStorage.h @@ -1,6 +1,6 @@ #pragma once -#include <Functions/UserDefined/IUserDefinedSQLObjectsLoader.h> +#include <Functions/UserDefined/UserDefinedSQLObjectsStorageBase.h> #include <Interpreters/Context_fwd.h> #include <Parsers/IAST_fwd.h> #include <Common/ConcurrentBoundedQueue.h> @@ -12,11 +12,11 @@ namespace DB { /// Loads user-defined sql objects from ZooKeeper. -class UserDefinedSQLObjectsLoaderFromZooKeeper : public IUserDefinedSQLObjectsLoader +class UserDefinedSQLObjectsZooKeeperStorage : public UserDefinedSQLObjectsStorageBase { public: - UserDefinedSQLObjectsLoaderFromZooKeeper(const ContextPtr & global_context_, const String & zookeeper_path_); - ~UserDefinedSQLObjectsLoaderFromZooKeeper() override; + UserDefinedSQLObjectsZooKeeperStorage(const ContextPtr & global_context_, const String & zookeeper_path_); + ~UserDefinedSQLObjectsZooKeeperStorage() override; bool isReplicated() const override { return true; } String getReplicationID() const override { return zookeeper_path; } @@ -26,16 +26,21 @@ public: void reloadObjects() override; void reloadObject(UserDefinedSQLObjectType object_type, const String & object_name) override; - bool storeObject( +private: + bool storeObjectImpl( + const ContextPtr & current_context, UserDefinedSQLObjectType object_type, const String & object_name, - const IAST & create_object_query, + ASTPtr create_object_query, bool throw_if_exists, bool replace_if_exists, const Settings & settings) override; - bool removeObject(UserDefinedSQLObjectType object_type, const String & object_name, bool throw_if_not_exists) override; + bool removeObjectImpl( + const ContextPtr & current_context, + UserDefinedSQLObjectType object_type, + const String & object_name, + bool throw_if_not_exists) override; -private: void processWatchQueue(); zkutil::ZooKeeperPtr getZooKeeper(); diff --git a/contrib/clickhouse/src/Functions/UserDefined/createUserDefinedSQLObjectsLoader.cpp b/contrib/clickhouse/src/Functions/UserDefined/createUserDefinedSQLObjectsLoader.cpp index b7ebc7abf1..e69de29bb2 100644 --- a/contrib/clickhouse/src/Functions/UserDefined/createUserDefinedSQLObjectsLoader.cpp +++ b/contrib/clickhouse/src/Functions/UserDefined/createUserDefinedSQLObjectsLoader.cpp @@ -1,44 +0,0 @@ -#include <Functions/UserDefined/createUserDefinedSQLObjectsLoader.h> -#include <Functions/UserDefined/UserDefinedSQLObjectsLoaderFromDisk.h> -#include <Functions/UserDefined/UserDefinedSQLObjectsLoaderFromZooKeeper.h> -#include <Interpreters/Context.h> -#include <Poco/Util/AbstractConfiguration.h> -#include <filesystem> - -namespace fs = std::filesystem; - - -namespace DB -{ - - -namespace ErrorCodes -{ - extern const int INVALID_CONFIG_PARAMETER; -} - -std::unique_ptr<IUserDefinedSQLObjectsLoader> createUserDefinedSQLObjectsLoader(const ContextMutablePtr & global_context) -{ - const String zookeeper_path_key = "user_defined_zookeeper_path"; - const String disk_path_key = "user_defined_path"; - - const auto & config = global_context->getConfigRef(); - if (config.has(zookeeper_path_key)) - { - if (config.has(disk_path_key)) - { - throw Exception( - ErrorCodes::INVALID_CONFIG_PARAMETER, - "'{}' and '{}' must not be both specified in the config", - zookeeper_path_key, - disk_path_key); - } - return std::make_unique<UserDefinedSQLObjectsLoaderFromZooKeeper>(global_context, config.getString(zookeeper_path_key)); - } - - String default_path = fs::path{global_context->getPath()} / "user_defined/"; - String path = config.getString(disk_path_key, default_path); - return std::make_unique<UserDefinedSQLObjectsLoaderFromDisk>(global_context, path); -} - -} diff --git a/contrib/clickhouse/src/Functions/UserDefined/createUserDefinedSQLObjectsLoader.h b/contrib/clickhouse/src/Functions/UserDefined/createUserDefinedSQLObjectsLoader.h deleted file mode 100644 index b3a4623dba..0000000000 --- a/contrib/clickhouse/src/Functions/UserDefined/createUserDefinedSQLObjectsLoader.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include <Interpreters/Context_fwd.h> - - -namespace DB -{ -class IUserDefinedSQLObjectsLoader; - -std::unique_ptr<IUserDefinedSQLObjectsLoader> createUserDefinedSQLObjectsLoader(const ContextMutablePtr & global_context); - -} diff --git a/contrib/clickhouse/src/Functions/UserDefined/createUserDefinedSQLObjectsStorage.cpp b/contrib/clickhouse/src/Functions/UserDefined/createUserDefinedSQLObjectsStorage.cpp new file mode 100644 index 0000000000..f884702450 --- /dev/null +++ b/contrib/clickhouse/src/Functions/UserDefined/createUserDefinedSQLObjectsStorage.cpp @@ -0,0 +1,44 @@ +#include <Functions/UserDefined/createUserDefinedSQLObjectsStorage.h> +#include <Functions/UserDefined/UserDefinedSQLObjectsDiskStorage.h> +#include <Functions/UserDefined/UserDefinedSQLObjectsZooKeeperStorage.h> +#include <Interpreters/Context.h> +#include <Poco/Util/AbstractConfiguration.h> +#include <filesystem> + +namespace fs = std::filesystem; + + +namespace DB +{ + + +namespace ErrorCodes +{ + extern const int INVALID_CONFIG_PARAMETER; +} + +std::unique_ptr<IUserDefinedSQLObjectsStorage> createUserDefinedSQLObjectsStorage(const ContextMutablePtr & global_context) +{ + const String zookeeper_path_key = "user_defined_zookeeper_path"; + const String disk_path_key = "user_defined_path"; + + const auto & config = global_context->getConfigRef(); + if (config.has(zookeeper_path_key)) + { + if (config.has(disk_path_key)) + { + throw Exception( + ErrorCodes::INVALID_CONFIG_PARAMETER, + "'{}' and '{}' must not be both specified in the config", + zookeeper_path_key, + disk_path_key); + } + return std::make_unique<UserDefinedSQLObjectsZooKeeperStorage>(global_context, config.getString(zookeeper_path_key)); + } + + String default_path = fs::path{global_context->getPath()} / "user_defined/"; + String path = config.getString(disk_path_key, default_path); + return std::make_unique<UserDefinedSQLObjectsDiskStorage>(global_context, path); +} + +} diff --git a/contrib/clickhouse/src/Functions/UserDefined/createUserDefinedSQLObjectsStorage.h b/contrib/clickhouse/src/Functions/UserDefined/createUserDefinedSQLObjectsStorage.h new file mode 100644 index 0000000000..01659372de --- /dev/null +++ b/contrib/clickhouse/src/Functions/UserDefined/createUserDefinedSQLObjectsStorage.h @@ -0,0 +1,12 @@ +#pragma once + +#include <Interpreters/Context_fwd.h> + + +namespace DB +{ +class IUserDefinedSQLObjectsStorage; + +std::unique_ptr<IUserDefinedSQLObjectsStorage> createUserDefinedSQLObjectsStorage(const ContextMutablePtr & global_context); + +} diff --git a/contrib/clickhouse/src/Interpreters/Context.cpp b/contrib/clickhouse/src/Interpreters/Context.cpp index 63f5997574..93fe12431f 100644 --- a/contrib/clickhouse/src/Interpreters/Context.cpp +++ b/contrib/clickhouse/src/Interpreters/Context.cpp @@ -58,8 +58,8 @@ #include <Interpreters/EmbeddedDictionaries.h> #include <Interpreters/ExternalDictionariesLoader.h> #include <Functions/UserDefined/ExternalUserDefinedExecutableFunctionsLoader.h> -#include <Functions/UserDefined/IUserDefinedSQLObjectsLoader.h> -#include <Functions/UserDefined/createUserDefinedSQLObjectsLoader.h> +#include <Functions/UserDefined/IUserDefinedSQLObjectsStorage.h> +#include <Functions/UserDefined/createUserDefinedSQLObjectsStorage.h> #include <Interpreters/ProcessList.h> #include <Interpreters/InterserverCredentials.h> #include <Interpreters/Cluster.h> @@ -238,7 +238,7 @@ struct ContextSharedPart : boost::noncopyable ExternalLoaderXMLConfigRepository * user_defined_executable_functions_config_repository = nullptr; scope_guard user_defined_executable_functions_xmls; - mutable std::unique_ptr<IUserDefinedSQLObjectsLoader> user_defined_sql_objects_loader; + mutable std::unique_ptr<IUserDefinedSQLObjectsStorage> user_defined_sql_objects_storage; #if USE_NLP mutable std::optional<SynonymsExtensions> synonyms_extensions; @@ -489,7 +489,7 @@ struct ContextSharedPart : boost::noncopyable SHUTDOWN(log, "dictionaries loader", external_dictionaries_loader, enablePeriodicUpdates(false)); SHUTDOWN(log, "UDFs loader", external_user_defined_executable_functions_loader, enablePeriodicUpdates(false)); - SHUTDOWN(log, "another UDFs loader", user_defined_sql_objects_loader, stopWatching()); + SHUTDOWN(log, "another UDFs storage", user_defined_sql_objects_storage, stopWatching()); LOG_TRACE(log, "Shutting down named sessions"); Session::shutdownNamedSessions(); @@ -516,7 +516,7 @@ struct ContextSharedPart : boost::noncopyable std::unique_ptr<EmbeddedDictionaries> delete_embedded_dictionaries; std::unique_ptr<ExternalDictionariesLoader> delete_external_dictionaries_loader; std::unique_ptr<ExternalUserDefinedExecutableFunctionsLoader> delete_external_user_defined_executable_functions_loader; - std::unique_ptr<IUserDefinedSQLObjectsLoader> delete_user_defined_sql_objects_loader; + std::unique_ptr<IUserDefinedSQLObjectsStorage> delete_user_defined_sql_objects_storage; std::unique_ptr<BackgroundSchedulePool> delete_buffer_flush_schedule_pool; std::unique_ptr<BackgroundSchedulePool> delete_schedule_pool; std::unique_ptr<BackgroundSchedulePool> delete_distributed_schedule_pool; @@ -575,7 +575,7 @@ struct ContextSharedPart : boost::noncopyable delete_embedded_dictionaries = std::move(embedded_dictionaries); delete_external_dictionaries_loader = std::move(external_dictionaries_loader); delete_external_user_defined_executable_functions_loader = std::move(external_user_defined_executable_functions_loader); - delete_user_defined_sql_objects_loader = std::move(user_defined_sql_objects_loader); + delete_user_defined_sql_objects_storage = std::move(user_defined_sql_objects_storage); delete_buffer_flush_schedule_pool = std::move(buffer_flush_schedule_pool); delete_schedule_pool = std::move(schedule_pool); delete_distributed_schedule_pool = std::move(distributed_schedule_pool); @@ -602,7 +602,7 @@ struct ContextSharedPart : boost::noncopyable delete_embedded_dictionaries.reset(); delete_external_dictionaries_loader.reset(); delete_external_user_defined_executable_functions_loader.reset(); - delete_user_defined_sql_objects_loader.reset(); + delete_user_defined_sql_objects_storage.reset(); delete_ddl_worker.reset(); delete_buffer_flush_schedule_pool.reset(); delete_schedule_pool.reset(); @@ -2188,20 +2188,26 @@ void Context::loadOrReloadUserDefinedExecutableFunctions(const Poco::Util::Abstr shared->user_defined_executable_functions_xmls = external_user_defined_executable_functions_loader.addConfigRepository(std::move(repository)); } -const IUserDefinedSQLObjectsLoader & Context::getUserDefinedSQLObjectsLoader() const +const IUserDefinedSQLObjectsStorage & Context::getUserDefinedSQLObjectsStorage() const { auto lock = getLock(); - if (!shared->user_defined_sql_objects_loader) - shared->user_defined_sql_objects_loader = createUserDefinedSQLObjectsLoader(getGlobalContext()); - return *shared->user_defined_sql_objects_loader; + if (!shared->user_defined_sql_objects_storage) + shared->user_defined_sql_objects_storage = createUserDefinedSQLObjectsStorage(getGlobalContext()); + return *shared->user_defined_sql_objects_storage; } -IUserDefinedSQLObjectsLoader & Context::getUserDefinedSQLObjectsLoader() +IUserDefinedSQLObjectsStorage & Context::getUserDefinedSQLObjectsStorage() { auto lock = getLock(); - if (!shared->user_defined_sql_objects_loader) - shared->user_defined_sql_objects_loader = createUserDefinedSQLObjectsLoader(getGlobalContext()); - return *shared->user_defined_sql_objects_loader; + if (!shared->user_defined_sql_objects_storage) + shared->user_defined_sql_objects_storage = createUserDefinedSQLObjectsStorage(getGlobalContext()); + return *shared->user_defined_sql_objects_storage; +} + +void Context::setUserDefinedSQLObjectsStorage(std::unique_ptr<IUserDefinedSQLObjectsStorage> storage) +{ + auto lock = getLock(); + shared->user_defined_sql_objects_storage = std::move(storage); } #if USE_NLP diff --git a/contrib/clickhouse/src/Interpreters/Context.h b/contrib/clickhouse/src/Interpreters/Context.h index eba205a88c..d9ce12d9ae 100644 --- a/contrib/clickhouse/src/Interpreters/Context.h +++ b/contrib/clickhouse/src/Interpreters/Context.h @@ -65,7 +65,7 @@ enum class RowPolicyFilterType; class EmbeddedDictionaries; class ExternalDictionariesLoader; class ExternalUserDefinedExecutableFunctionsLoader; -class IUserDefinedSQLObjectsLoader; +class IUserDefinedSQLObjectsStorage; class InterserverCredentials; using InterserverCredentialsPtr = std::shared_ptr<const InterserverCredentials>; class InterserverIOHandler; @@ -764,8 +764,9 @@ public: const ExternalUserDefinedExecutableFunctionsLoader & getExternalUserDefinedExecutableFunctionsLoader() const; ExternalUserDefinedExecutableFunctionsLoader & getExternalUserDefinedExecutableFunctionsLoader(); ExternalUserDefinedExecutableFunctionsLoader & getExternalUserDefinedExecutableFunctionsLoaderUnlocked(); - const IUserDefinedSQLObjectsLoader & getUserDefinedSQLObjectsLoader() const; - IUserDefinedSQLObjectsLoader & getUserDefinedSQLObjectsLoader(); + const IUserDefinedSQLObjectsStorage & getUserDefinedSQLObjectsStorage() const; + IUserDefinedSQLObjectsStorage & getUserDefinedSQLObjectsStorage(); + void setUserDefinedSQLObjectsStorage(std::unique_ptr<IUserDefinedSQLObjectsStorage> storage); void loadOrReloadUserDefinedExecutableFunctions(const Poco::Util::AbstractConfiguration & config); #if USE_NLP diff --git a/contrib/clickhouse/src/Interpreters/InterpreterCreateFunctionQuery.cpp b/contrib/clickhouse/src/Interpreters/InterpreterCreateFunctionQuery.cpp index 3e87f4fe44..b155476fd7 100644 --- a/contrib/clickhouse/src/Interpreters/InterpreterCreateFunctionQuery.cpp +++ b/contrib/clickhouse/src/Interpreters/InterpreterCreateFunctionQuery.cpp @@ -1,7 +1,7 @@ #include <Interpreters/InterpreterCreateFunctionQuery.h> #include <Access/ContextAccess.h> -#include <Functions/UserDefined/IUserDefinedSQLObjectsLoader.h> +#include <Functions/UserDefined/IUserDefinedSQLObjectsStorage.h> #include <Functions/UserDefined/UserDefinedSQLFunctionFactory.h> #include <Interpreters/Context.h> #include <Interpreters/executeDDLQueryOnCluster.h> @@ -32,7 +32,7 @@ BlockIO InterpreterCreateFunctionQuery::execute() if (!create_function_query.cluster.empty()) { - if (current_context->getUserDefinedSQLObjectsLoader().isReplicated()) + if (current_context->getUserDefinedSQLObjectsStorage().isReplicated()) throw Exception(ErrorCodes::INCORRECT_QUERY, "ON CLUSTER is not allowed because used-defined functions are replicated automatically"); DDLQueryOnClusterParams params; diff --git a/contrib/clickhouse/src/Interpreters/InterpreterDropFunctionQuery.cpp b/contrib/clickhouse/src/Interpreters/InterpreterDropFunctionQuery.cpp index af60d9c5df..c2cd24044d 100644 --- a/contrib/clickhouse/src/Interpreters/InterpreterDropFunctionQuery.cpp +++ b/contrib/clickhouse/src/Interpreters/InterpreterDropFunctionQuery.cpp @@ -1,7 +1,7 @@ #include <Interpreters/InterpreterDropFunctionQuery.h> #include <Access/ContextAccess.h> -#include <Functions/UserDefined/IUserDefinedSQLObjectsLoader.h> +#include <Functions/UserDefined/IUserDefinedSQLObjectsStorage.h> #include <Functions/UserDefined/UserDefinedSQLFunctionFactory.h> #include <Interpreters/Context.h> #include <Interpreters/FunctionNameNormalizer.h> @@ -32,7 +32,7 @@ BlockIO InterpreterDropFunctionQuery::execute() if (!drop_function_query.cluster.empty()) { - if (current_context->getUserDefinedSQLObjectsLoader().isReplicated()) + if (current_context->getUserDefinedSQLObjectsStorage().isReplicated()) throw Exception(ErrorCodes::INCORRECT_QUERY, "ON CLUSTER is not allowed because used-defined functions are replicated automatically"); DDLQueryOnClusterParams params; diff --git a/contrib/clickhouse/src/Interpreters/removeOnClusterClauseIfNeeded.cpp b/contrib/clickhouse/src/Interpreters/removeOnClusterClauseIfNeeded.cpp index da3930d62a..f8df03ed83 100644 --- a/contrib/clickhouse/src/Interpreters/removeOnClusterClauseIfNeeded.cpp +++ b/contrib/clickhouse/src/Interpreters/removeOnClusterClauseIfNeeded.cpp @@ -3,7 +3,7 @@ #include <Access/AccessControl.h> #include <Access/ReplicatedAccessStorage.h> #include <Common/logger_useful.h> -#include <Functions/UserDefined/IUserDefinedSQLObjectsLoader.h> +#include <Functions/UserDefined/IUserDefinedSQLObjectsStorage.h> #include <Interpreters/Context.h> #include <Parsers/ASTCreateFunctionQuery.h> #include <Parsers/ASTDropFunctionQuery.h> @@ -47,7 +47,7 @@ ASTPtr removeOnClusterClauseIfNeeded(const ASTPtr & query, ContextPtr context, c if ((isUserDefinedFunctionQuery(query) && context->getSettings().ignore_on_cluster_for_replicated_udf_queries - && context->getUserDefinedSQLObjectsLoader().isReplicated()) + && context->getUserDefinedSQLObjectsStorage().isReplicated()) || (isAccessControlQuery(query) && context->getSettings().ignore_on_cluster_for_replicated_access_entities_queries && context->getAccessControl().containsStorage(ReplicatedAccessStorage::STORAGE_TYPE))) diff --git a/contrib/clickhouse/src/ya.make b/contrib/clickhouse/src/ya.make index 1f5158a3ca..4b91f7a155 100644 --- a/contrib/clickhouse/src/ya.make +++ b/contrib/clickhouse/src/ya.make @@ -770,9 +770,13 @@ SRCS( Functions/UserDefined/UserDefinedSQLFunctionFactory.cpp Functions/UserDefined/UserDefinedSQLFunctionVisitor.cpp Functions/UserDefined/UserDefinedSQLObjectsBackup.cpp + Functions/UserDefined/UserDefinedSQLObjectsDiskStorage.cpp Functions/UserDefined/UserDefinedSQLObjectsLoaderFromDisk.cpp Functions/UserDefined/UserDefinedSQLObjectsLoaderFromZooKeeper.cpp + Functions/UserDefined/UserDefinedSQLObjectsStorageBase.cpp + Functions/UserDefined/UserDefinedSQLObjectsZooKeeperStorage.cpp Functions/UserDefined/createUserDefinedSQLObjectsLoader.cpp + Functions/UserDefined/createUserDefinedSQLObjectsStorage.cpp Functions/divide/divide.cpp GLOBAL Functions/CRC.cpp GLOBAL Functions/CastOverloadResolver.cpp diff --git a/contrib/libs/curl/COPYING b/contrib/libs/curl/COPYING index d1eab3eb93..90f05adf25 100644 --- a/contrib/libs/curl/COPYING +++ b/contrib/libs/curl/COPYING @@ -1,6 +1,6 @@ COPYRIGHT AND PERMISSION NOTICE -Copyright (c) 1996 - 2023, Daniel Stenberg, <daniel@haxx.se>, and many +Copyright (c) 1996 - 2022, Daniel Stenberg, <daniel@haxx.se>, and many contributors, see the THANKS file. All rights reserved. diff --git a/contrib/libs/curl/RELEASE-NOTES b/contrib/libs/curl/RELEASE-NOTES index 4720b47e2a..9b70c8c86e 100644 --- a/contrib/libs/curl/RELEASE-NOTES +++ b/contrib/libs/curl/RELEASE-NOTES @@ -1,43 +1,210 @@ -curl and libcurl 8.2.1 +curl and libcurl 7.86.0 - Public curl releases: 221 - Command line options: 255 - curl_easy_setopt() options: 303 + Public curl releases: 211 + Command line options: 248 + curl_easy_setopt() options: 300 Public functions in libcurl: 91 - Contributors: 2927 + Contributors: 2733 This release includes the following changes: + o NPN: remove support for and use of [16] + o Websockets: initial support [23] This release includes the following bugfixes: - o amigaos: fix sys/mbuf.h m_len macro clash [9] - o amissl: add missing signal.h include [8] - o amissl: fix AmiSSL v5 detection [2] - o cfilters: rename close/connect functions to avoid clashes [12] - o ciphers.d: put URL in first column [1] - o cmake: add `libcurlu`/`libcurltool` for unit tests [5] - o cmake: update ngtcp2 detection [4] - o configure: check for nghttp2_session_get_stream_local_window_size [14] - o CONTRIBUTE: drop mention of copyright year ranges [20] - o CONTRIBUTE: fix syntax in commit message description [21] - o curl_multi_wait.3: fix arg quoting to doc macro .BR [27] - o docs: mark two TLS options for TLS, not SSL [26] - o docs: provide more see also for cipher options [23] - o hostip: return IPv6 first for localhost resolves [16] - o http2: fix regression on upload EOF handling [13] - o http: VLH, very large header test and fixes [19] - o libcurl-errors.3: add CURLUE_OK [11] - o os400: correct EXPECTED_STRING_LASTZEROTERMINATED [7] - o quiche: fix lookup of transfer at multi [18] - o quiche: fix segfault and other things [15] - o rustls: update rustls-ffi 0.10.0 [24] - o socks: print ipv6 address within brackets [10] - o src/mkhelp: strip off escape sequences [22] - o tool: fix tool_seek_cb build when SIZEOF_CURL_OFF_T > SIZEOF_OFF_T [17] - o transfer: do not clear the credentials on redirect to absolute URL [6] - o unittest: remove unneeded *_LDADD [3] - o websocket: rename arguments/variables to match docs [25] + o altsvc: reject bad port numbers [86] + o altsvc: use 'h3' for h3 [46] + o amiga: do not hardcode openssl/zlib into the os config [158] + o amiga: set SIZEOF_CURL_OFF_T=8 by default [150] + o amigaos: add missing curl header [159] + o asyn-ares: set hint flags when calling ares_getaddrinfo [93] + o autotools: allow --enable-symbol-hiding with windows [65] + o autotools: allow unix sockets on Windows [144] + o autotools: reduce brute-force when detecting recv/send arg list [66] + o aws_sigv4: fix header computation [139] + o bearssl: make it proper C89 compliant + o CI/GHA: cancel outdated CI runs on new PR changes [20] + o CI/GHA: merge msh3 and openssl3 builds into linux workflow [110] + o cirrus-ci: add macOS build with m1 [81] + o cirrus: use make LDFLAGS=-all-static instead of curl_LDFLAGS [129] + o cli tool: do not use disabled protocols + o cmake: add missing inet_ntop check [145] + o cmake: add the check of HAVE_SOCKETPAIR [98] + o cmake: define BUILDING_LIBCURL in lib/CMakeLists, not config.h [5] + o cmake: delete duplicate HAVE_GETADDRINFO test [149] + o cmake: enable more detection on Windows [143] + o cmake: fix original MinGW builds [177] + o cmake: improve usability of CMake build as a sub-project [186] + o cmake: set HAVE_GETADDRINFO_THREADSAFE on Windows [147] + o cmake: set HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID on Windows [146] + o cmake: sync HAVE_SIGNAL detection with autotools [148] + o cmdline/docs: add a required 'multi' keyword for each option [160] + o configure: correct the wording when checking grep -E [13] + o configure: deprecate builds with small curl_off_t [89] + o configure: fail if '--without-ssl' + explicit parameter for an ssl lib [164] + o configure: the ngtcp2 option should default to 'no' [125] + o connect: change verbose IPv6 address:port to [address]:port [83] + o connect: fix builds without AF_INET6 [152] + o connect: fix Curl_updateconninfo for TRNSPRT_UNIX [108] + o connect: fix the wrong error message on connect failures [55] + o content_encoding: use writer struct subclasses for different encodings [8] + o cookie: reject cookie names or content with TAB characters [94] + o ctype: remove all use of <ctype.h>, use our own versions [12] + o curl-compilers.m4: for gcc + want warnings, set gnu89 standard [72] + o curl-compilers.m4: use -O2 as default optimize for clang [6] + o curl-wolfssl.m4: error out if wolfSSL is not usable [102] + o curl.h: fix mention of wrong error code in comment + o curl/add_file_name_to_url: use the libcurl URL parser [99] + o curl/add_parallel_transfers: better error handling [101] + o curl/get_url_file_name: use libcurl URL parser [97] + o curl: warn for --ssl use, considered insecure [49] + o curl_ctype: convert to macros-only [10] + o curl_easy_pause.3: unpausing is as fast as possible [14] + o curl_escape.3: fix typo [50] + o curl_setup: disable use of FLOSS for 64-bit NonStop builds [69] + o curl_setup: include curl.h after platform setup headers [37] + o curl_setup: include only system.h instead of curl.h [34] + o curl_strequal.3: fix argument typo [60] + o curl_url_set.3: document CURLU_APPENDQUERY proper [96] + o CURLMOPT_PIPELINING.3: dedup manpage xref [111] + o CURLOPT_ACCEPT_ENCODING.3: remove "four" as they are five [85] + o CURLOPT_AUTOREFERER.3: highlight the privacy leak risk [161] + o CURLOPT_COOKIEFILE: insist on "" for enable-without-file [119] + o CURLOPT_COOKIELIST.3: fix formatting mistake [80] + o CURLOPT_DNS_INTERFACE.3: mention it works for almost all protocols [15] + o CURLOPT_MIMEPOST.3: add an (inline) example [126] + o CURLOPT_POSTFIELDS.3: refer to CURLOPT_MIMEPOST [167] + o CURLOPT_PROXY_SSLCERT_BLOB.3: this is for HTTPS proxies [9] + o CURLOPT_WILDCARDMATCH.3: Fix backslash escaping under single quotes [172] + o CURLSHOPT_UNLOCKFUNC.3: the callback has no 'access' argument [84] + o DEPRECATE.md: Support for systems without 64 bit data types [19] + o docs/examples: avoid deprecated options in examples where possible [115] + o docs/INSTALL: update Android Instructions for newer NDKs [151] + o docs/libcurl/symbols-in-versions: add several missing symbols + o docs: 100+ spellfixes + o docs: correct missing uppercase in Markdown files [38] + o docs: document more server names for test files + o docs: fix deprecation versions inconsistencies [123] + o docs: make sure libcurl opts examples pass in long arguments [182] + o docs: remove mentions of deprecated '--without-openssl' parameter [170] + o docs: tag curl options better in man pages + o docs: tell about disabled protocols in CURLOPT_*PROTOCOLS_STR. + o docs: update sourceforge project links [95] + o easy: fix the #include order [53] + o easy: fix the altsvc init for curl_easy_duphandle [77] + o easy_lock: check for HAVE_STDATOMIC_H as well [187] + o examples/chkspeed: improve portability [48] + o formdata: fix warning: 'CURLformoption' is promoted to 'int' [24] + o ftp: ignore a 550 response to MDTM [1] + o ftp: remove redundant if [163] + o functypes: provide the recv and send arg and return types [87] + o getparameter: return PARAM_MANUAL_REQUESTED for -M even when disabled [17] + o GHA: build tests in a separate step from the running of them [78] + o GHA: run proselint on markdown files [22] + o github: initial CODEOWNERS setup for CI configuration [52] + o header: define public API functions as extern c [26] + o headers: reset the requests counter at transfer start [25] + o hostip: guard PF_INET6 use [157] + o hostip: lazily wait to figure out if IPv6 works until needed [36] + o http, vauth: always provide Curl_allow_auth_to_host() functionality [90] + o http2: make nghttp2 less picky about field whitespace [27] + o HTTP3.md: update Caddy example [76] + o http: try parsing Retry-After: as a number first [122] + o http_proxy: restore the protocol pointer on error [104] + o httpput-postfields.c: shorten string for C89 compliance [57] + o ldap: delete stray CURL_HAS_MOZILLA_LDAP reference [79] + o lib1560: extended to verify detect/reject of unknown schemes + o lib517: fix C89 constant signedness [73] + o lib: add missing limits.h includes [35] + o lib: add required Win32 setup definitions in setup-win32.h [4] + o lib: prepare the incoming of additional protocols [71] + o lib: sanitize conditional exclusion around MIME [82] + o lib: set more flags in config-win32.h [109] + o lib: the number four in a sequence is the "fourth" [28] + o libssh: if sftp_init fails, don't get the sftp error code [132] + o Makefile.m32: deduplicate build rules [131] + o Makefile.m32: drop CROSSPREFIX and our CC/AR defaults [137] + o Makefile.m32: exclude libs & libpaths for shared mode exes [127] + o Makefile.m32: fix regression with tool_hugehelp [130] + o Makefile.m32: major rework [92] + o Makefile.m32: reintroduce CROSSPREFIX and -W -Wall [179] + o Makefile.m32: support more options [142] + o manpage-syntax.pl: all libcurl option symbols should be \fI-tagged [75] + o manpages: Fix spelling of "allows to" -> "allows one to" [171] + o misc: ISSPACE() => ISBLANK() [11] + o misc: use the term "null-terminate" consistently [41] + o mprintf: reject two kinds of precision for the same argument [162] + o mprintf: use snprintf if available [74] + o mqtt: return error for too long topic [133] + o mqtt: spell out CONNECT in comments [166] + o msh3: change the static_assert to make the code C89 + o netrc: compare user name case sensitively [118] + o netrc: replace fgets with Curl_get_line [174] + o netrc: use the URL-decoded user [103] + o ngtcp2: fix build errors due to changes in ngtcp2 library [107] + o ngtcp2: fix C89 compliance nit + o noproxy: support proxies specified using cidr notation [184] + o openssl: make certinfo available for QUIC [91] + o README.md: add GHA status badges for Linux and macOS builds [40] + o RELEASE-PROCEDURE.md: mention patch releases [21] + o resolve: make forced IPv4 resolve only use A queries [61] + o runtests: fix uninitialized value on ignored tests [128] + o schannel: ban server ALPN change during recv renegotiation [63] + o schannel: don't reset recv/send function pointers on renegotiation [156] + o schannel: when importing PFX, disable key persistence [141] + o scripts: use `grep -E` instead of `egrep` [30] + o setopt: use the handler table for protocol name to number conversions [45] + o setopt: when POST is set, reset the 'upload' field [51] + o setup-win32: no longer define UNICODE/_UNICODE implicitly [3] + o single_transfer: use the libcurl URL parser when appending query parts [100] + o smb: replace CURL_WIN32 with WIN32 [138] + o strcase: add and use Curl_timestrcmp [106] + o strerror: improve two URL API error messages + o symbol-scan.pl: also check for LIBCURL* symbols [43] + o symbol-scan.pl: scan and verify .3 man pages [42] + o symbols-in-versions: add missing LIBCURL* symbols + o symbols-in-versions: CURLOPT_ENCODING is deprecated since 7.21.6 + o test1119: scan all public headers [44] + o test1275: verify uppercase after period in markdown [135] + o test972: verify the output without using external tool [32] + o tests/certs/scripts: insert standard curl source headers [169] + o tests/Makefile: remove run time stats from ci-test [120] + o tests: avoid CreateThread if _beginthreadex is available [155] + o tests: fix tag syntax errors in test files + o tests: skip mime/form tests when mime is not built-in [54] + o tidy-up: delete parallel/unused feature flags [117] + o tidy-up: delete unused HAVE_STRUCT_POLLFD [134] + o TODO: provide the error body from a CONNECT response [67] + o tool: avoid generating ambiguous escaped characters in --libcurl [124] + o tool: remove dead code [70] + o tool: reorganize function c_escape around a dynbuf [121] + o tool_hugehelp: make hugehelp a blank macro when disabled [7] + o tool_main: exit at once if out of file descriptors [113] + o tool_operate: avoid a few #ifdefs for disabled-libcurl builds [29] + o tool_operate: more transfer cleanup after parallel transfer fail [165] + o tool_operate: prevent over-queuing in parallel mode [176] + o tool_operate: reduce errorbuffer allocs [173] + o tool_paramhelp: asserts verify maximum sizes for string loading [112] + o tool_paramhelp: make the max argument a 'double' [136] + o tool_progress: remove 'Qd' from the parallel progress bar [175] + o tool_setopt: use better English in --libcurl source comments [39] + o tool_xattr: save the original URL, not the final redirected one [181] + o unit test 1655: make it C89-compliant [59] + o url: a zero-length userinfo part in the URL is still a (blank) user [64] + o url: allow non-HTTPS HSTS-matching for debug builds [105] + o url: rename function due to name-clash in Watt-32 [62] + o url: use IDN decoded names for HSTS checks [140] + o urlapi: detect scheme better when not guessing [56] + o urlapi: fix parsing URL without slash with CURLU_URLENCODE [154] + o urlapi: leaner with fewer allocs [2] + o urlapi: reject more bad characters from the host name field [88] + o winbuild/MakefileBuild.vc: handle spaces in libssh(2) include paths [18] + o winbuild: use NMake batch-rules for compilation [47] + o windows: add .rc support to autotools builds [33] + o windows: adjust name of two internal public functions [58] + o windows: autotools .rc warnings fixup [68] + o wolfSSL: fix session management bug. [31] This release includes the following known bugs: @@ -45,49 +212,212 @@ This release includes the following known bugs: Planned upcoming removals include: - o gskit o NSS - o support for space-separated NOPROXY patterns - o support for the original legacy mingw version 1 + o Support for systems without 64 bit data types See https://curl.se/dev/deprecate.html for details This release would not have looked like this without help, code, reports and advice from friends like these: - Aleksander Mazur, Alois Klink, Andrei Rybak, Brad Harder, - Chilledheart on github, Christian Schmitz, Cloudogu Siebels, Daniel Stenberg, - Harry Sintonen, Jacob Hoffman-Andrews, Jan Macku, John Haugabook, Jon Rumsey, - Oliver Roberts, Paul Howarth, Stefan Eissing, VictorVG on github, - Viktor Szakats, Yair Lenga, ウさん - (20 contributors) + 12932 on github, a1346054 on github, Aftab Alam, ajak in #curl, + Andrew Lambert, Benjamin Loison, Brad Harder, bsergean on github, + Christopher Sauer, Dan Fandrich, Daniel Gustafsson, Daniel Hallberg, + Daniel Stenberg, David Hu, David McLaughlin, Dmitry Karpov, Dominik Klemba, + Don J Olmstead, Dustin Howett, Edoardo Lolletti, Eloy Degen, Emanuele Torre, + Emilio López, Gisle Vanem, Hayden Roche, Hiroki Kurosawa, James Fuller, + Jeremy Maitin-Shepard, Joel Depooter, John Bampton, Jonas Haag, + jurisuk on github, justchen1369 on github, Keitagit-kun on github, + Kelly Kaoudis, Marcel Raad, Marc Hörsken, Mark Itzcovitz, Martin Ågren, + Martin Strunz, Mathieu Carbonneaux, Matthias Gatto, Matt Holt, Max Dymond, + Michael Drake, Michael Heimpold, n0name321 on github, Orgad Shaneh, + Patrick Monnerat, Paul Seligman, Peter Goodman, Petr Štetiar, Philip H, + Philip H., Philip Heiduck, ProceduralMan on github, Randall S. Becker, + Ray Satiro, Rickard Hallerbäck, RobBotic1 on github, Robby Simpson, + Samuel Henrique, Sergey Bronnikov, ShadowZzj on github, Shaun Mirani, + ssdbest on github, Thiago Suchorski, Tobias Schaefer, Trail of Bits, + Vasiliy Ulyanov, Viktor Szakats, Xiang Xiao, Yuriy Chernyshov, + zhanghu on xiaomi + (74 contributors) References to bug reports and discussions on issues: - [1] = https://curl.se/bug/?i=11464 - [2] = https://curl.se/bug/?i=11477 - [3] = https://curl.se/bug/?i=11494 - [4] = https://curl.se/bug/?i=11508 - [5] = https://curl.se/bug/?i=11446 - [6] = https://curl.se/bug/?i=11486 - [7] = https://curl.se/bug/?i=11476 - [8] = https://curl.se/bug/?i=11478 - [9] = https://curl.se/bug/?i=11479 - [10] = https://curl.se/bug/?i=11483 - [11] = https://curl.se/bug/?i=11488 - [12] = https://curl.se/bug/?i=11491 - [13] = https://curl.se/bug/?i=11485 - [14] = https://curl.se/bug/?i=11470 - [15] = https://curl.se/bug/?i=11449 - [16] = https://curl.se/bug/?i=11465 - [17] = https://curl.se/bug/?i=11468 - [18] = https://curl.se/bug/?i=11462 - [19] = https://curl.se/bug/?i=11509 - [20] = https://curl.se/bug/?i=11504 - [21] = https://curl.se/bug/?i=11504 - [22] = https://curl.se/bug/?i=11501 - [23] = https://curl.se/bug/?i=11513 - [24] = https://curl.se/bug/?i=10865 - [25] = https://curl.se/bug/?i=11493 - [26] = https://curl.se/bug/?i=11514 - [27] = https://curl.se/bug/?i=11511 + [1] = https://curl.se/bug/?i=9357 + [2] = https://curl.se/bug/?i=9408 + [3] = https://curl.se/bug/?i=9375 + [4] = https://curl.se/bug/?i=9375 + [5] = https://curl.se/bug/?i=9498 + [6] = https://curl.se/bug/?i=9444 + [7] = https://curl.se/bug/?i=9485 + [8] = https://curl.se/bug/?i=9455 + [9] = https://curl.se/bug/?i=9434 + [10] = https://curl.se/bug/?i=9429 + [11] = https://curl.se/bug/?i=9432 + [12] = https://curl.se/bug/?i=9433 + [13] = https://curl.se/bug/?i=9471 + [14] = https://curl.se/bug/?i=9410 + [15] = https://curl.se/bug/?i=9427 + [16] = https://curl.se/bug/?i=9307 + [17] = https://curl.se/bug/?i=9485 + [18] = https://curl.se/mail/lib-2022-09/0038.html + [19] = https://curl.se/bug/?i=9604 + [20] = https://curl.se/bug/?i=9533 + [21] = https://curl.se/bug/?i=9495 + [22] = https://curl.se/bug/?i=9520 + [23] = https://curl.se/bug/?i=8995 + [24] = https://curl.se/bug/?i=9484 + [25] = https://curl.se/bug/?i=9424 + [26] = https://curl.se/bug/?i=9424 + [27] = https://curl.se/bug/?i=9448 + [28] = https://curl.se/bug/?i=9535 + [29] = https://curl.se/bug/?i=9486 + [30] = https://curl.se/bug/?i=9491 + [31] = https://curl.se/bug/?i=9492 + [32] = https://curl.se/bug/?i=9563 + [33] = https://curl.se/bug/?i=9521 + [34] = https://curl.se/bug/?i=9453 + [35] = https://curl.se/bug/?i=9453 + [36] = https://curl.se/bug/?i=9553 + [37] = https://curl.se/bug/?i=9453 + [38] = https://curl.se/bug/?i=9474 + [39] = https://curl.se/bug/?i=9475 + [40] = https://curl.se/bug/?i=9530 + [41] = https://curl.se/bug/?i=9527 + [42] = https://curl.se/bug/?i=9544 + [43] = https://curl.se/bug/?i=9544 + [44] = https://curl.se/bug/?i=9544 + [45] = https://curl.se/bug/?i=9472 + [46] = https://curl.se/bug/?i=9515 + [47] = https://curl.se/bug/?i=9512 + [48] = https://curl.se/bug/?i=9562 + [49] = https://curl.se/bug/?i=9519 + [50] = https://curl.se/bug/?i=9517 + [51] = https://curl.se/bug/?i=9507 + [52] = https://curl.se/bug/?i=9505 + [53] = https://curl.se/bug/?i=9560 + [54] = https://curl.se/bug/?i=9596 + [55] = https://curl.se/bug/?i=9549 + [56] = https://curl.se/bug/?i=9503 + [57] = https://curl.se/bug/?i=9555 + [58] = https://curl.se/bug/?i=9598 + [59] = https://curl.se/bug/?i=9551 + [60] = https://curl.se/bug/?i=9548 + [61] = https://curl.se/bug/?i=9540 + [62] = https://curl.se/bug/?i=9585 + [63] = https://curl.se/bug/?i=9463 + [64] = https://curl.se/bug/?i=9088 + [65] = https://curl.se/bug/?i=9586 + [66] = https://curl.se/bug/?i=9591 + [67] = https://curl.se/bug/?i=9513 + [68] = https://curl.se/bug/?i=9582 + [69] = https://curl.se/bug/?i=9575 + [70] = https://curl.se/bug/?i=9576 + [71] = https://curl.se/bug/?i=9534 + [72] = https://curl.se/bug/?i=9542 + [73] = https://curl.se/bug/?i=9572 + [74] = https://curl.se/bug/?i=9569 + [75] = https://curl.se/bug/?i=9574 + [76] = https://curl.se/bug/?i=9623 + [77] = https://curl.se/bug/?i=9624 + [78] = https://curl.se/bug/?i=9619 + [79] = https://curl.se/bug/?i=9625 + [80] = https://curl.se/bug/?i=9639 + [81] = https://curl.se/bug/?i=9565 + [82] = https://curl.se/bug/?i=9610 + [83] = https://curl.se/mail/archive-2022-02/0041.html + [84] = https://curl.se/bug/?i=9612 + [85] = https://curl.se/bug/?i=9614 + [86] = https://curl.se/bug/?i=9607 + [87] = https://curl.se/bug/?i=9592 + [88] = https://curl.se/bug/?i=9608 + [89] = https://curl.se/bug/?i=9605 + [90] = https://curl.se/bug/?i=9600 + [91] = https://curl.se/bug/?i=9584 + [92] = https://curl.se/bug/?i=9632 + [93] = https://curl.se/bug/?i=9694 + [94] = https://curl.se/bug/?i=9659 + [95] = https://curl.se/bug/?i=9630 + [96] = https://curl.se/bug/?i=9628 + [97] = https://curl.se/bug/?i=9684 + [98] = https://curl.se/bug/?i=9686 + [99] = https://curl.se/bug/?i=9683 + [100] = https://curl.se/bug/?i=9681 + [101] = https://curl.se/bug/?i=9729 + [102] = https://curl.se/bug/?i=9682 + [103] = https://curl.se/bug/?i=9709 + [104] = https://curl.se/bug/?i=9790 + [105] = https://curl.se/bug/?i=9728 + [106] = https://curl.se/bug/?i=9658 + [107] = https://curl.se/bug/?i=9747 + [108] = https://curl.se/bug/?i=9664 + [109] = https://curl.se/bug/?i=9712 + [110] = https://curl.se/bug/?i=9646 + [111] = https://curl.se/bug/?i=9776 + [112] = https://curl.se/bug/?i=9719 + [113] = https://curl.se/bug/?i=9663 + [115] = https://curl.se/bug/?i=9661 + [117] = https://curl.se/bug/?i=9652 + [118] = https://curl.se/bug/?i=9657 + [119] = https://curl.se/bug/?i=9654 + [120] = https://curl.se/bug/?i=9656 + [121] = https://curl.se/bug/?i=9653 + [122] = https://curl.se/bug/?i=9718 + [123] = https://curl.se/bug/?i=9711 + [124] = https://curl.se/bug/?i=9643 + [125] = https://curl.se/mail/lib-2022-10/0007.html + [126] = https://curl.se/bug/?i=9637 + [127] = https://curl.se/bug/?i=9651 + [128] = https://curl.se/bug/?i=9648 + [129] = https://curl.se/bug/?i=9633 + [130] = https://curl.se/bug/?i=9645 + [131] = https://curl.se/bug/?i=9642 + [132] = https://curl.se/bug/?i=9737 + [133] = https://curl.se/bug/?i=9744 + [134] = https://curl.se/bug/?i=9707 + [135] = https://curl.se/bug/?i=9697 + [136] = https://curl.se/bug/?i=9700 + [137] = https://curl.se/bug/?i=9698 + [138] = https://curl.se/bug/?i=9701 + [139] = https://curl.se/bug/?i=7966 + [140] = https://curl.se/bug/?i=9791 + [141] = https://curl.se/bug/?i=9300 + [142] = https://curl.se/bug/?i=9680 + [143] = https://curl.se/bug/?i=9687 + [144] = https://github.com/curl/curl-for-win/blob/73a070d96fd906fdee929e2f1f00a9149fb39239/curl-autotools.sh#L44-L47 + [145] = https://curl.se/bug/?i=9689 + [146] = https://curl.se/bug/?i=9726 + [147] = https://curl.se/bug/?i=9727 + [148] = https://curl.se/bug/?i=9725 + [149] = https://curl.se/bug/?i=9731 + [150] = https://curl.se/bug/?i=9771 + [151] = https://curl.se/bug/?i=9732 + [152] = https://curl.se/bug/?i=9770 + [154] = https://curl.se/bug/?i=9763 + [155] = https://curl.se/bug/?i=9705 + [156] = https://curl.se/bug/?i=9451 + [157] = https://curl.se/bug/?i=9760 + [158] = https://curl.se/bug/?i=9762 + [159] = https://curl.se/bug/?i=9761 + [160] = https://curl.se/bug/?i=9759 + [161] = https://curl.se/bug/?i=9757 + [162] = https://curl.se/bug/?i=9754 + [163] = https://curl.se/bug/?i=9753 + [164] = https://curl.se/bug/?i=9414 + [165] = https://curl.se/bug/?i=9749 + [166] = https://curl.se/bug/?i=9751 + [167] = https://curl.se/bug/?i=9752 + [169] = https://curl.se/bug/?i=9417 + [170] = https://curl.se/bug/?i=9415 + [171] = https://curl.se/bug/?i=9419 + [172] = https://curl.se/bug/?i=9418 + [173] = https://curl.se/bug/?i=9394 + [174] = https://curl.se/bug/?i=9789 + [175] = https://curl.se/bug/?i=9389 + [176] = https://curl.se/bug/?i=8933 + [177] = https://curl.se/bug/?i=9214 + [179] = https://curl.se/bug/?i=9784 + [181] = https://curl.se/bug/?i=9766 + [182] = https://curl.se/bug/?i=9779 + [184] = https://curl.se/bug/?i=9773 + [186] = https://curl.se/bug/?i=9638 + [187] = https://curl.se/bug/?i=9755 diff --git a/contrib/libs/curl/bin/ya.make b/contrib/libs/curl/bin/ya.make index 2716b063e9..a6c461a7f9 100644 --- a/contrib/libs/curl/bin/ya.make +++ b/contrib/libs/curl/bin/ya.make @@ -70,12 +70,12 @@ SRCS( src/tool_msgs.c src/tool_operate.c src/tool_operhlp.c + src/tool_panykey.c src/tool_paramhlp.c src/tool_parsecfg.c src/tool_progress.c src/tool_setopt.c src/tool_sleep.c - src/tool_stderr.c src/tool_strdup.c src/tool_urlglob.c src/tool_util.c diff --git a/contrib/libs/curl/include/README.md b/contrib/libs/curl/include/README.md index c96593263f..8fdbe0e0e9 100644 --- a/contrib/libs/curl/include/README.md +++ b/contrib/libs/curl/include/README.md @@ -1,5 +1,5 @@ <!-- -Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. +Copyright (C) 2000 - 2022 Daniel Stenberg, <daniel@haxx.se>, et al. SPDX-License-Identifier: curl --> diff --git a/contrib/libs/curl/include/curl/curl.h b/contrib/libs/curl/include/curl/curl.h index ea6fcb672e..e28dd0b5a0 100644 --- a/contrib/libs/curl/include/curl/curl.h +++ b/contrib/libs/curl/include/curl/curl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,23 +33,6 @@ #define CURL_STRICTER #endif -/* Compile-time deprecation macros. */ -#if defined(__GNUC__) && \ - ((__GNUC__ > 12) || ((__GNUC__ == 12) && (__GNUC_MINOR__ >= 1 ))) && \ - !defined(__INTEL_COMPILER) && \ - !defined(CURL_DISABLE_DEPRECATION) && !defined(BUILDING_LIBCURL) -#define CURL_DEPRECATED(version, message) \ - __attribute__((deprecated("since " # version ". " message))) -#define CURL_IGNORE_DEPRECATION(statements) \ - _Pragma("GCC diagnostic push") \ - _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") \ - statements \ - _Pragma("GCC diagnostic pop") -#else -#define CURL_DEPRECATED(version, message) -#define CURL_IGNORE_DEPRECATION(statements) statements -#endif - #include "curlver.h" /* libcurl version defines */ #include "system.h" /* determine things run-time */ @@ -93,7 +76,7 @@ defined(__CYGWIN__) || defined(AMIGA) || defined(__NuttX__) || \ (defined(__FreeBSD_version) && (__FreeBSD_version < 800000)) || \ (defined(__MidnightBSD_version) && (__MidnightBSD_version < 100000)) || \ - defined(__sun__) || defined(__serenity__) || defined(__vxworks__) + defined(__sun__) #include <sys/select.h> #endif @@ -162,21 +145,20 @@ typedef enum { CURLSSLBACKEND_NSS = 3, CURLSSLBACKEND_OBSOLETE4 = 4, /* Was QSOSSL. */ CURLSSLBACKEND_GSKIT = 5, - CURLSSLBACKEND_POLARSSL CURL_DEPRECATED(7.69.0, "") = 6, + CURLSSLBACKEND_POLARSSL = 6, CURLSSLBACKEND_WOLFSSL = 7, CURLSSLBACKEND_SCHANNEL = 8, CURLSSLBACKEND_SECURETRANSPORT = 9, - CURLSSLBACKEND_AXTLS CURL_DEPRECATED(7.61.0, "") = 10, + CURLSSLBACKEND_AXTLS = 10, /* never used since 7.63.0 */ CURLSSLBACKEND_MBEDTLS = 11, - CURLSSLBACKEND_MESALINK CURL_DEPRECATED(7.82.0, "") = 12, + CURLSSLBACKEND_MESALINK = 12, CURLSSLBACKEND_BEARSSL = 13, CURLSSLBACKEND_RUSTLS = 14 } curl_sslbackend; /* aliases for library clones and renames */ -#define CURLSSLBACKEND_AWSLC CURLSSLBACKEND_OPENSSL -#define CURLSSLBACKEND_BORINGSSL CURLSSLBACKEND_OPENSSL #define CURLSSLBACKEND_LIBRESSL CURLSSLBACKEND_OPENSSL +#define CURLSSLBACKEND_BORINGSSL CURLSSLBACKEND_OPENSSL /* deprecated names: */ #define CURLSSLBACKEND_CYASSL CURLSSLBACKEND_WOLFSSL @@ -250,7 +232,7 @@ typedef int (*curl_xferinfo_callback)(void *clientp, #ifndef CURL_MAX_READ_SIZE /* The maximum receive buffer size configurable via CURLOPT_BUFFERSIZE. */ -#define CURL_MAX_READ_SIZE (10*1024*1024) +#define CURL_MAX_READ_SIZE 524288 #endif #ifndef CURL_MAX_WRITE_SIZE @@ -274,10 +256,6 @@ typedef int (*curl_xferinfo_callback)(void *clientp, will signal libcurl to pause receiving on the current transfer. */ #define CURL_WRITEFUNC_PAUSE 0x10000001 -/* This is a magic return code for the write callback that, when returned, - will signal an error from the callback. */ -#define CURL_WRITEFUNC_ERROR 0xFFFFFFFF - typedef size_t (*curl_write_callback)(char *buffer, size_t size, size_t nitems, @@ -332,8 +310,7 @@ struct curl_fileinfo { unsigned int flags; - /* These are libcurl private struct fields. Previously used by libcurl, so - they must never be interfered with. */ + /* used internally */ char *b_data; size_t b_size; size_t b_used; @@ -391,7 +368,7 @@ typedef int (*curl_seek_callback)(void *instream, #define CURL_READFUNC_PAUSE 0x10000001 /* Return code for when the trailing headers' callback has terminated - without any errors */ + without any errors*/ #define CURL_TRAILERFUNC_OK 0 /* Return code for when was an error in the trailing header's list and we want to abort the request */ @@ -473,7 +450,7 @@ typedef void *(*curl_calloc_callback)(size_t nmemb, size_t size); #define CURL_DID_MEMORY_FUNC_TYPEDEFS #endif -/* the kind of data that is passed to information_callback */ +/* the kind of data that is passed to information_callback*/ typedef enum { CURLINFO_TEXT = 0, CURLINFO_HEADER_IN, /* 1 */ @@ -721,7 +698,7 @@ typedef enum { #define CURLOPT_WRITEINFO CURLOPT_OBSOLETE40 #define CURLOPT_CLOSEPOLICY CURLOPT_OBSOLETE72 -#endif /* !CURL_NO_OLDIES */ +#endif /*!CURL_NO_OLDIES*/ /* * Proxy error codes. Returned in CURLINFO_PROXY_ERROR if CURLE_PROXY was @@ -780,8 +757,7 @@ typedef enum { CONNECT HTTP/1.1 */ CURLPROXY_HTTP_1_0 = 1, /* added in 7.19.4, force to use CONNECT HTTP/1.0 */ - CURLPROXY_HTTPS = 2, /* HTTPS but stick to HTTP/1 added in 7.52.0 */ - CURLPROXY_HTTPS2 = 3, /* HTTPS and attempt HTTP/2 added in 8.2.0 */ + CURLPROXY_HTTPS = 2, /* added in 7.52.0 */ CURLPROXY_SOCKS4 = 4, /* support added in 7.15.2, enum existed already in 7.10 */ CURLPROXY_SOCKS5 = 5, /* added in 7.10 */ @@ -867,7 +843,7 @@ enum curl_khstat { CURLKHSTAT_DEFER, /* do not accept it, but we can't answer right now. Causes a CURLE_PEER_FAILED_VERIFICATION error but the connection will be left intact etc */ - CURLKHSTAT_FINE_REPLACE, /* accept and replace the wrong key */ + CURLKHSTAT_FINE_REPLACE, /* accept and replace the wrong key*/ CURLKHSTAT_LAST /* not for use, only a marker for last-in-list */ }; @@ -888,13 +864,13 @@ typedef int /* CURLOPT_SSH_KEYDATA */ typedef int - (*curl_sshhostkeycallback) (void *clientp,/* custom pointer passed */ + (*curl_sshhostkeycallback) (void *clientp,/* custom pointer passed*/ /* with CURLOPT_SSH_HOSTKEYDATA */ int keytype, /* CURLKHTYPE */ - const char *key, /* hostkey to check */ - size_t keylen); /* length of the key */ - /* return CURLE_OK to accept */ - /* or something else to refuse */ + const char *key, /*hostkey to check*/ + size_t keylen); /*length of the key*/ + /*return CURLE_OK to accept*/ + /*or something else to refuse*/ /* parameter for the CURLOPT_USE_SSL option */ @@ -956,7 +932,7 @@ typedef enum { #define CURLFTPSSL_ALL CURLUSESSL_ALL #define CURLFTPSSL_LAST CURLUSESSL_LAST #define curl_ftpssl curl_usessl -#endif /* !CURL_NO_OLDIES */ +#endif /*!CURL_NO_OLDIES*/ /* parameter for the CURLOPT_FTP_SSL_CCC option */ typedef enum { @@ -1082,7 +1058,6 @@ typedef CURLSTScode (*curl_hstswrite_callback)(CURL *easy, #define CURLOPT(na,t,nu) na = t + nu -#define CURLOPTDEPRECATED(na,t,nu,v,m) na CURL_DEPRECATED(v,m) = t + nu /* CURLOPT aliases that make no run-time difference */ @@ -1144,7 +1119,7 @@ typedef enum { /* Time-out the read operation after this amount of seconds */ CURLOPT(CURLOPT_TIMEOUT, CURLOPTTYPE_LONG, 13), - /* If CURLOPT_READDATA is used, this can be used to inform libcurl about + /* If the CURLOPT_INFILE is used, this can be used to inform libcurl about * how large the file being sent really is. That allows better error * checking and better verifies that the upload was successful. -1 means * unknown size. @@ -1196,8 +1171,7 @@ typedef enum { CURLOPT(CURLOPT_HTTPHEADER, CURLOPTTYPE_SLISTPOINT, 23), /* This points to a linked list of post entries, struct curl_httppost */ - CURLOPTDEPRECATED(CURLOPT_HTTPPOST, CURLOPTTYPE_OBJECTPOINT, 24, - 7.56.0, "Use CURLOPT_MIMEPOST"), + CURLOPT(CURLOPT_HTTPPOST, CURLOPTTYPE_OBJECTPOINT, 24), /* name of the file keeping your private SSL-certificate */ CURLOPT(CURLOPT_SSLCERT, CURLOPTTYPE_STRINGPOINT, 25), @@ -1287,8 +1261,7 @@ typedef enum { CURLOPT(CURLOPT_TRANSFERTEXT, CURLOPTTYPE_LONG, 53), /* HTTP PUT */ - CURLOPTDEPRECATED(CURLOPT_PUT, CURLOPTTYPE_LONG, 54, - 7.12.1, "Use CURLOPT_UPLOAD"), + CURLOPT(CURLOPT_PUT, CURLOPTTYPE_LONG, 54), /* 55 = OBSOLETE */ @@ -1296,8 +1269,7 @@ typedef enum { * Function that will be called instead of the internal progress display * function. This function should be defined as the curl_progress_callback * prototype defines. */ - CURLOPTDEPRECATED(CURLOPT_PROGRESSFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 56, - 7.32.0, "Use CURLOPT_XFERINFOFUNCTION"), + CURLOPT(CURLOPT_PROGRESSFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 56), /* Data passed to the CURLOPT_PROGRESSFUNCTION and CURLOPT_XFERINFOFUNCTION callbacks */ @@ -1314,7 +1286,7 @@ typedef enum { /* size of the POST input data, if strlen() is not good to use */ CURLOPT(CURLOPT_POSTFIELDSIZE, CURLOPTTYPE_LONG, 60), - /* tunnel non-http operations through an HTTP proxy */ + /* tunnel non-http operations through a HTTP proxy */ CURLOPT(CURLOPT_HTTPPROXYTUNNEL, CURLOPTTYPE_LONG, 61), /* Set the interface string to use as outgoing network interface */ @@ -1365,12 +1337,10 @@ typedef enum { /* Set to a file name that contains random data for libcurl to use to seed the random engine when doing SSL connects. */ - CURLOPTDEPRECATED(CURLOPT_RANDOM_FILE, CURLOPTTYPE_STRINGPOINT, 76, - 7.84.0, "Serves no purpose anymore"), + CURLOPT(CURLOPT_RANDOM_FILE, CURLOPTTYPE_STRINGPOINT, 76), /* Set to the Entropy Gathering Daemon socket pathname */ - CURLOPTDEPRECATED(CURLOPT_EGDSOCKET, CURLOPTTYPE_STRINGPOINT, 77, - 7.84.0, "Serves no purpose anymore"), + CURLOPT(CURLOPT_EGDSOCKET, CURLOPTTYPE_STRINGPOINT, 77), /* Time-out connect operations after this amount of seconds, if connects are OK within this time, then fine... This only aborts the connect phase. */ @@ -1425,8 +1395,7 @@ typedef enum { /* Non-zero value means to use the global dns cache */ /* DEPRECATED, do not use! */ - CURLOPTDEPRECATED(CURLOPT_DNS_USE_GLOBAL_CACHE, CURLOPTTYPE_LONG, 91, - 7.11.1, "Use CURLOPT_SHARE"), + CURLOPT(CURLOPT_DNS_USE_GLOBAL_CACHE, CURLOPTTYPE_LONG, 91), /* DNS cache timeout */ CURLOPT(CURLOPT_DNS_CACHE_TIMEOUT, CURLOPTTYPE_LONG, 92), @@ -1581,10 +1550,8 @@ typedef enum { */ CURLOPT(CURLOPT_FTPSSLAUTH, CURLOPTTYPE_VALUES, 129), - CURLOPTDEPRECATED(CURLOPT_IOCTLFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 130, - 7.18.0, "Use CURLOPT_SEEKFUNCTION"), - CURLOPTDEPRECATED(CURLOPT_IOCTLDATA, CURLOPTTYPE_CBPOINT, 131, - 7.18.0, "Use CURLOPT_SEEKDATA"), + CURLOPT(CURLOPT_IOCTLFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 130), + CURLOPT(CURLOPT_IOCTLDATA, CURLOPTTYPE_CBPOINT, 131), /* 132 OBSOLETE. Gone in 7.16.0 */ /* 133 OBSOLETE. Gone in 7.16.0 */ @@ -1623,22 +1590,16 @@ typedef enum { /* Function that will be called to convert from the network encoding (instead of using the iconv calls in libcurl) */ - CURLOPTDEPRECATED(CURLOPT_CONV_FROM_NETWORK_FUNCTION, - CURLOPTTYPE_FUNCTIONPOINT, 142, - 7.82.0, "Serves no purpose anymore"), + CURLOPT(CURLOPT_CONV_FROM_NETWORK_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 142), /* Function that will be called to convert to the network encoding (instead of using the iconv calls in libcurl) */ - CURLOPTDEPRECATED(CURLOPT_CONV_TO_NETWORK_FUNCTION, - CURLOPTTYPE_FUNCTIONPOINT, 143, - 7.82.0, "Serves no purpose anymore"), + CURLOPT(CURLOPT_CONV_TO_NETWORK_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 143), /* Function that will be called to convert from UTF8 (instead of using the iconv calls in libcurl) Note that this is used only for SSL certificate processing */ - CURLOPTDEPRECATED(CURLOPT_CONV_FROM_UTF8_FUNCTION, - CURLOPTTYPE_FUNCTIONPOINT, 144, - 7.82.0, "Serves no purpose anymore"), + CURLOPT(CURLOPT_CONV_FROM_UTF8_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 144), /* if the connection proceeds too quickly then need to slow it down */ /* limit-rate: maximum number of bytes per second to send or receive */ @@ -1739,9 +1700,7 @@ typedef enum { /* Socks Service */ /* DEPRECATED, do not use! */ - CURLOPTDEPRECATED(CURLOPT_SOCKS5_GSSAPI_SERVICE, - CURLOPTTYPE_STRINGPOINT, 179, - 7.49.0, "Use CURLOPT_PROXY_SERVICE_NAME"), + CURLOPT(CURLOPT_SOCKS5_GSSAPI_SERVICE, CURLOPTTYPE_STRINGPOINT, 179), /* Socks Service */ CURLOPT(CURLOPT_SOCKS5_GSSAPI_NEC, CURLOPTTYPE_LONG, 180), @@ -1750,14 +1709,12 @@ typedef enum { transfer, which thus helps the app which takes URLs from users or other external inputs and want to restrict what protocol(s) to deal with. Defaults to CURLPROTO_ALL. */ - CURLOPTDEPRECATED(CURLOPT_PROTOCOLS, CURLOPTTYPE_LONG, 181, - 7.85.0, "Use CURLOPT_PROTOCOLS_STR"), + CURLOPT(CURLOPT_PROTOCOLS, CURLOPTTYPE_LONG, 181), /* set the bitmask for the protocols that libcurl is allowed to follow to, as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs to be set in both bitmasks to be allowed to get redirected to. */ - CURLOPTDEPRECATED(CURLOPT_REDIR_PROTOCOLS, CURLOPTTYPE_LONG, 182, - 7.85.0, "Use CURLOPT_REDIR_PROTOCOLS_STR"), + CURLOPT(CURLOPT_REDIR_PROTOCOLS, CURLOPTTYPE_LONG, 182), /* set the SSH knownhost file name to use */ CURLOPT(CURLOPT_SSH_KNOWNHOSTS, CURLOPTTYPE_STRINGPOINT, 183), @@ -1902,13 +1859,12 @@ typedef enum { CURLOPT(CURLOPT_LOGIN_OPTIONS, CURLOPTTYPE_STRINGPOINT, 224), /* Enable/disable TLS NPN extension (http2 over ssl might fail without) */ - CURLOPTDEPRECATED(CURLOPT_SSL_ENABLE_NPN, CURLOPTTYPE_LONG, 225, - 7.86.0, "Has no function"), + CURLOPT(CURLOPT_SSL_ENABLE_NPN, CURLOPTTYPE_LONG, 225), /* Enable/disable TLS ALPN extension (http2 over ssl might fail without) */ CURLOPT(CURLOPT_SSL_ENABLE_ALPN, CURLOPTTYPE_LONG, 226), - /* Time to wait for a response to an HTTP request containing an + /* Time to wait for a response to a HTTP request containing an * Expect: 100-continue header before sending the data anyway. */ CURLOPT(CURLOPT_EXPECT_100_TIMEOUT_MS, CURLOPTTYPE_LONG, 227), @@ -2113,7 +2069,7 @@ typedef enum { CURLOPT(CURLOPT_SASL_AUTHZID, CURLOPTTYPE_STRINGPOINT, 289), /* allow RCPT TO command to fail for some recipients */ - CURLOPT(CURLOPT_MAIL_RCPT_ALLOWFAILS, CURLOPTTYPE_LONG, 290), + CURLOPT(CURLOPT_MAIL_RCPT_ALLLOWFAILS, CURLOPTTYPE_LONG, 290), /* the private SSL-certificate as a "blob" */ CURLOPT(CURLOPT_SSLCERT_BLOB, CURLOPTTYPE_BLOB, 291), @@ -2201,15 +2157,6 @@ typedef enum { /* websockets options */ CURLOPT(CURLOPT_WS_OPTIONS, CURLOPTTYPE_LONG, 320), - /* CA cache timeout */ - CURLOPT(CURLOPT_CA_CACHE_TIMEOUT, CURLOPTTYPE_LONG, 321), - - /* Can leak things, gonna exit() soon */ - CURLOPT(CURLOPT_QUICK_EXIT, CURLOPTTYPE_LONG, 322), - - /* set a specific client IP for HAProxy PROXY protocol header? */ - CURLOPT(CURLOPT_HAPROXY_CLIENT_IP, CURLOPTTYPE_STRINGPOINT, 323), - CURLOPT_LASTENTRY /* the last unused */ } CURLoption; @@ -2238,9 +2185,6 @@ typedef enum { /* */ #define CURLOPT_FTP_RESPONSE_TIMEOUT CURLOPT_SERVER_RESPONSE_TIMEOUT -/* Added in 8.2.0 */ -#define CURLOPT_MAIL_RCPT_ALLLOWFAILS CURLOPT_MAIL_RCPT_ALLOWFAILS - #else /* This is set if CURL_NO_OLDIES is defined at compile-time */ #undef CURLOPT_DNS_USE_GLOBAL_CACHE /* soon obsolete */ @@ -2269,13 +2213,8 @@ enum { CURL_HTTP_VERSION_2TLS, /* use version 2 for HTTPS, version 1.1 for HTTP */ CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE, /* please use HTTP 2 without HTTP/1.1 Upgrade */ - CURL_HTTP_VERSION_3 = 30, /* Use HTTP/3, fallback to HTTP/2 or HTTP/1 if - needed. For HTTPS only. For HTTP, this option - makes libcurl return error. */ - CURL_HTTP_VERSION_3ONLY = 31, /* Use HTTP/3 without fallback. For HTTPS - only. For HTTP, this makes libcurl - return error. */ - + CURL_HTTP_VERSION_3 = 30, /* Makes use of explicit HTTP/3 without fallback. + Use CURLOPT_ALTSVC to enable HTTP/3 upgrade */ CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */ }; @@ -2505,32 +2444,30 @@ CURL_EXTERN CURLcode curl_mime_headers(curl_mimepart *part, int take_ownership); typedef enum { - /********* the first one is unused ************/ - CURLFORM_NOTHING CURL_DEPRECATED(7.56.0, ""), - CURLFORM_COPYNAME CURL_DEPRECATED(7.56.0, "Use curl_mime_name()"), - CURLFORM_PTRNAME CURL_DEPRECATED(7.56.0, "Use curl_mime_name()"), - CURLFORM_NAMELENGTH CURL_DEPRECATED(7.56.0, ""), - CURLFORM_COPYCONTENTS CURL_DEPRECATED(7.56.0, "Use curl_mime_data()"), - CURLFORM_PTRCONTENTS CURL_DEPRECATED(7.56.0, "Use curl_mime_data()"), - CURLFORM_CONTENTSLENGTH CURL_DEPRECATED(7.56.0, "Use curl_mime_data()"), - CURLFORM_FILECONTENT CURL_DEPRECATED(7.56.0, "Use curl_mime_data_cb()"), - CURLFORM_ARRAY CURL_DEPRECATED(7.56.0, ""), + CURLFORM_NOTHING, /********* the first one is unused ************/ + CURLFORM_COPYNAME, + CURLFORM_PTRNAME, + CURLFORM_NAMELENGTH, + CURLFORM_COPYCONTENTS, + CURLFORM_PTRCONTENTS, + CURLFORM_CONTENTSLENGTH, + CURLFORM_FILECONTENT, + CURLFORM_ARRAY, CURLFORM_OBSOLETE, - CURLFORM_FILE CURL_DEPRECATED(7.56.0, "Use curl_mime_filedata()"), + CURLFORM_FILE, - CURLFORM_BUFFER CURL_DEPRECATED(7.56.0, "Use curl_mime_filename()"), - CURLFORM_BUFFERPTR CURL_DEPRECATED(7.56.0, "Use curl_mime_data()"), - CURLFORM_BUFFERLENGTH CURL_DEPRECATED(7.56.0, "Use curl_mime_data()"), + CURLFORM_BUFFER, + CURLFORM_BUFFERPTR, + CURLFORM_BUFFERLENGTH, - CURLFORM_CONTENTTYPE CURL_DEPRECATED(7.56.0, "Use curl_mime_type()"), - CURLFORM_CONTENTHEADER CURL_DEPRECATED(7.56.0, "Use curl_mime_headers()"), - CURLFORM_FILENAME CURL_DEPRECATED(7.56.0, "Use curl_mime_filename()"), + CURLFORM_CONTENTTYPE, + CURLFORM_CONTENTHEADER, + CURLFORM_FILENAME, CURLFORM_END, CURLFORM_OBSOLETE2, - CURLFORM_STREAM CURL_DEPRECATED(7.56.0, "Use curl_mime_data_cb()"), - CURLFORM_CONTENTLEN /* added in 7.46.0, provide a curl_off_t length */ - CURL_DEPRECATED(7.56.0, "Use curl_mime_data()"), + CURLFORM_STREAM, + CURLFORM_CONTENTLEN, /* added in 7.46.0, provide a curl_off_t length */ CURLFORM_LASTENTRY /* the last unused */ } CURLformoption; @@ -2558,16 +2495,15 @@ struct curl_forms { * ***************************************************************************/ typedef enum { - CURL_FORMADD_OK CURL_DEPRECATED(7.56.0, ""), /* 1st, no error */ + CURL_FORMADD_OK, /* first, no error */ - CURL_FORMADD_MEMORY CURL_DEPRECATED(7.56.0, ""), - CURL_FORMADD_OPTION_TWICE CURL_DEPRECATED(7.56.0, ""), - CURL_FORMADD_NULL CURL_DEPRECATED(7.56.0, ""), - CURL_FORMADD_UNKNOWN_OPTION CURL_DEPRECATED(7.56.0, ""), - CURL_FORMADD_INCOMPLETE CURL_DEPRECATED(7.56.0, ""), - CURL_FORMADD_ILLEGAL_ARRAY CURL_DEPRECATED(7.56.0, ""), - /* libcurl was built with form api disabled */ - CURL_FORMADD_DISABLED CURL_DEPRECATED(7.56.0, ""), + CURL_FORMADD_MEMORY, + CURL_FORMADD_OPTION_TWICE, + CURL_FORMADD_NULL, + CURL_FORMADD_UNKNOWN_OPTION, + CURL_FORMADD_INCOMPLETE, + CURL_FORMADD_ILLEGAL_ARRAY, + CURL_FORMADD_DISABLED, /* libcurl was built with this disabled */ CURL_FORMADD_LAST /* last */ } CURLFORMcode; @@ -2581,10 +2517,9 @@ typedef enum { * adds one part that together construct a full post. Then use * CURLOPT_HTTPPOST to send it off to libcurl. */ -CURL_EXTERN CURLFORMcode CURL_DEPRECATED(7.56.0, "Use curl_mime_init()") -curl_formadd(struct curl_httppost **httppost, - struct curl_httppost **last_post, - ...); +CURL_EXTERN CURLFORMcode curl_formadd(struct curl_httppost **httppost, + struct curl_httppost **last_post, + ...); /* * callback function for curl_formget() @@ -2607,9 +2542,8 @@ typedef size_t (*curl_formget_callback)(void *arg, const char *buf, * the curl_formget_callback function. * Returns 0 on success. */ -CURL_EXTERN int CURL_DEPRECATED(7.56.0, "") -curl_formget(struct curl_httppost *form, void *arg, - curl_formget_callback append); +CURL_EXTERN int curl_formget(struct curl_httppost *form, void *arg, + curl_formget_callback append); /* * NAME curl_formfree() * @@ -2617,8 +2551,7 @@ curl_formget(struct curl_httppost *form, void *arg, * * Free a multipart formpost previously built with curl_formadd(). */ -CURL_EXTERN void CURL_DEPRECATED(7.56.0, "Use curl_mime_free()") -curl_formfree(struct curl_httppost *form); +CURL_EXTERN void curl_formfree(struct curl_httppost *form); /* * NAME curl_getenv() @@ -2787,8 +2720,8 @@ CURL_EXTERN CURLsslset curl_global_sslset(curl_sslbackend id, const char *name, * Appends a string to a linked list. If no list exists, it will be created * first. Returns the new list, after appending. */ -CURL_EXTERN struct curl_slist *curl_slist_append(struct curl_slist *list, - const char *data); +CURL_EXTERN struct curl_slist *curl_slist_append(struct curl_slist *, + const char *); /* * NAME curl_slist_free_all() @@ -2797,7 +2730,7 @@ CURL_EXTERN struct curl_slist *curl_slist_append(struct curl_slist *list, * * free a previously built curl_slist. */ -CURL_EXTERN void curl_slist_free_all(struct curl_slist *list); +CURL_EXTERN void curl_slist_free_all(struct curl_slist *); /* * NAME curl_getdate() @@ -2845,35 +2778,22 @@ typedef enum { CURLINFO_NAMELOOKUP_TIME = CURLINFO_DOUBLE + 4, CURLINFO_CONNECT_TIME = CURLINFO_DOUBLE + 5, CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE + 6, - CURLINFO_SIZE_UPLOAD CURL_DEPRECATED(7.55.0, "Use CURLINFO_SIZE_UPLOAD_T") - = CURLINFO_DOUBLE + 7, + CURLINFO_SIZE_UPLOAD = CURLINFO_DOUBLE + 7, CURLINFO_SIZE_UPLOAD_T = CURLINFO_OFF_T + 7, - CURLINFO_SIZE_DOWNLOAD - CURL_DEPRECATED(7.55.0, "Use CURLINFO_SIZE_DOWNLOAD_T") - = CURLINFO_DOUBLE + 8, + CURLINFO_SIZE_DOWNLOAD = CURLINFO_DOUBLE + 8, CURLINFO_SIZE_DOWNLOAD_T = CURLINFO_OFF_T + 8, - CURLINFO_SPEED_DOWNLOAD - CURL_DEPRECATED(7.55.0, "Use CURLINFO_SPEED_DOWNLOAD_T") - = CURLINFO_DOUBLE + 9, + CURLINFO_SPEED_DOWNLOAD = CURLINFO_DOUBLE + 9, CURLINFO_SPEED_DOWNLOAD_T = CURLINFO_OFF_T + 9, - CURLINFO_SPEED_UPLOAD - CURL_DEPRECATED(7.55.0, "Use CURLINFO_SPEED_UPLOAD_T") - = CURLINFO_DOUBLE + 10, + CURLINFO_SPEED_UPLOAD = CURLINFO_DOUBLE + 10, CURLINFO_SPEED_UPLOAD_T = CURLINFO_OFF_T + 10, CURLINFO_HEADER_SIZE = CURLINFO_LONG + 11, CURLINFO_REQUEST_SIZE = CURLINFO_LONG + 12, CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG + 13, CURLINFO_FILETIME = CURLINFO_LONG + 14, CURLINFO_FILETIME_T = CURLINFO_OFF_T + 14, - CURLINFO_CONTENT_LENGTH_DOWNLOAD - CURL_DEPRECATED(7.55.0, - "Use CURLINFO_CONTENT_LENGTH_DOWNLOAD_T") - = CURLINFO_DOUBLE + 15, + CURLINFO_CONTENT_LENGTH_DOWNLOAD = CURLINFO_DOUBLE + 15, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T = CURLINFO_OFF_T + 15, - CURLINFO_CONTENT_LENGTH_UPLOAD - CURL_DEPRECATED(7.55.0, - "Use CURLINFO_CONTENT_LENGTH_UPLOAD_T") - = CURLINFO_DOUBLE + 16, + CURLINFO_CONTENT_LENGTH_UPLOAD = CURLINFO_DOUBLE + 16, CURLINFO_CONTENT_LENGTH_UPLOAD_T = CURLINFO_OFF_T + 16, CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE + 17, CURLINFO_CONTENT_TYPE = CURLINFO_STRING + 18, @@ -2887,8 +2807,7 @@ typedef enum { CURLINFO_NUM_CONNECTS = CURLINFO_LONG + 26, CURLINFO_SSL_ENGINES = CURLINFO_SLIST + 27, CURLINFO_COOKIELIST = CURLINFO_SLIST + 28, - CURLINFO_LASTSOCKET CURL_DEPRECATED(7.45.0, "Use CURLINFO_ACTIVESOCKET") - = CURLINFO_LONG + 29, + CURLINFO_LASTSOCKET = CURLINFO_LONG + 29, CURLINFO_FTP_ENTRY_PATH = CURLINFO_STRING + 30, CURLINFO_REDIRECT_URL = CURLINFO_STRING + 31, CURLINFO_PRIMARY_IP = CURLINFO_STRING + 32, @@ -2902,14 +2821,12 @@ typedef enum { CURLINFO_PRIMARY_PORT = CURLINFO_LONG + 40, CURLINFO_LOCAL_IP = CURLINFO_STRING + 41, CURLINFO_LOCAL_PORT = CURLINFO_LONG + 42, - CURLINFO_TLS_SESSION CURL_DEPRECATED(7.48.0, "Use CURLINFO_TLS_SSL_PTR") - = CURLINFO_PTR + 43, + CURLINFO_TLS_SESSION = CURLINFO_PTR + 43, CURLINFO_ACTIVESOCKET = CURLINFO_SOCKET + 44, CURLINFO_TLS_SSL_PTR = CURLINFO_PTR + 45, CURLINFO_HTTP_VERSION = CURLINFO_LONG + 46, CURLINFO_PROXY_SSL_VERIFYRESULT = CURLINFO_LONG + 47, - CURLINFO_PROTOCOL CURL_DEPRECATED(7.85.0, "Use CURLINFO_SCHEME") - = CURLINFO_LONG + 48, + CURLINFO_PROTOCOL = CURLINFO_LONG + 48, CURLINFO_SCHEME = CURLINFO_STRING + 49, CURLINFO_TOTAL_TIME_T = CURLINFO_OFF_T + 50, CURLINFO_NAMELOOKUP_TIME_T = CURLINFO_OFF_T + 51, @@ -2924,9 +2841,7 @@ typedef enum { CURLINFO_REFERER = CURLINFO_STRING + 60, CURLINFO_CAINFO = CURLINFO_STRING + 61, CURLINFO_CAPATH = CURLINFO_STRING + 62, - CURLINFO_XFER_ID = CURLINFO_OFF_T + 63, - CURLINFO_CONN_ID = CURLINFO_OFF_T + 64, - CURLINFO_LASTONE = 64 + CURLINFO_LASTONE = 62 } CURLINFO; /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as @@ -2970,7 +2885,6 @@ typedef enum { CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_DATA_CONNECT, CURL_LOCK_DATA_PSL, - CURL_LOCK_DATA_HSTS, CURL_LOCK_DATA_LAST } curl_lock_data; @@ -3013,9 +2927,8 @@ typedef enum { } CURLSHoption; CURL_EXTERN CURLSH *curl_share_init(void); -CURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *share, CURLSHoption option, - ...); -CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *share); +CURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *, CURLSHoption option, ...); +CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *); /**************************************************************************** * Structures for querying information about the curl library at runtime. @@ -3032,7 +2945,6 @@ typedef enum { CURLVERSION_EIGHTH, CURLVERSION_NINTH, CURLVERSION_TENTH, - CURLVERSION_ELEVENTH, CURLVERSION_LAST /* never actually use this */ } CURLversion; @@ -3041,7 +2953,7 @@ typedef enum { meant to be a built-in version number for what kind of struct the caller expects. If the struct ever changes, we redefine the NOW to another enum from above. */ -#define CURLVERSION_NOW CURLVERSION_ELEVENTH +#define CURLVERSION_NOW CURLVERSION_TENTH struct curl_version_info_data { CURLversion age; /* age of the returned struct */ @@ -3097,10 +3009,6 @@ struct curl_version_info_data { /* These fields were added in CURLVERSION_TENTH */ const char *gsasl_version; /* human readable string. */ - - /* These fields were added in CURLVERSION_ELEVENTH */ - /* feature_names is terminated by an entry with a NULL feature name */ - const char * const *feature_names; }; typedef struct curl_version_info_data curl_version_info_data; @@ -3194,7 +3102,7 @@ CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask); #define CURLPAUSE_CONT (CURLPAUSE_RECV_CONT|CURLPAUSE_SEND_CONT) #ifdef __cplusplus -} /* end of extern "C" */ +} #endif /* unfortunately, the easy.h and multi.h include files need options and info @@ -3221,6 +3129,6 @@ CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask); #define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param) #define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param) #endif /* __STDC__ >= 1 */ -#endif /* gcc >= 4.3 && !__cplusplus && !CURL_DISABLE_TYPECHECK */ +#endif /* gcc >= 4.3 && !__cplusplus */ #endif /* CURLINC_CURL_H */ diff --git a/contrib/libs/curl/include/curl/curlver.h b/contrib/libs/curl/include/curl/curlver.h index f0db63c156..2e7124e6ea 100644 --- a/contrib/libs/curl/include/curl/curlver.h +++ b/contrib/libs/curl/include/curl/curlver.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -28,17 +28,17 @@ a script at release-time. This was made its own header file in 7.11.2 */ /* This is the global package copyright */ -#define LIBCURL_COPYRIGHT "Daniel Stenberg, <daniel@haxx.se>." +#define LIBCURL_COPYRIGHT "1996 - 2022 Daniel Stenberg, <daniel@haxx.se>." /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "8.2.1" +#define LIBCURL_VERSION "7.86.0" /* The numeric version number is also available "in parts" by using these defines: */ -#define LIBCURL_VERSION_MAJOR 8 -#define LIBCURL_VERSION_MINOR 2 -#define LIBCURL_VERSION_PATCH 1 +#define LIBCURL_VERSION_MAJOR 7 +#define LIBCURL_VERSION_MINOR 86 +#define LIBCURL_VERSION_PATCH 0 /* This is the numeric version of the libcurl version number, meant for easier parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will @@ -59,7 +59,7 @@ CURL_VERSION_BITS() macro since curl's own configure script greps for it and needs it to contain the full number. */ -#define LIBCURL_VERSION_NUM 0x080201 +#define LIBCURL_VERSION_NUM 0x075600 /* * This is the date and time when the full source package was created. The @@ -70,7 +70,7 @@ * * "2007-11-23" */ -#define LIBCURL_TIMESTAMP "2023-07-26" +#define LIBCURL_TIMESTAMP "2022-10-26" #define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z)) #define CURL_AT_LEAST_VERSION(x,y,z) \ diff --git a/contrib/libs/curl/include/curl/easy.h b/contrib/libs/curl/include/curl/easy.h index 1285101c58..9c7e63adad 100644 --- a/contrib/libs/curl/include/curl/easy.h +++ b/contrib/libs/curl/include/curl/easy.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -48,13 +48,13 @@ CURL_EXTERN void curl_easy_cleanup(CURL *curl); * * DESCRIPTION * - * Request internal information from the curl session with this function. - * The third argument MUST be pointing to the specific type of the used option - * which is documented in each man page of the option. The data pointed to - * will be filled in accordingly and can be relied upon only if the function - * returns CURLE_OK. This function is intended to get used *AFTER* a performed - * transfer, all results from this function are undefined until the transfer - * is completed. + * Request internal information from the curl session with this function. The + * third argument MUST be a pointer to a long, a pointer to a char * or a + * pointer to a double (as the documentation describes elsewhere). The data + * pointed to will be filled in accordingly and can be relied upon only if the + * function returns CURLE_OK. This function is intended to get used *AFTER* a + * performed transfer, all results from this function are undefined until the + * transfer is completed. */ CURL_EXTERN CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...); @@ -119,7 +119,7 @@ CURL_EXTERN CURLcode curl_easy_send(CURL *curl, const void *buffer, CURL_EXTERN CURLcode curl_easy_upkeep(CURL *curl); #ifdef __cplusplus -} /* end of extern "C" */ +} #endif #endif diff --git a/contrib/libs/curl/include/curl/header.h b/contrib/libs/curl/include/curl/header.h index 8df11e1e42..1598c6f113 100644 --- a/contrib/libs/curl/include/curl/header.h +++ b/contrib/libs/curl/include/curl/header.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2018 - 2022, 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 diff --git a/contrib/libs/curl/include/curl/mprintf.h b/contrib/libs/curl/include/curl/mprintf.h index e652a6520e..cb948dcd97 100644 --- a/contrib/libs/curl/include/curl/mprintf.h +++ b/contrib/libs/curl/include/curl/mprintf.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -46,7 +46,7 @@ CURL_EXTERN char *curl_maprintf(const char *format, ...); CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args); #ifdef __cplusplus -} /* end of extern "C" */ +} #endif #endif /* CURLINC_MPRINTF_H */ diff --git a/contrib/libs/curl/include/curl/multi.h b/contrib/libs/curl/include/curl/multi.h index 30a3d93017..2f3ec37a76 100644 --- a/contrib/libs/curl/include/curl/multi.h +++ b/contrib/libs/curl/include/curl/multi.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -318,16 +318,16 @@ typedef int (*curl_multi_timer_callback)(CURLM *multi, /* multi handle */ void *userp); /* private callback pointer */ -CURL_EXTERN CURLMcode CURL_DEPRECATED(7.19.5, "Use curl_multi_socket_action()") -curl_multi_socket(CURLM *multi_handle, curl_socket_t s, int *running_handles); +CURL_EXTERN CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s, + int *running_handles); CURL_EXTERN CURLMcode curl_multi_socket_action(CURLM *multi_handle, curl_socket_t s, int ev_bitmask, int *running_handles); -CURL_EXTERN CURLMcode CURL_DEPRECATED(7.19.5, "Use curl_multi_socket_action()") -curl_multi_socket_all(CURLM *multi_handle, int *running_handles); +CURL_EXTERN CURLMcode curl_multi_socket_all(CURLM *multi_handle, + int *running_handles); #ifndef CURL_ALLOW_OLD_MULTI_SOCKET /* This macro below was added in 7.16.3 to push users who recompile to use diff --git a/contrib/libs/curl/include/curl/options.h b/contrib/libs/curl/include/curl/options.h index 1ed76a95c6..a792687cf4 100644 --- a/contrib/libs/curl/include/curl/options.h +++ b/contrib/libs/curl/include/curl/options.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2018 - 2022, 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 diff --git a/contrib/libs/curl/include/curl/stdcheaders.h b/contrib/libs/curl/include/curl/stdcheaders.h index 7451aa3052..82e1b5fef6 100644 --- a/contrib/libs/curl/include/curl/stdcheaders.h +++ b/contrib/libs/curl/include/curl/stdcheaders.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/include/curl/system.h b/contrib/libs/curl/include/curl/system.h index b2640c8bdd..8d56b8a4a1 100644 --- a/contrib/libs/curl/include/curl/system.h +++ b/contrib/libs/curl/include/curl/system.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -164,33 +164,13 @@ # endif # define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int -#elif defined(macintosh) -# include <ConditionalMacros.h> -# if TYPE_LONGLONG -# define CURL_TYPEOF_CURL_OFF_T long long -# define CURL_FORMAT_CURL_OFF_T "lld" -# define CURL_FORMAT_CURL_OFF_TU "llu" -# define CURL_SUFFIX_CURL_OFF_T LL -# define CURL_SUFFIX_CURL_OFF_TU ULL -# else -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL -# endif -# define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int - -#elif defined(__TANDEM) -# if ! defined(__LP64) - /* Required for 32-bit NonStop builds only. */ +#elif defined(__MWERKS__) # define CURL_TYPEOF_CURL_OFF_T long long # define CURL_FORMAT_CURL_OFF_T "lld" # define CURL_FORMAT_CURL_OFF_TU "llu" # define CURL_SUFFIX_CURL_OFF_T LL # define CURL_SUFFIX_CURL_OFF_TU ULL # define CURL_TYPEOF_CURL_SOCKLEN_T int -# endif #elif defined(_WIN32_WCE) # define CURL_TYPEOF_CURL_OFF_T __int64 @@ -227,38 +207,45 @@ # define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int #elif defined(__OS400__) -# define CURL_TYPEOF_CURL_OFF_T long long -# define CURL_FORMAT_CURL_OFF_T "lld" -# define CURL_FORMAT_CURL_OFF_TU "llu" -# define CURL_SUFFIX_CURL_OFF_T LL -# define CURL_SUFFIX_CURL_OFF_TU ULL -# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t -# define CURL_PULL_SYS_TYPES_H 1 -# define CURL_PULL_SYS_SOCKET_H 1 - -#elif defined(__MVS__) -# if defined(_LONG_LONG) +# if defined(__ILEC400__) # define CURL_TYPEOF_CURL_OFF_T long long # define CURL_FORMAT_CURL_OFF_T "lld" # define CURL_FORMAT_CURL_OFF_TU "llu" # define CURL_SUFFIX_CURL_OFF_T LL # define CURL_SUFFIX_CURL_OFF_TU ULL -# elif defined(_LP64) -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL -# else -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 +# endif + +#elif defined(__MVS__) +# if defined(__IBMC__) || defined(__IBMCPP__) +# if defined(_ILP32) +# elif defined(_LP64) +# endif +# if defined(_LONG_LONG) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(_LP64) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 # endif -# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t -# define CURL_PULL_SYS_TYPES_H 1 -# define CURL_PULL_SYS_SOCKET_H 1 #elif defined(__370__) # if defined(__IBMC__) || defined(__IBMCPP__) diff --git a/contrib/libs/curl/include/curl/typecheck-gcc.h b/contrib/libs/curl/include/curl/typecheck-gcc.h index b880f3dc60..2dabcb4166 100644 --- a/contrib/libs/curl/include/curl/typecheck-gcc.h +++ b/contrib/libs/curl/include/curl/typecheck-gcc.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -42,111 +42,107 @@ */ #define curl_easy_setopt(handle, option, value) \ __extension__({ \ - CURLoption _curl_opt = (option); \ + __typeof__(option) _curl_opt = option; \ if(__builtin_constant_p(_curl_opt)) { \ - CURL_IGNORE_DEPRECATION( \ - if(curlcheck_long_option(_curl_opt)) \ - if(!curlcheck_long(value)) \ - _curl_easy_setopt_err_long(); \ - if(curlcheck_off_t_option(_curl_opt)) \ - if(!curlcheck_off_t(value)) \ - _curl_easy_setopt_err_curl_off_t(); \ - if(curlcheck_string_option(_curl_opt)) \ - if(!curlcheck_string(value)) \ - _curl_easy_setopt_err_string(); \ - if(curlcheck_write_cb_option(_curl_opt)) \ - if(!curlcheck_write_cb(value)) \ - _curl_easy_setopt_err_write_callback(); \ - if((_curl_opt) == CURLOPT_RESOLVER_START_FUNCTION) \ - if(!curlcheck_resolver_start_callback(value)) \ - _curl_easy_setopt_err_resolver_start_callback(); \ - if((_curl_opt) == CURLOPT_READFUNCTION) \ - if(!curlcheck_read_cb(value)) \ - _curl_easy_setopt_err_read_cb(); \ - if((_curl_opt) == CURLOPT_IOCTLFUNCTION) \ - if(!curlcheck_ioctl_cb(value)) \ - _curl_easy_setopt_err_ioctl_cb(); \ - if((_curl_opt) == CURLOPT_SOCKOPTFUNCTION) \ - if(!curlcheck_sockopt_cb(value)) \ - _curl_easy_setopt_err_sockopt_cb(); \ - if((_curl_opt) == CURLOPT_OPENSOCKETFUNCTION) \ - if(!curlcheck_opensocket_cb(value)) \ - _curl_easy_setopt_err_opensocket_cb(); \ - if((_curl_opt) == CURLOPT_PROGRESSFUNCTION) \ - if(!curlcheck_progress_cb(value)) \ - _curl_easy_setopt_err_progress_cb(); \ - if((_curl_opt) == CURLOPT_DEBUGFUNCTION) \ - if(!curlcheck_debug_cb(value)) \ - _curl_easy_setopt_err_debug_cb(); \ - if((_curl_opt) == CURLOPT_SSL_CTX_FUNCTION) \ - if(!curlcheck_ssl_ctx_cb(value)) \ - _curl_easy_setopt_err_ssl_ctx_cb(); \ - if(curlcheck_conv_cb_option(_curl_opt)) \ - if(!curlcheck_conv_cb(value)) \ - _curl_easy_setopt_err_conv_cb(); \ - if((_curl_opt) == CURLOPT_SEEKFUNCTION) \ - if(!curlcheck_seek_cb(value)) \ - _curl_easy_setopt_err_seek_cb(); \ - if(curlcheck_cb_data_option(_curl_opt)) \ - if(!curlcheck_cb_data(value)) \ - _curl_easy_setopt_err_cb_data(); \ - if((_curl_opt) == CURLOPT_ERRORBUFFER) \ - if(!curlcheck_error_buffer(value)) \ - _curl_easy_setopt_err_error_buffer(); \ - if((_curl_opt) == CURLOPT_STDERR) \ - if(!curlcheck_FILE(value)) \ - _curl_easy_setopt_err_FILE(); \ - if(curlcheck_postfields_option(_curl_opt)) \ - if(!curlcheck_postfields(value)) \ - _curl_easy_setopt_err_postfields(); \ - if((_curl_opt) == CURLOPT_HTTPPOST) \ - if(!curlcheck_arr((value), struct curl_httppost)) \ - _curl_easy_setopt_err_curl_httpost(); \ - if((_curl_opt) == CURLOPT_MIMEPOST) \ - if(!curlcheck_ptr((value), curl_mime)) \ - _curl_easy_setopt_err_curl_mimepost(); \ - if(curlcheck_slist_option(_curl_opt)) \ - if(!curlcheck_arr((value), struct curl_slist)) \ - _curl_easy_setopt_err_curl_slist(); \ - if((_curl_opt) == CURLOPT_SHARE) \ - if(!curlcheck_ptr((value), CURLSH)) \ - _curl_easy_setopt_err_CURLSH(); \ - ) \ + if(curlcheck_long_option(_curl_opt)) \ + if(!curlcheck_long(value)) \ + _curl_easy_setopt_err_long(); \ + if(curlcheck_off_t_option(_curl_opt)) \ + if(!curlcheck_off_t(value)) \ + _curl_easy_setopt_err_curl_off_t(); \ + if(curlcheck_string_option(_curl_opt)) \ + if(!curlcheck_string(value)) \ + _curl_easy_setopt_err_string(); \ + if(curlcheck_write_cb_option(_curl_opt)) \ + if(!curlcheck_write_cb(value)) \ + _curl_easy_setopt_err_write_callback(); \ + if((_curl_opt) == CURLOPT_RESOLVER_START_FUNCTION) \ + if(!curlcheck_resolver_start_callback(value)) \ + _curl_easy_setopt_err_resolver_start_callback(); \ + if((_curl_opt) == CURLOPT_READFUNCTION) \ + if(!curlcheck_read_cb(value)) \ + _curl_easy_setopt_err_read_cb(); \ + if((_curl_opt) == CURLOPT_IOCTLFUNCTION) \ + if(!curlcheck_ioctl_cb(value)) \ + _curl_easy_setopt_err_ioctl_cb(); \ + if((_curl_opt) == CURLOPT_SOCKOPTFUNCTION) \ + if(!curlcheck_sockopt_cb(value)) \ + _curl_easy_setopt_err_sockopt_cb(); \ + if((_curl_opt) == CURLOPT_OPENSOCKETFUNCTION) \ + if(!curlcheck_opensocket_cb(value)) \ + _curl_easy_setopt_err_opensocket_cb(); \ + if((_curl_opt) == CURLOPT_PROGRESSFUNCTION) \ + if(!curlcheck_progress_cb(value)) \ + _curl_easy_setopt_err_progress_cb(); \ + if((_curl_opt) == CURLOPT_DEBUGFUNCTION) \ + if(!curlcheck_debug_cb(value)) \ + _curl_easy_setopt_err_debug_cb(); \ + if((_curl_opt) == CURLOPT_SSL_CTX_FUNCTION) \ + if(!curlcheck_ssl_ctx_cb(value)) \ + _curl_easy_setopt_err_ssl_ctx_cb(); \ + if(curlcheck_conv_cb_option(_curl_opt)) \ + if(!curlcheck_conv_cb(value)) \ + _curl_easy_setopt_err_conv_cb(); \ + if((_curl_opt) == CURLOPT_SEEKFUNCTION) \ + if(!curlcheck_seek_cb(value)) \ + _curl_easy_setopt_err_seek_cb(); \ + if(curlcheck_cb_data_option(_curl_opt)) \ + if(!curlcheck_cb_data(value)) \ + _curl_easy_setopt_err_cb_data(); \ + if((_curl_opt) == CURLOPT_ERRORBUFFER) \ + if(!curlcheck_error_buffer(value)) \ + _curl_easy_setopt_err_error_buffer(); \ + if((_curl_opt) == CURLOPT_STDERR) \ + if(!curlcheck_FILE(value)) \ + _curl_easy_setopt_err_FILE(); \ + if(curlcheck_postfields_option(_curl_opt)) \ + if(!curlcheck_postfields(value)) \ + _curl_easy_setopt_err_postfields(); \ + if((_curl_opt) == CURLOPT_HTTPPOST) \ + if(!curlcheck_arr((value), struct curl_httppost)) \ + _curl_easy_setopt_err_curl_httpost(); \ + if((_curl_opt) == CURLOPT_MIMEPOST) \ + if(!curlcheck_ptr((value), curl_mime)) \ + _curl_easy_setopt_err_curl_mimepost(); \ + if(curlcheck_slist_option(_curl_opt)) \ + if(!curlcheck_arr((value), struct curl_slist)) \ + _curl_easy_setopt_err_curl_slist(); \ + if((_curl_opt) == CURLOPT_SHARE) \ + if(!curlcheck_ptr((value), CURLSH)) \ + _curl_easy_setopt_err_CURLSH(); \ } \ curl_easy_setopt(handle, _curl_opt, value); \ }) /* wraps curl_easy_getinfo() with typechecking */ #define curl_easy_getinfo(handle, info, arg) \ - __extension__({ \ - CURLINFO _curl_info = (info); \ + __extension__({ \ + __typeof__(info) _curl_info = info; \ if(__builtin_constant_p(_curl_info)) { \ - CURL_IGNORE_DEPRECATION( \ - if(curlcheck_string_info(_curl_info)) \ - if(!curlcheck_arr((arg), char *)) \ - _curl_easy_getinfo_err_string(); \ - if(curlcheck_long_info(_curl_info)) \ - if(!curlcheck_arr((arg), long)) \ - _curl_easy_getinfo_err_long(); \ - if(curlcheck_double_info(_curl_info)) \ - if(!curlcheck_arr((arg), double)) \ - _curl_easy_getinfo_err_double(); \ - if(curlcheck_slist_info(_curl_info)) \ - if(!curlcheck_arr((arg), struct curl_slist *)) \ - _curl_easy_getinfo_err_curl_slist(); \ - if(curlcheck_tlssessioninfo_info(_curl_info)) \ - if(!curlcheck_arr((arg), struct curl_tlssessioninfo *)) \ - _curl_easy_getinfo_err_curl_tlssesssioninfo(); \ - if(curlcheck_certinfo_info(_curl_info)) \ - if(!curlcheck_arr((arg), struct curl_certinfo *)) \ - _curl_easy_getinfo_err_curl_certinfo(); \ - if(curlcheck_socket_info(_curl_info)) \ - if(!curlcheck_arr((arg), curl_socket_t)) \ - _curl_easy_getinfo_err_curl_socket(); \ - if(curlcheck_off_t_info(_curl_info)) \ - if(!curlcheck_arr((arg), curl_off_t)) \ - _curl_easy_getinfo_err_curl_off_t(); \ - ) \ + if(curlcheck_string_info(_curl_info)) \ + if(!curlcheck_arr((arg), char *)) \ + _curl_easy_getinfo_err_string(); \ + if(curlcheck_long_info(_curl_info)) \ + if(!curlcheck_arr((arg), long)) \ + _curl_easy_getinfo_err_long(); \ + if(curlcheck_double_info(_curl_info)) \ + if(!curlcheck_arr((arg), double)) \ + _curl_easy_getinfo_err_double(); \ + if(curlcheck_slist_info(_curl_info)) \ + if(!curlcheck_arr((arg), struct curl_slist *)) \ + _curl_easy_getinfo_err_curl_slist(); \ + if(curlcheck_tlssessioninfo_info(_curl_info)) \ + if(!curlcheck_arr((arg), struct curl_tlssessioninfo *)) \ + _curl_easy_getinfo_err_curl_tlssesssioninfo(); \ + if(curlcheck_certinfo_info(_curl_info)) \ + if(!curlcheck_arr((arg), struct curl_certinfo *)) \ + _curl_easy_getinfo_err_curl_certinfo(); \ + if(curlcheck_socket_info(_curl_info)) \ + if(!curlcheck_arr((arg), curl_socket_t)) \ + _curl_easy_getinfo_err_curl_socket(); \ + if(curlcheck_off_t_info(_curl_info)) \ + if(!curlcheck_arr((arg), curl_off_t)) \ + _curl_easy_getinfo_err_curl_off_t(); \ } \ curl_easy_getinfo(handle, _curl_info, arg); \ }) @@ -280,7 +276,6 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t, (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER || \ (option) == CURLOPT_FTPPORT || \ (option) == CURLOPT_HSTS || \ - (option) == CURLOPT_HAPROXY_CLIENT_IP || \ (option) == CURLOPT_INTERFACE || \ (option) == CURLOPT_ISSUERCERT || \ (option) == CURLOPT_KEYPASSWD || \ @@ -441,7 +436,7 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t, (CURLINFO_OFF_T < (info)) -/* typecheck helpers -- check whether given expression has requested type */ +/* typecheck helpers -- check whether given expression has requested type*/ /* For pointers, you can use the curlcheck_ptr/curlcheck_arr macros, * otherwise define a new macro. Search for __builtin_types_compatible_p diff --git a/contrib/libs/curl/include/curl/urlapi.h b/contrib/libs/curl/include/curl/urlapi.h index b3504b683a..e15c213ccd 100644 --- a/contrib/libs/curl/include/curl/urlapi.h +++ b/contrib/libs/curl/include/curl/urlapi.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2018 - 2022, 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 @@ -62,7 +62,6 @@ typedef enum { CURLUE_BAD_SCHEME, /* 27 */ CURLUE_BAD_SLASHES, /* 28 */ CURLUE_BAD_USER, /* 29 */ - CURLUE_LACKS_IDN, /* 30 */ CURLUE_LAST } CURLUcode; @@ -96,7 +95,6 @@ typedef enum { #define CURLU_NO_AUTHORITY (1<<10) /* Allow empty authority when the scheme is unknown. */ #define CURLU_ALLOW_SPACE (1<<11) /* Allow spaces in the URL */ -#define CURLU_PUNYCODE (1<<12) /* get the host name in pynycode */ typedef struct Curl_URL CURLU; @@ -117,14 +115,14 @@ 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(const CURLU *in); +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(const CURLU *handle, CURLUPart what, +CURL_EXTERN CURLUcode curl_url_get(CURLU *handle, CURLUPart what, char **part, unsigned int flags); /* diff --git a/contrib/libs/curl/include/curl/websockets.h b/contrib/libs/curl/include/curl/websockets.h index 6ef6a2bc92..4d57f91e56 100644 --- a/contrib/libs/curl/include/curl/websockets.h +++ b/contrib/libs/curl/include/curl/websockets.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,6 @@ struct curl_ws_frame { int flags; /* See the CURLWS_* defines */ curl_off_t offset; /* the offset of this data into the frame */ curl_off_t bytesleft; /* number of pending bytes left of the payload */ - size_t len; /* size of the current data chunk */ }; /* flag bits */ @@ -54,13 +53,13 @@ struct curl_ws_frame { */ CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, size_t *recv, - const struct curl_ws_frame **metap); + struct curl_ws_frame **metap); -/* flags for curl_ws_send() */ +/* sendflags for curl_ws_send() */ #define CURLWS_PONG (1<<6) /* - * NAME curl_ws_send() + * NAME curl_easy_send() * * DESCRIPTION * @@ -69,13 +68,13 @@ CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, */ CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer, size_t buflen, size_t *sent, - curl_off_t fragsize, - unsigned int flags); + curl_off_t framesize, + unsigned int sendflags); /* bits for the CURLOPT_WS_OPTIONS bitmask: */ #define CURLWS_RAW_MODE (1<<0) -CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(CURL *curl); +CURL_EXTERN struct curl_ws_frame *curl_ws_meta(CURL *curl); #ifdef __cplusplus } diff --git a/contrib/libs/curl/lib/altsvc.c b/contrib/libs/curl/lib/altsvc.c index 11009d5ac8..7bca840151 100644 --- a/contrib/libs/curl/lib/altsvc.c +++ b/contrib/libs/curl/lib/altsvc.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2019 - 2022, 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 @@ -117,7 +117,7 @@ static struct altsvc *altsvc_createid(const char *srchost, as->dst.port = curlx_ultous(dstport); return as; -error: + error: altsvc_free(as); return NULL; } @@ -217,7 +217,7 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file) } return result; -fail: + fail: Curl_safefree(asi->filename); free(line); fclose(fp); @@ -424,7 +424,7 @@ static void altsvc_flush(struct altsvcinfo *asi, enum alpnid srcalpnid, #ifdef DEBUGBUILD /* to play well with debug builds, we can *set* a fixed time this will return */ -static time_t altsvc_debugtime(void *unused) +static time_t debugtime(void *unused) { char *timestr = getenv("CURL_TIME"); (void)unused; @@ -434,8 +434,7 @@ static time_t altsvc_debugtime(void *unused) } return time(NULL); } -#undef time -#define time(x) altsvc_debugtime(x) +#define time(x) debugtime(x) #endif #define ISNEWLINE(x) (((x) == '\n') || (x) == '\r') @@ -518,21 +517,15 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, dsthost = srchost; } if(*p == ':') { - unsigned long port = 0; - p++; - if(ISDIGIT(*p)) - /* a port number */ - port = strtoul(p, &end_ptr, 10); - else - end_ptr = (char *)p; /* not left uninitialized */ - if(!port || port > USHRT_MAX || end_ptr == p || *end_ptr != '\"') { + /* a port number */ + unsigned long port = strtoul(++p, &end_ptr, 10); + if(port > USHRT_MAX || end_ptr == p || *end_ptr != '\"') { infof(data, "Unknown alt-svc port number, ignoring."); valid = FALSE; } - else { + else dstport = curlx_ultous(port); - p = end_ptr; - } + p = end_ptr; } if(*p++ != '\"') break; diff --git a/contrib/libs/curl/lib/altsvc.h b/contrib/libs/curl/lib/altsvc.h index 7fea1434a5..2751d272a1 100644 --- a/contrib/libs/curl/lib/altsvc.h +++ b/contrib/libs/curl/lib/altsvc.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2019 - 2022, 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 diff --git a/contrib/libs/curl/lib/amigaos.c b/contrib/libs/curl/lib/amigaos.c index 4c648d9bcd..42b0c39b37 100644 --- a/contrib/libs/curl/lib/amigaos.c +++ b/contrib/libs/curl/lib/amigaos.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -178,7 +178,6 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, #endif /* CURLRES_AMIGA */ #ifdef USE_AMISSL -#include <signal.h> int Curl_amiga_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout) { diff --git a/contrib/libs/curl/lib/amigaos.h b/contrib/libs/curl/lib/amigaos.h index 7997ede80e..9abfb59eac 100644 --- a/contrib/libs/curl/lib/amigaos.h +++ b/contrib/libs/curl/lib/amigaos.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/arpa_telnet.h b/contrib/libs/curl/lib/arpa_telnet.h index de1373800c..523f7f51e7 100644 --- a/contrib/libs/curl/lib/arpa_telnet.h +++ b/contrib/libs/curl/lib/arpa_telnet.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/asyn-ares.c b/contrib/libs/curl/lib/asyn-ares.c index 19fe8536b0..33edba1395 100644 --- a/contrib/libs/curl/lib/asyn-ares.c +++ b/contrib/libs/curl/lib/asyn-ares.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -47,6 +47,15 @@ #include <inet.h> #endif +#ifdef HAVE_PROCESS_H +#include <process.h> +#endif + +#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) +#undef in_addr_t +#define in_addr_t unsigned long +#endif + #include "urldata.h" #include "sendf.h" #include "hostip.h" diff --git a/contrib/libs/curl/lib/asyn-thread.c b/contrib/libs/curl/lib/asyn-thread.c index 3e10462379..0e4ebdc8b5 100644 --- a/contrib/libs/curl/lib/asyn-thread.c +++ b/contrib/libs/curl/lib/asyn-thread.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -44,8 +44,19 @@ #include <inet.h> #endif -#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) -# include <pthread.h> +#if defined(USE_THREADS_POSIX) +# ifdef HAVE_PTHREAD_H +# include <pthread.h> +# endif +#elif defined(USE_THREADS_WIN32) +# ifdef HAVE_PROCESS_H +# include <process.h> +# endif +#endif + +#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) +#undef in_addr_t +#define in_addr_t unsigned long #endif #ifdef HAVE_GETADDRINFO @@ -64,6 +75,7 @@ #include "inet_ntop.h" #include "curl_threads.h" #include "connect.h" +#include "socketpair.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" @@ -308,7 +320,7 @@ int init_thread_sync_data(struct thread_data *td, return 1; -err_exit: + err_exit: #ifndef CURL_DISABLE_SOCKETPAIR if(tsd->sock_pair[0] != CURL_SOCKET_BAD) { sclose(tsd->sock_pair[0]); @@ -573,10 +585,10 @@ static bool init_resolve_thread(struct Curl_easy *data, return TRUE; -err_exit: + err_exit: destroy_async_data(asp); -errno_exit: + errno_exit: errno = err; return FALSE; } @@ -633,8 +645,7 @@ void Curl_resolver_kill(struct Curl_easy *data) /* If we're still resolving, we must wait for the threads to fully clean up, unfortunately. Otherwise, we can simply cancel to clean up any resolver data. */ - if(td && td->thread_hnd != curl_thread_t_null - && (data->set.quick_exit != 1L)) + if(td && td->thread_hnd != curl_thread_t_null) (void)thread_wait_resolv(data, NULL, FALSE); else Curl_resolver_cancel(data); diff --git a/contrib/libs/curl/lib/asyn.h b/contrib/libs/curl/lib/asyn.h index 7e207c4f56..1aab21aaa7 100644 --- a/contrib/libs/curl/lib/asyn.h +++ b/contrib/libs/curl/lib/asyn.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/base64.c b/contrib/libs/curl/lib/base64.c index 9d495d4612..52654c2bc3 100644 --- a/contrib/libs/curl/lib/base64.c +++ b/contrib/libs/curl/lib/base64.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,13 +37,14 @@ #include "warnless.h" #include "curl_base64.h" -/* The last 2 #include files should be in this order */ +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" /* ---- Base64 Encoding/Decoding Table --- */ /* Padding character string starts at offset 64. */ -static const char base64encdec[]= +static const char base64[]= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; /* The Base 64 encoding with a URL and filename safe alphabet, RFC 4648 @@ -51,12 +52,39 @@ static const char base64encdec[]= static const char base64url[]= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; -static const unsigned char decodetable[] = -{ 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, - 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, - 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51 }; +static size_t decodeQuantum(unsigned char *dest, const char *src) +{ + size_t padding = 0; + const char *s; + unsigned long i, x = 0; + + for(i = 0, s = src; i < 4; i++, s++) { + if(*s == '=') { + x <<= 6; + padding++; + } + else { + const char *p = strchr(base64, *s); + if(p) + x = (x << 6) + curlx_uztoul(p - base64); + else + return 0; + } + } + + if(padding < 1) + dest[2] = curlx_ultouc(x & 0xFFUL); + + x >>= 8; + if(padding < 2) + dest[1] = curlx_ultouc(x & 0xFFUL); + + x >>= 8; + dest[0] = curlx_ultouc(x & 0xFFUL); + + return 3 - padding; +} + /* * Curl_base64_decode() * @@ -78,11 +106,10 @@ CURLcode Curl_base64_decode(const char *src, size_t padding = 0; size_t i; size_t numQuantums; - size_t fullQuantums; size_t rawlen = 0; + const char *padptr; unsigned char *pos; unsigned char *newstr; - unsigned char lookup[256]; *outptr = NULL; *outlen = 0; @@ -92,18 +119,21 @@ CURLcode Curl_base64_decode(const char *src, if(!srclen || srclen % 4) return CURLE_BAD_CONTENT_ENCODING; - /* srclen is at least 4 here */ - while(src[srclen - 1 - padding] == '=') { - /* count padding characters */ + /* Find the position of any = padding characters */ + padptr = strchr(src, '='); + if(padptr) { padding++; /* A maximum of two = padding characters is allowed */ - if(padding > 2) + if(padptr[1] == '=') + padding++; + + /* Check the = padding characters weren't part way through the input */ + if(padptr + padding != src + srclen) return CURLE_BAD_CONTENT_ENCODING; } /* Calculate the number of quantums */ numQuantums = srclen / 4; - fullQuantums = numQuantums - (padding ? 1 : 0); /* Calculate the size of the decoded string */ rawlen = (numQuantums * 3) - padding; @@ -115,59 +145,17 @@ CURLcode Curl_base64_decode(const char *src, pos = newstr; - memset(lookup, 0xff, sizeof(lookup)); - memcpy(&lookup['+'], decodetable, sizeof(decodetable)); - /* replaces - { - unsigned char c; - const unsigned char *p = (const unsigned char *)base64encdec; - for(c = 0; *p; c++, p++) - lookup[*p] = c; - } - */ - - /* Decode the complete quantums first */ - for(i = 0; i < fullQuantums; i++) { - unsigned char val; - unsigned int x = 0; - int j; - - for(j = 0; j < 4; j++) { - val = lookup[(unsigned char)*src++]; - if(val == 0xff) /* bad symbol */ - goto bad; - x = (x << 6) | val; - } - pos[2] = x & 0xff; - pos[1] = (x >> 8) & 0xff; - pos[0] = (x >> 16) & 0xff; - pos += 3; - } - if(padding) { - /* this means either 8 or 16 bits output */ - unsigned char val; - unsigned int x = 0; - int j; - size_t padc = 0; - for(j = 0; j < 4; j++) { - if(*src == '=') { - x <<= 6; - src++; - if(++padc > padding) - /* this is a badly placed '=' symbol! */ - goto bad; - } - else { - val = lookup[(unsigned char)*src++]; - if(val == 0xff) /* bad symbol */ - goto bad; - x = (x << 6) | val; - } + /* Decode the quantums */ + for(i = 0; i < numQuantums; i++) { + size_t result = decodeQuantum(pos, src); + if(!result) { + free(newstr); + + return CURLE_BAD_CONTENT_ENCODING; } - if(padding == 1) - pos[1] = (x >> 8) & 0xff; - pos[0] = (x >> 16) & 0xff; - pos += 3 - padding; + + pos += result; + src += 4; } /* Zero terminate */ @@ -178,60 +166,81 @@ CURLcode Curl_base64_decode(const char *src, *outlen = rawlen; return CURLE_OK; -bad: - free(newstr); - return CURLE_BAD_CONTENT_ENCODING; } static CURLcode base64_encode(const char *table64, const char *inputbuff, size_t insize, char **outptr, size_t *outlen) { + unsigned char ibuf[3]; + unsigned char obuf[4]; + int i; + int inputparts; char *output; char *base64data; - const unsigned char *in = (unsigned char *)inputbuff; + const char *indata = inputbuff; const char *padstr = &table64[64]; /* Point to padding string. */ *outptr = NULL; *outlen = 0; if(!insize) - insize = strlen(inputbuff); + insize = strlen(indata); #if SIZEOF_SIZE_T == 4 if(insize > UINT_MAX/4) return CURLE_OUT_OF_MEMORY; #endif - base64data = output = malloc((insize + 2) / 3 * 4 + 1); + base64data = output = malloc(insize * 4 / 3 + 4); if(!output) return CURLE_OUT_OF_MEMORY; - while(insize >= 3) { - *output++ = table64[ in[0] >> 2 ]; - *output++ = table64[ ((in[0] & 0x03) << 4) | (in[1] >> 4) ]; - *output++ = table64[ ((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6) ]; - *output++ = table64[ in[2] & 0x3F ]; - insize -= 3; - in += 3; - } - if(insize) { - /* this is only one or two bytes now */ - *output++ = table64[ in[0] >> 2 ]; - if(insize == 1) { - *output++ = table64[ ((in[0] & 0x03) << 4) ]; - if(*padstr) { - *output++ = *padstr; - *output++ = *padstr; + while(insize > 0) { + for(i = inputparts = 0; i < 3; i++) { + if(insize > 0) { + inputparts++; + ibuf[i] = (unsigned char) *indata; + indata++; + insize--; } + else + ibuf[i] = 0; } - else { - /* insize == 2 */ - *output++ = table64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4) ]; - *output++ = table64[ ((in[1] & 0x0F) << 2) ]; - if(*padstr) - *output++ = *padstr; + + obuf[0] = (unsigned char) ((ibuf[0] & 0xFC) >> 2); + obuf[1] = (unsigned char) (((ibuf[0] & 0x03) << 4) | \ + ((ibuf[1] & 0xF0) >> 4)); + obuf[2] = (unsigned char) (((ibuf[1] & 0x0F) << 2) | \ + ((ibuf[2] & 0xC0) >> 6)); + obuf[3] = (unsigned char) (ibuf[2] & 0x3F); + + switch(inputparts) { + case 1: /* only one byte read */ + i = msnprintf(output, 5, "%c%c%s%s", + table64[obuf[0]], + table64[obuf[1]], + padstr, + padstr); + break; + + case 2: /* two bytes read */ + i = msnprintf(output, 5, "%c%c%c%s", + table64[obuf[0]], + table64[obuf[1]], + table64[obuf[2]], + padstr); + break; + + default: + i = msnprintf(output, 5, "%c%c%c%c", + table64[obuf[0]], + table64[obuf[1]], + table64[obuf[2]], + table64[obuf[3]]); + break; } + output += i; } /* Zero terminate */ @@ -264,7 +273,7 @@ static CURLcode base64_encode(const char *table64, CURLcode Curl_base64_encode(const char *inputbuff, size_t insize, char **outptr, size_t *outlen) { - return base64_encode(base64encdec, inputbuff, insize, outptr, outlen); + return base64_encode(base64, inputbuff, insize, outptr, outlen); } /* diff --git a/contrib/libs/curl/lib/bufq.c b/contrib/libs/curl/lib/bufq.c deleted file mode 100644 index 155544993d..0000000000 --- a/contrib/libs/curl/lib/bufq.c +++ /dev/null @@ -1,678 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" -#include "bufq.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -static bool chunk_is_empty(const struct buf_chunk *chunk) -{ - return chunk->r_offset >= chunk->w_offset; -} - -static bool chunk_is_full(const struct buf_chunk *chunk) -{ - return chunk->w_offset >= chunk->dlen; -} - -static size_t chunk_len(const struct buf_chunk *chunk) -{ - return chunk->w_offset - chunk->r_offset; -} - -static size_t chunk_space(const struct buf_chunk *chunk) -{ - return chunk->dlen - chunk->w_offset; -} - -static void chunk_reset(struct buf_chunk *chunk) -{ - chunk->next = NULL; - chunk->r_offset = chunk->w_offset = 0; -} - -static size_t chunk_append(struct buf_chunk *chunk, - const unsigned char *buf, size_t len) -{ - unsigned char *p = &chunk->x.data[chunk->w_offset]; - size_t n = chunk->dlen - chunk->w_offset; - DEBUGASSERT(chunk->dlen >= chunk->w_offset); - if(n) { - n = CURLMIN(n, len); - memcpy(p, buf, n); - chunk->w_offset += n; - } - return n; -} - -static size_t chunk_read(struct buf_chunk *chunk, - unsigned char *buf, size_t len) -{ - unsigned char *p = &chunk->x.data[chunk->r_offset]; - size_t n = chunk->w_offset - chunk->r_offset; - DEBUGASSERT(chunk->w_offset >= chunk->r_offset); - if(!n) { - return 0; - } - else if(n <= len) { - memcpy(buf, p, n); - chunk->r_offset = chunk->w_offset = 0; - return n; - } - else { - memcpy(buf, p, len); - chunk->r_offset += len; - return len; - } -} - -static ssize_t chunk_slurpn(struct buf_chunk *chunk, size_t max_len, - Curl_bufq_reader *reader, - void *reader_ctx, CURLcode *err) -{ - unsigned char *p = &chunk->x.data[chunk->w_offset]; - size_t n = chunk->dlen - chunk->w_offset; /* free amount */ - ssize_t nread; - - DEBUGASSERT(chunk->dlen >= chunk->w_offset); - if(!n) { - *err = CURLE_AGAIN; - return -1; - } - if(max_len && n > max_len) - n = max_len; - nread = reader(reader_ctx, p, n, err); - if(nread > 0) { - DEBUGASSERT((size_t)nread <= n); - chunk->w_offset += nread; - } - return nread; -} - -static void chunk_peek(const struct buf_chunk *chunk, - const unsigned char **pbuf, size_t *plen) -{ - DEBUGASSERT(chunk->w_offset >= chunk->r_offset); - *pbuf = &chunk->x.data[chunk->r_offset]; - *plen = chunk->w_offset - chunk->r_offset; -} - -static void chunk_peek_at(const struct buf_chunk *chunk, size_t offset, - const unsigned char **pbuf, size_t *plen) -{ - offset += chunk->r_offset; - DEBUGASSERT(chunk->w_offset >= offset); - *pbuf = &chunk->x.data[offset]; - *plen = chunk->w_offset - offset; -} - -static size_t chunk_skip(struct buf_chunk *chunk, size_t amount) -{ - size_t n = chunk->w_offset - chunk->r_offset; - DEBUGASSERT(chunk->w_offset >= chunk->r_offset); - if(n) { - n = CURLMIN(n, amount); - chunk->r_offset += n; - if(chunk->r_offset == chunk->w_offset) - chunk->r_offset = chunk->w_offset = 0; - } - return n; -} - -static void chunk_shift(struct buf_chunk *chunk) -{ - if(chunk->r_offset) { - if(!chunk_is_empty(chunk)) { - size_t n = chunk->w_offset - chunk->r_offset; - memmove(chunk->x.data, chunk->x.data + chunk->r_offset, n); - chunk->w_offset -= chunk->r_offset; - chunk->r_offset = 0; - } - else { - chunk->r_offset = chunk->w_offset = 0; - } - } -} - -static void chunk_list_free(struct buf_chunk **anchor) -{ - struct buf_chunk *chunk; - while(*anchor) { - chunk = *anchor; - *anchor = chunk->next; - free(chunk); - } -} - - - -void Curl_bufcp_init(struct bufc_pool *pool, - size_t chunk_size, size_t spare_max) -{ - DEBUGASSERT(chunk_size > 0); - DEBUGASSERT(spare_max > 0); - memset(pool, 0, sizeof(*pool)); - pool->chunk_size = chunk_size; - pool->spare_max = spare_max; -} - -static CURLcode bufcp_take(struct bufc_pool *pool, - struct buf_chunk **pchunk) -{ - struct buf_chunk *chunk = NULL; - - if(pool->spare) { - chunk = pool->spare; - pool->spare = chunk->next; - --pool->spare_count; - chunk_reset(chunk); - *pchunk = chunk; - return CURLE_OK; - } - - chunk = calloc(1, sizeof(*chunk) + pool->chunk_size); - if(!chunk) { - *pchunk = NULL; - return CURLE_OUT_OF_MEMORY; - } - chunk->dlen = pool->chunk_size; - *pchunk = chunk; - return CURLE_OK; -} - -static void bufcp_put(struct bufc_pool *pool, - struct buf_chunk *chunk) -{ - if(pool->spare_count >= pool->spare_max) { - free(chunk); - } - else { - chunk_reset(chunk); - chunk->next = pool->spare; - pool->spare = chunk; - ++pool->spare_count; - } -} - -void Curl_bufcp_free(struct bufc_pool *pool) -{ - chunk_list_free(&pool->spare); - pool->spare_count = 0; -} - -static void bufq_init(struct bufq *q, struct bufc_pool *pool, - size_t chunk_size, size_t max_chunks, int opts) -{ - DEBUGASSERT(chunk_size > 0); - DEBUGASSERT(max_chunks > 0); - memset(q, 0, sizeof(*q)); - q->chunk_size = chunk_size; - q->max_chunks = max_chunks; - q->pool = pool; - q->opts = opts; -} - -void Curl_bufq_init2(struct bufq *q, size_t chunk_size, size_t max_chunks, - int opts) -{ - bufq_init(q, NULL, chunk_size, max_chunks, opts); -} - -void Curl_bufq_init(struct bufq *q, size_t chunk_size, size_t max_chunks) -{ - bufq_init(q, NULL, chunk_size, max_chunks, BUFQ_OPT_NONE); -} - -void Curl_bufq_initp(struct bufq *q, struct bufc_pool *pool, - size_t max_chunks, int opts) -{ - bufq_init(q, pool, pool->chunk_size, max_chunks, opts); -} - -void Curl_bufq_free(struct bufq *q) -{ - chunk_list_free(&q->head); - chunk_list_free(&q->spare); - q->tail = NULL; - q->chunk_count = 0; -} - -void Curl_bufq_reset(struct bufq *q) -{ - struct buf_chunk *chunk; - while(q->head) { - chunk = q->head; - q->head = chunk->next; - chunk->next = q->spare; - q->spare = chunk; - } - q->tail = NULL; -} - -size_t Curl_bufq_len(const struct bufq *q) -{ - const struct buf_chunk *chunk = q->head; - size_t len = 0; - while(chunk) { - len += chunk_len(chunk); - chunk = chunk->next; - } - return len; -} - -size_t Curl_bufq_space(const struct bufq *q) -{ - size_t space = 0; - if(q->tail) - space += chunk_space(q->tail); - if(q->spare) { - struct buf_chunk *chunk = q->spare; - while(chunk) { - space += chunk->dlen; - chunk = chunk->next; - } - } - if(q->chunk_count < q->max_chunks) { - space += (q->max_chunks - q->chunk_count) * q->chunk_size; - } - return space; -} - -bool Curl_bufq_is_empty(const struct bufq *q) -{ - return !q->head || chunk_is_empty(q->head); -} - -bool Curl_bufq_is_full(const struct bufq *q) -{ - if(!q->tail || q->spare) - return FALSE; - if(q->chunk_count < q->max_chunks) - return FALSE; - if(q->chunk_count > q->max_chunks) - return TRUE; - /* we have no spares and cannot make more, is the tail full? */ - return chunk_is_full(q->tail); -} - -static struct buf_chunk *get_spare(struct bufq *q) -{ - struct buf_chunk *chunk = NULL; - - if(q->spare) { - chunk = q->spare; - q->spare = chunk->next; - chunk_reset(chunk); - return chunk; - } - - if(q->chunk_count >= q->max_chunks && (!(q->opts & BUFQ_OPT_SOFT_LIMIT))) - return NULL; - - if(q->pool) { - if(bufcp_take(q->pool, &chunk)) - return NULL; - ++q->chunk_count; - return chunk; - } - else { - chunk = calloc(1, sizeof(*chunk) + q->chunk_size); - if(!chunk) - return NULL; - chunk->dlen = q->chunk_size; - ++q->chunk_count; - return chunk; - } -} - -static void prune_head(struct bufq *q) -{ - struct buf_chunk *chunk; - - while(q->head && chunk_is_empty(q->head)) { - chunk = q->head; - q->head = chunk->next; - if(q->tail == chunk) - q->tail = q->head; - if(q->pool) { - bufcp_put(q->pool, chunk); - --q->chunk_count; - } - else if((q->chunk_count > q->max_chunks) || - (q->opts & BUFQ_OPT_NO_SPARES)) { - /* SOFT_LIMIT allowed us more than max. free spares until - * we are at max again. Or free them if we are configured - * to not use spares. */ - free(chunk); - --q->chunk_count; - } - else { - chunk->next = q->spare; - q->spare = chunk; - } - } -} - -static struct buf_chunk *get_non_full_tail(struct bufq *q) -{ - struct buf_chunk *chunk; - - if(q->tail && !chunk_is_full(q->tail)) - return q->tail; - chunk = get_spare(q); - if(chunk) { - /* new tail, and possibly new head */ - if(q->tail) { - q->tail->next = chunk; - q->tail = chunk; - } - else { - DEBUGASSERT(!q->head); - q->head = q->tail = chunk; - } - } - return chunk; -} - -ssize_t Curl_bufq_write(struct bufq *q, - const unsigned char *buf, size_t len, - CURLcode *err) -{ - struct buf_chunk *tail; - ssize_t nwritten = 0; - size_t n; - - DEBUGASSERT(q->max_chunks > 0); - while(len) { - tail = get_non_full_tail(q); - if(!tail) { - if(q->chunk_count < q->max_chunks) { - *err = CURLE_OUT_OF_MEMORY; - return -1; - } - break; - } - n = chunk_append(tail, buf, len); - if(!n) - break; - nwritten += n; - buf += n; - len -= n; - } - if(nwritten == 0 && len) { - *err = CURLE_AGAIN; - return -1; - } - *err = CURLE_OK; - return nwritten; -} - -ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len, - CURLcode *err) -{ - ssize_t nread = 0; - size_t n; - - *err = CURLE_OK; - while(len && q->head) { - n = chunk_read(q->head, buf, len); - if(n) { - nread += n; - buf += n; - len -= n; - } - prune_head(q); - } - if(nread == 0) { - *err = CURLE_AGAIN; - return -1; - } - return nread; -} - -bool Curl_bufq_peek(struct bufq *q, - const unsigned char **pbuf, size_t *plen) -{ - if(q->head && chunk_is_empty(q->head)) { - prune_head(q); - } - if(q->head && !chunk_is_empty(q->head)) { - chunk_peek(q->head, pbuf, plen); - return TRUE; - } - *pbuf = NULL; - *plen = 0; - return FALSE; -} - -bool Curl_bufq_peek_at(struct bufq *q, size_t offset, - const unsigned char **pbuf, size_t *plen) -{ - struct buf_chunk *c = q->head; - size_t clen; - - while(c) { - clen = chunk_len(c); - if(!clen) - break; - if(offset >= clen) { - offset -= clen; - c = c->next; - continue; - } - chunk_peek_at(c, offset, pbuf, plen); - return TRUE; - } - *pbuf = NULL; - *plen = 0; - return FALSE; -} - -void Curl_bufq_skip(struct bufq *q, size_t amount) -{ - size_t n; - - while(amount && q->head) { - n = chunk_skip(q->head, amount); - amount -= n; - prune_head(q); - } -} - -void Curl_bufq_skip_and_shift(struct bufq *q, size_t amount) -{ - Curl_bufq_skip(q, amount); - if(q->tail) - chunk_shift(q->tail); -} - -ssize_t Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer, - void *writer_ctx, CURLcode *err) -{ - const unsigned char *buf; - size_t blen; - ssize_t nwritten = 0; - - while(Curl_bufq_peek(q, &buf, &blen)) { - ssize_t chunk_written; - - chunk_written = writer(writer_ctx, buf, blen, err); - if(chunk_written < 0) { - if(!nwritten || *err != CURLE_AGAIN) { - /* blocked on first write or real error, fail */ - nwritten = -1; - } - break; - } - if(!chunk_written) { - if(!nwritten) { - /* treat as blocked */ - *err = CURLE_AGAIN; - nwritten = -1; - } - break; - } - Curl_bufq_skip(q, (size_t)chunk_written); - nwritten += chunk_written; - } - return nwritten; -} - -ssize_t Curl_bufq_write_pass(struct bufq *q, - const unsigned char *buf, size_t len, - Curl_bufq_writer *writer, void *writer_ctx, - CURLcode *err) -{ - ssize_t nwritten = 0, n; - - *err = CURLE_OK; - while(len) { - if(Curl_bufq_is_full(q)) { - /* try to make room in case we are full */ - n = Curl_bufq_pass(q, writer, writer_ctx, err); - if(n < 0) { - if(*err != CURLE_AGAIN) { - /* real error, fail */ - return -1; - } - /* would block, bufq is full, give up */ - break; - } - } - - /* Add whatever is remaining now to bufq */ - n = Curl_bufq_write(q, buf, len, err); - if(n < 0) { - if(*err != CURLE_AGAIN) { - /* real error, fail */ - return -1; - } - /* no room in bufq */ - break; - } - /* edge case of writer returning 0 (and len is >0) - * break or we might enter an infinite loop here */ - if(n == 0) - break; - - /* Maybe only part of `data` has been added, continue to loop */ - buf += (size_t)n; - len -= (size_t)n; - nwritten += (size_t)n; - } - - if(!nwritten && len) { - *err = CURLE_AGAIN; - return -1; - } - *err = CURLE_OK; - return nwritten; -} - -ssize_t Curl_bufq_sipn(struct bufq *q, size_t max_len, - Curl_bufq_reader *reader, void *reader_ctx, - CURLcode *err) -{ - struct buf_chunk *tail = NULL; - ssize_t nread; - - *err = CURLE_AGAIN; - tail = get_non_full_tail(q); - if(!tail) { - if(q->chunk_count < q->max_chunks) { - *err = CURLE_OUT_OF_MEMORY; - return -1; - } - /* full, blocked */ - *err = CURLE_AGAIN; - return -1; - } - - nread = chunk_slurpn(tail, max_len, reader, reader_ctx, err); - if(nread < 0) { - return -1; - } - else if(nread == 0) { - /* eof */ - *err = CURLE_OK; - } - return nread; -} - -/** - * Read up to `max_len` bytes and append it to the end of the buffer queue. - * if `max_len` is 0, no limit is imposed and the call behaves exactly - * the same as `Curl_bufq_slurp()`. - * Returns the total amount of buf read (may be 0) or -1 on other - * reader errors. - * Note that even in case of a -1 chunks may have been read and - * the buffer queue will have different length than before. - */ -static ssize_t bufq_slurpn(struct bufq *q, size_t max_len, - Curl_bufq_reader *reader, void *reader_ctx, - CURLcode *err) -{ - ssize_t nread = 0, n; - - *err = CURLE_AGAIN; - while(1) { - - n = Curl_bufq_sipn(q, max_len, reader, reader_ctx, err); - if(n < 0) { - if(!nread || *err != CURLE_AGAIN) { - /* blocked on first read or real error, fail */ - nread = -1; - } - else - *err = CURLE_OK; - break; - } - else if(n == 0) { - /* eof */ - *err = CURLE_OK; - break; - } - nread += (size_t)n; - if(max_len) { - DEBUGASSERT((size_t)n <= max_len); - max_len -= (size_t)n; - if(!max_len) - break; - } - /* give up slurping when we get less bytes than we asked for */ - if(q->tail && !chunk_is_full(q->tail)) - break; - } - return nread; -} - -ssize_t Curl_bufq_slurp(struct bufq *q, Curl_bufq_reader *reader, - void *reader_ctx, CURLcode *err) -{ - return bufq_slurpn(q, 0, reader, reader_ctx, err); -} diff --git a/contrib/libs/curl/lib/bufq.h b/contrib/libs/curl/lib/bufq.h deleted file mode 100644 index 89b5c84efe..0000000000 --- a/contrib/libs/curl/lib/bufq.h +++ /dev/null @@ -1,271 +0,0 @@ -#ifndef HEADER_CURL_BUFQ_H -#define HEADER_CURL_BUFQ_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ -#include "curl_setup.h" - -#include <curl/curl.h> - -/** - * A chunk of bytes for reading and writing. - * The size is fixed a creation with read and write offset - * for where unread content is. - */ -struct buf_chunk { - struct buf_chunk *next; /* to keep it in a list */ - size_t dlen; /* the amount of allocated x.data[] */ - size_t r_offset; /* first unread bytes */ - size_t w_offset; /* one after last written byte */ - union { - unsigned char data[1]; /* the buffer for `dlen` bytes */ - void *dummy; /* alignment */ - } x; -}; - -/** - * A pool for providing/keeping a number of chunks of the same size - * - * The same pool can be shared by many `bufq` instances. However, a pool - * is not thread safe. All bufqs using it are supposed to operate in the - * same thread. - */ -struct bufc_pool { - struct buf_chunk *spare; /* list of available spare chunks */ - size_t chunk_size; /* the size of chunks in this pool */ - size_t spare_count; /* current number of spare chunks in list */ - size_t spare_max; /* max number of spares to keep */ -}; - -void Curl_bufcp_init(struct bufc_pool *pool, - size_t chunk_size, size_t spare_max); - -void Curl_bufcp_free(struct bufc_pool *pool); - -/** - * A queue of byte chunks for reading and writing. - * Reading is done from `head`, writing is done to `tail`. - * - * `bufq`s can be empty or full or neither. Its `len` is the number - * of bytes that can be read. For an empty bufq, `len` will be 0. - * - * By default, a bufq can hold up to `max_chunks * chunk_size` number - * of bytes. When `max_chunks` are used (in the `head` list) and the - * `tail` chunk is full, the bufq will report that it is full. - * - * On a full bufq, `len` may be less than the maximum number of bytes, - * e.g. when the head chunk is partially read. `len` may also become - * larger than the max when option `BUFQ_OPT_SOFT_LIMIT` is used. - * - * By default, writing to a full bufq will return (-1, CURLE_AGAIN). Same - * as reading from an empty bufq. - * With `BUFQ_OPT_SOFT_LIMIT` set, a bufq will allow writing becond this - * limit and use more than `max_chunks`. However it will report that it - * is full nevertheless. This is provided for situation where writes - * preferably never fail (except for memory exhaustion). - * - * By default and without a pool, a bufq will keep chunks that read - * read empty in its `spare` list. Option `BUFQ_OPT_NO_SPARES` will - * disable that and free chunks once they become empty. - * - * When providing a pool to a bufq, all chunk creation and spare handling - * will be delegated to that pool. - */ -struct bufq { - struct buf_chunk *head; /* chunk with bytes to read from */ - struct buf_chunk *tail; /* chunk to write to */ - struct buf_chunk *spare; /* list of free chunks, unless `pool` */ - struct bufc_pool *pool; /* optional pool for free chunks */ - size_t chunk_count; /* current number of chunks in `head+spare` */ - size_t max_chunks; /* max `head` chunks to use */ - size_t chunk_size; /* size of chunks to manage */ - int opts; /* options for handling queue, see below */ -}; - -/** - * Default behaviour: chunk limit is "hard", meaning attempts to write - * more bytes than can be hold in `max_chunks` is refused and will return - * -1, CURLE_AGAIN. */ -#define BUFQ_OPT_NONE (0) -/** - * Make `max_chunks` a "soft" limit. A bufq will report that it is "full" - * when `max_chunks` are used, but allows writing beyond this limit. - */ -#define BUFQ_OPT_SOFT_LIMIT (1 << 0) -/** - * Do not keep spare chunks. - */ -#define BUFQ_OPT_NO_SPARES (1 << 1) - -/** - * Initialize a buffer queue that can hold up to `max_chunks` buffers - * each of size `chunk_size`. The bufq will not allow writing of - * more bytes than can be held in `max_chunks`. - */ -void Curl_bufq_init(struct bufq *q, size_t chunk_size, size_t max_chunks); - -/** - * Initialize a buffer queue that can hold up to `max_chunks` buffers - * each of size `chunk_size` with the given options. See `BUFQ_OPT_*`. - */ -void Curl_bufq_init2(struct bufq *q, size_t chunk_size, - size_t max_chunks, int opts); - -void Curl_bufq_initp(struct bufq *q, struct bufc_pool *pool, - size_t max_chunks, int opts); - -/** - * Reset the buffer queue to be empty. Will keep any allocated buffer - * chunks around. - */ -void Curl_bufq_reset(struct bufq *q); - -/** - * Free all resources held by the buffer queue. - */ -void Curl_bufq_free(struct bufq *q); - -/** - * Return the total amount of data in the queue. - */ -size_t Curl_bufq_len(const struct bufq *q); - -/** - * Return the total amount of free space in the queue. - * The returned length is the number of bytes that can - * be expected to be written successfully to the bufq, - * providing no memory allocations fail. - */ -size_t Curl_bufq_space(const struct bufq *q); - -/** - * Returns TRUE iff there is no data in the buffer queue. - */ -bool Curl_bufq_is_empty(const struct bufq *q); - -/** - * Returns TRUE iff there is no space left in the buffer queue. - */ -bool Curl_bufq_is_full(const struct bufq *q); - -/** - * Write buf to the end of the buffer queue. The buf is copied - * and the amount of copied bytes is returned. - * A return code of -1 indicates an error, setting `err` to the - * cause. An err of CURLE_AGAIN is returned if the buffer queue is full. - */ -ssize_t Curl_bufq_write(struct bufq *q, - const unsigned char *buf, size_t len, - CURLcode *err); - -/** - * Read buf from the start of the buffer queue. The buf is copied - * and the amount of copied bytes is returned. - * A return code of -1 indicates an error, setting `err` to the - * cause. An err of CURLE_AGAIN is returned if the buffer queue is empty. - */ -ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len, - CURLcode *err); - -/** - * Peek at the head chunk in the buffer queue. Returns a pointer to - * the chunk buf (at the current offset) and its length. Does not - * modify the buffer queue. - * Returns TRUE iff bytes are available. Sets `pbuf` to NULL and `plen` - * to 0 when no bytes are available. - * Repeated calls return the same information until the buffer queue - * is modified, see `Curl_bufq_skip()`` - */ -bool Curl_bufq_peek(struct bufq *q, - const unsigned char **pbuf, size_t *plen); - -bool Curl_bufq_peek_at(struct bufq *q, size_t offset, - const unsigned char **pbuf, size_t *plen); - -/** - * Tell the buffer queue to discard `amount` buf bytes at the head - * of the queue. Skipping more buf than is currently buffered will - * just empty the queue. - */ -void Curl_bufq_skip(struct bufq *q, size_t amount); - -/** - * Same as `skip` but shift tail data to the start afterwards, - * so that further writes will find room in tail. - */ -void Curl_bufq_skip_and_shift(struct bufq *q, size_t amount); - -typedef ssize_t Curl_bufq_writer(void *writer_ctx, - const unsigned char *buf, size_t len, - CURLcode *err); -/** - * Passes the chunks in the buffer queue to the writer and returns - * the amount of buf written. A writer may return -1 and CURLE_AGAIN - * to indicate blocking at which point the queue will stop and return - * the amount of buf passed so far. - * -1 is returned on any other errors reported by the writer. - * Note that in case of a -1 chunks may have been written and - * the buffer queue will have different length than before. - */ -ssize_t Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer, - void *writer_ctx, CURLcode *err); - -typedef ssize_t Curl_bufq_reader(void *reader_ctx, - unsigned char *buf, size_t len, - CURLcode *err); - -/** - * Read date and append it to the end of the buffer queue until the - * reader returns blocking or the queue is full. A reader returns - * -1 and CURLE_AGAIN to indicate blocking. - * Returns the total amount of buf read (may be 0) or -1 on other - * reader errors. - * Note that in case of a -1 chunks may have been read and - * the buffer queue will have different length than before. - */ -ssize_t Curl_bufq_slurp(struct bufq *q, Curl_bufq_reader *reader, - void *reader_ctx, CURLcode *err); - -/** - * Read *once* up to `max_len` bytes and append it to the buffer. - * if `max_len` is 0, no limit is imposed besides the chunk space. - * Returns the total amount of buf read (may be 0) or -1 on other - * reader errors. - */ -ssize_t Curl_bufq_sipn(struct bufq *q, size_t max_len, - Curl_bufq_reader *reader, void *reader_ctx, - CURLcode *err); - -/** - * Write buf to the end of the buffer queue. - * Will write bufq content or passed `buf` directly using the `writer` - * callback when it sees fit. 'buf' might get passed directly - * on or is placed into the buffer, depending on `len` and current - * amount buffered, chunk size, etc. - */ -ssize_t Curl_bufq_write_pass(struct bufq *q, - const unsigned char *buf, size_t len, - Curl_bufq_writer *writer, void *writer_ctx, - CURLcode *err); - -#endif /* HEADER_CURL_BUFQ_H */ diff --git a/contrib/libs/curl/lib/bufref.c b/contrib/libs/curl/lib/bufref.c index ce686b6f37..91b037431d 100644 --- a/contrib/libs/curl/lib/bufref.c +++ b/contrib/libs/curl/lib/bufref.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2021 - 2022, 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 diff --git a/contrib/libs/curl/lib/bufref.h b/contrib/libs/curl/lib/bufref.h index dd424f18f5..96b818b537 100644 --- a/contrib/libs/curl/lib/bufref.h +++ b/contrib/libs/curl/lib/bufref.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2021 - 2022, 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 diff --git a/contrib/libs/curl/lib/c-hyper.c b/contrib/libs/curl/lib/c-hyper.c index 309f295921..ab6740ca4a 100644 --- a/contrib/libs/curl/lib/c-hyper.c +++ b/contrib/libs/curl/lib/c-hyper.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -71,11 +71,9 @@ size_t Curl_hyper_recv(void *userp, hyper_context *ctx, DEBUGASSERT(conn); (void)ctx; - DEBUGF(infof(data, "Curl_hyper_recv(%zu)", buflen)); result = Curl_read(data, conn->sockfd, (char *)buf, buflen, &nread); if(result == CURLE_AGAIN) { /* would block, register interest */ - DEBUGF(infof(data, "Curl_hyper_recv(%zu) -> EAGAIN", buflen)); if(data->hyp.read_waker) hyper_waker_free(data->hyp.read_waker); data->hyp.read_waker = hyper_context_waker(ctx); @@ -89,7 +87,6 @@ size_t Curl_hyper_recv(void *userp, hyper_context *ctx, failf(data, "Curl_read failed"); return HYPER_IO_ERROR; } - DEBUGF(infof(data, "Curl_hyper_recv(%zu) -> %zd", buflen, nread)); return (size_t)nread; } @@ -101,12 +98,8 @@ size_t Curl_hyper_send(void *userp, hyper_context *ctx, CURLcode result; ssize_t nwrote; - DEBUGF(infof(data, "Curl_hyper_send(%zu)", buflen)); result = Curl_write(data, conn->sockfd, (void *)buf, buflen, &nwrote); - if(!result && !nwrote) - result = CURLE_AGAIN; if(result == CURLE_AGAIN) { - DEBUGF(infof(data, "Curl_hyper_send(%zu) -> EAGAIN", buflen)); /* would block, register interest */ if(data->hyp.write_waker) hyper_waker_free(data->hyp.write_waker); @@ -121,7 +114,6 @@ size_t Curl_hyper_send(void *userp, hyper_context *ctx, failf(data, "Curl_write failed"); return HYPER_IO_ERROR; } - DEBUGF(infof(data, "Curl_hyper_send(%zu) -> %zd", buflen, nwrote)); return (size_t)nwrote; } @@ -171,10 +163,6 @@ static int hyper_each_header(void *userdata, writetype = CLIENTWRITE_HEADER; if(data->set.include_header) writetype |= CLIENTWRITE_BODY; - if(data->state.hconnect) - writetype |= CLIENTWRITE_CONNECT; - if(data->req.httpcode/100 == 1) - writetype |= CLIENTWRITE_1XX; result = Curl_client_write(data, writetype, headp, len); if(result) { data->state.hresult = CURLE_ABORTED_BY_CALLBACK; @@ -182,8 +170,8 @@ static int hyper_each_header(void *userdata, } } - data->info.header_size += (curl_off_t)len; - data->req.headerbytecount += (curl_off_t)len; + data->info.header_size += (long)len; + data->req.headerbytecount += (long)len; return HYPER_ITER_CONTINUE; } @@ -257,7 +245,7 @@ static int hyper_body_chunk(void *userdata, const hyper_buf *chunk) } /* - * Hyper does not consider the status line, the first line in an HTTP/1 + * Hyper does not consider the status line, the first line in a HTTP/1 * response, to be a header. The libcurl API does. This function sends the * status line in the header callback. */ static CURLcode status_line(struct Curl_easy *data, @@ -272,25 +260,23 @@ static CURLcode status_line(struct Curl_easy *data, int writetype; vstr = http_version == HYPER_HTTP_VERSION_1_1 ? "1.1" : (http_version == HYPER_HTTP_VERSION_2 ? "2" : "1.0"); - - /* We need to set 'httpcodeq' for functions that check the response code in - a single place. */ - data->req.httpcode = http_status; + conn->httpversion = + http_version == HYPER_HTTP_VERSION_1_1 ? 11 : + (http_version == HYPER_HTTP_VERSION_2 ? 20 : 10); + if(http_version == HYPER_HTTP_VERSION_1_0) + data->state.httpwant = CURL_HTTP_VERSION_1_0; if(data->state.hconnect) /* CONNECT */ data->info.httpproxycode = http_status; - else { - conn->httpversion = - http_version == HYPER_HTTP_VERSION_1_1 ? 11 : - (http_version == HYPER_HTTP_VERSION_2 ? 20 : 10); - if(http_version == HYPER_HTTP_VERSION_1_0) - data->state.httpwant = CURL_HTTP_VERSION_1_0; - result = Curl_http_statusline(data, conn); - if(result) - return result; - } + /* We need to set 'httpcodeq' for functions that check the response code in + a single place. */ + data->req.httpcode = http_status; + + result = Curl_http_statusline(data, conn); + if(result) + return result; Curl_dyn_reset(&data->state.headerb); @@ -313,8 +299,9 @@ static CURLcode status_line(struct Curl_easy *data, if(result) return result; } - data->info.header_size += (curl_off_t)len; - data->req.headerbytecount += (curl_off_t)len; + data->info.header_size += (long)len; + data->req.headerbytecount += (long)len; + data->req.httpcode = http_status; return CURLE_OK; } @@ -428,10 +415,8 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data, break; } else if(h->endtask == task) { - /* end of transfer, forget the task handled, we might get a - * new one with the same address in the future. */ + /* end of transfer */ *done = TRUE; - h->endtask = NULL; infof(data, "hyperstream is done"); if(!k->bodywrites) { /* hyper doesn't always call the body write callback */ @@ -441,7 +426,8 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data, break; } else if(t != HYPER_TASK_RESPONSE) { - continue; + *didwhat = KEEP_RECV; + break; } /* HYPER_TASK_RESPONSE */ @@ -492,7 +478,7 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data, if(k->upgr101 == UPGR101_WS) { if(http_status == 101) { /* verify the response */ - result = Curl_ws_accept(data, NULL, 0); + result = Curl_ws_accept(data); if(result) return result; } @@ -693,7 +679,7 @@ static int uploadpostfields(void *userdata, hyper_context *ctx, return HYPER_POLL_ERROR; } /* increasing the writebytecount here is a little premature but we - don't know exactly when the body is sent */ + don't know exactly when the body is sent*/ data->req.writebytecount += (size_t)data->req.p.http->postsize; Curl_pgrsSetUploadCounter(data, data->req.writebytecount); data->req.upload_done = TRUE; @@ -706,7 +692,6 @@ static int uploadstreamed(void *userdata, hyper_context *ctx, { size_t fillcount; struct Curl_easy *data = (struct Curl_easy *)userdata; - struct connectdata *conn = (struct connectdata *)data->conn; CURLcode result; (void)ctx; @@ -721,15 +706,7 @@ static int uploadstreamed(void *userdata, hyper_context *ctx, return HYPER_POLL_PENDING; } - if(data->req.upload_chunky && conn->bits.authneg) { - fillcount = 0; - data->req.upload_chunky = FALSE; - result = CURLE_OK; - } - else { - result = Curl_fillreadbuffer(data, data->set.upload_buffer_size, - &fillcount); - } + result = Curl_fillreadbuffer(data, data->set.upload_buffer_size, &fillcount); if(result) { data->state.hresult = result; return HYPER_POLL_ERROR; @@ -755,7 +732,7 @@ static int uploadstreamed(void *userdata, hyper_context *ctx, return HYPER_POLL_ERROR; } /* increasing the writebytecount here is a little premature but we - don't know exactly when the body is sent */ + don't know exactly when the body is sent*/ data->req.writebytecount += fillcount; Curl_pgrsSetUploadCounter(data, fillcount); } @@ -763,7 +740,7 @@ static int uploadstreamed(void *userdata, hyper_context *ctx, } /* - * bodysend() sets up headers in the outgoing request for an HTTP transfer that + * bodysend() sets up headers in the outgoing request for a HTTP transfer that * sends a body */ @@ -868,7 +845,7 @@ static void http1xx_cb(void *arg, struct hyper_response *resp) } /* - * Curl_http() gets called from the generic multi_do() function when an HTTP + * Curl_http() gets called from the generic multi_do() function when a HTTP * request is to be performed. This creates and sends a properly constructed * HTTP request. */ @@ -1135,16 +1112,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) goto error; } -#ifdef HAVE_LIBZ - /* we only consider transfer-encoding magic if libz support is built-in */ - result = Curl_transferencode(data); - if(result) - goto error; - result = Curl_hyper_header(data, headers, data->state.aptr.te); - if(result) - goto error; -#endif - if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && data->set.str[STRING_ENCODING]) { Curl_safefree(data->state.aptr.accept_encoding); @@ -1161,6 +1128,16 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) else Curl_safefree(data->state.aptr.accept_encoding); +#ifdef HAVE_LIBZ + /* we only consider transfer-encoding magic if libz support is built-in */ + result = Curl_transferencode(data); + if(result) + goto error; + result = Curl_hyper_header(data, headers, data->state.aptr.te); + if(result) + goto error; +#endif + result = cookies(data, conn, headers); if(result) goto error; @@ -1182,12 +1159,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"\r\n", 2); - if(data->req.upload_chunky && conn->bits.authneg) { - data->req.upload_chunky = TRUE; - } - else { - data->req.upload_chunky = FALSE; - } + data->req.upload_chunky = FALSE; sendtask = hyper_clientconn_send(client, req); if(!sendtask) { failf(data, "hyper_clientconn_send"); @@ -1219,7 +1191,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) Curl_safefree(data->state.aptr.userpwd); Curl_safefree(data->state.aptr.proxyuserpwd); return CURLE_OK; -error: + error: DEBUGASSERT(result); if(io) hyper_io_free(io); diff --git a/contrib/libs/curl/lib/c-hyper.h b/contrib/libs/curl/lib/c-hyper.h index 5c0357f37b..f268222b5c 100644 --- a/contrib/libs/curl/lib/c-hyper.h +++ b/contrib/libs/curl/lib/c-hyper.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/cf-h1-proxy.c b/contrib/libs/curl/lib/cf-h1-proxy.c deleted file mode 100644 index 90527c1954..0000000000 --- a/contrib/libs/curl/lib/cf-h1-proxy.c +++ /dev/null @@ -1,1184 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) - -#include <curl/curl.h> -#ifdef USE_HYPER -#error #include <hyper.h> -#endif -#include "urldata.h" -#include "dynbuf.h" -#include "sendf.h" -#include "http.h" -#include "http_proxy.h" -#include "url.h" -#include "select.h" -#include "progress.h" -#include "cfilters.h" -#include "cf-h1-proxy.h" -#include "connect.h" -#include "curl_log.h" -#include "curlx.h" -#include "vtls/vtls.h" -#include "transfer.h" -#include "multiif.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - - -typedef enum { - H1_TUNNEL_INIT, /* init/default/no tunnel state */ - H1_TUNNEL_CONNECT, /* CONNECT request is being send */ - H1_TUNNEL_RECEIVE, /* CONNECT answer is being received */ - H1_TUNNEL_RESPONSE, /* CONNECT response received completely */ - H1_TUNNEL_ESTABLISHED, - H1_TUNNEL_FAILED -} h1_tunnel_state; - -/* struct for HTTP CONNECT tunneling */ -struct h1_tunnel_state { - int sockindex; - const char *hostname; - int remote_port; - struct HTTP CONNECT; - struct dynbuf rcvbuf; - struct dynbuf req; - size_t nsend; - size_t headerlines; - enum keeponval { - KEEPON_DONE, - KEEPON_CONNECT, - KEEPON_IGNORE - } keepon; - curl_off_t cl; /* size of content to read and ignore */ - h1_tunnel_state tunnel_state; - BIT(chunked_encoding); - BIT(close_connection); -}; - - -static bool tunnel_is_established(struct h1_tunnel_state *ts) -{ - return ts && (ts->tunnel_state == H1_TUNNEL_ESTABLISHED); -} - -static bool tunnel_is_failed(struct h1_tunnel_state *ts) -{ - return ts && (ts->tunnel_state == H1_TUNNEL_FAILED); -} - -static CURLcode tunnel_reinit(struct h1_tunnel_state *ts, - struct connectdata *conn, - struct Curl_easy *data) -{ - (void)data; - DEBUGASSERT(ts); - Curl_dyn_reset(&ts->rcvbuf); - Curl_dyn_reset(&ts->req); - ts->tunnel_state = H1_TUNNEL_INIT; - ts->keepon = KEEPON_CONNECT; - ts->cl = 0; - ts->close_connection = FALSE; - - if(conn->bits.conn_to_host) - ts->hostname = conn->conn_to_host.name; - else if(ts->sockindex == SECONDARYSOCKET) - ts->hostname = conn->secondaryhostname; - else - ts->hostname = conn->host.name; - - if(ts->sockindex == SECONDARYSOCKET) - ts->remote_port = conn->secondary_port; - else if(conn->bits.conn_to_port) - ts->remote_port = conn->conn_to_port; - else - ts->remote_port = conn->remote_port; - - return CURLE_OK; -} - -static CURLcode tunnel_init(struct h1_tunnel_state **pts, - struct Curl_easy *data, - struct connectdata *conn, - int sockindex) -{ - struct h1_tunnel_state *ts; - CURLcode result; - - if(conn->handler->flags & PROTOPT_NOTCPPROXY) { - failf(data, "%s cannot be done over CONNECT", conn->handler->scheme); - return CURLE_UNSUPPORTED_PROTOCOL; - } - - /* we might need the upload buffer for streaming a partial request */ - result = Curl_get_upload_buffer(data); - if(result) - return result; - - ts = calloc(1, sizeof(*ts)); - if(!ts) - return CURLE_OUT_OF_MEMORY; - - ts->sockindex = sockindex; - infof(data, "allocate connect buffer"); - - Curl_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS); - Curl_dyn_init(&ts->req, DYN_HTTP_REQUEST); - - *pts = ts; - connkeep(conn, "HTTP proxy CONNECT"); - return tunnel_reinit(ts, conn, data); -} - -static void h1_tunnel_go_state(struct Curl_cfilter *cf, - struct h1_tunnel_state *ts, - h1_tunnel_state new_state, - struct Curl_easy *data) -{ - if(ts->tunnel_state == new_state) - return; - /* leaving this one */ - switch(ts->tunnel_state) { - case H1_TUNNEL_CONNECT: - data->req.ignorebody = FALSE; - break; - default: - break; - } - /* entering this one */ - switch(new_state) { - case H1_TUNNEL_INIT: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'init'")); - tunnel_reinit(ts, cf->conn, data); - break; - - case H1_TUNNEL_CONNECT: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'connect'")); - ts->tunnel_state = H1_TUNNEL_CONNECT; - ts->keepon = KEEPON_CONNECT; - Curl_dyn_reset(&ts->rcvbuf); - break; - - case H1_TUNNEL_RECEIVE: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'receive'")); - ts->tunnel_state = H1_TUNNEL_RECEIVE; - break; - - case H1_TUNNEL_RESPONSE: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'response'")); - ts->tunnel_state = H1_TUNNEL_RESPONSE; - break; - - case H1_TUNNEL_ESTABLISHED: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'established'")); - infof(data, "CONNECT phase completed"); - data->state.authproxy.done = TRUE; - data->state.authproxy.multipass = FALSE; - /* FALLTHROUGH */ - case H1_TUNNEL_FAILED: - if(new_state == H1_TUNNEL_FAILED) - DEBUGF(LOG_CF(data, cf, "new tunnel state 'failed'")); - ts->tunnel_state = new_state; - Curl_dyn_reset(&ts->rcvbuf); - Curl_dyn_reset(&ts->req); - /* restore the protocol pointer */ - data->info.httpcode = 0; /* clear it as it might've been used for the - proxy */ - /* If a proxy-authorization header was used for the proxy, then we should - make sure that it isn't accidentally used for the document request - after we've connected. So let's free and clear it here. */ - Curl_safefree(data->state.aptr.proxyuserpwd); -#ifdef USE_HYPER - data->state.hconnect = FALSE; -#endif - break; - } -} - -static void tunnel_free(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct h1_tunnel_state *ts = cf->ctx; - if(ts) { - h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); - Curl_dyn_free(&ts->rcvbuf); - Curl_dyn_free(&ts->req); - free(ts); - cf->ctx = NULL; - } -} - -static CURLcode CONNECT_host(struct Curl_easy *data, - struct connectdata *conn, - const char *hostname, - int remote_port, - char **connecthostp, - char **hostp) -{ - char *hostheader; /* for CONNECT */ - char *host = NULL; /* Host: */ - bool ipv6_ip = conn->bits.ipv6_ip; - - /* the hostname may be different */ - if(hostname != conn->host.name) - ipv6_ip = (strchr(hostname, ':') != NULL); - hostheader = /* host:port with IPv6 support */ - aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"", - remote_port); - if(!hostheader) - return CURLE_OUT_OF_MEMORY; - - if(!Curl_checkProxyheaders(data, conn, STRCONST("Host"))) { - host = aprintf("Host: %s\r\n", hostheader); - if(!host) { - free(hostheader); - return CURLE_OUT_OF_MEMORY; - } - } - *connecthostp = hostheader; - *hostp = host; - return CURLE_OK; -} - -#ifndef USE_HYPER -static CURLcode start_CONNECT(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h1_tunnel_state *ts) -{ - struct connectdata *conn = cf->conn; - char *hostheader = NULL; - char *host = NULL; - const char *httpv; - CURLcode result; - - infof(data, "Establish HTTP proxy tunnel to %s:%d", - ts->hostname, ts->remote_port); - - /* This only happens if we've looped here due to authentication - reasons, and we don't really use the newly cloned URL here - then. Just free() it. */ - Curl_safefree(data->req.newurl); - - result = CONNECT_host(data, conn, - ts->hostname, ts->remote_port, - &hostheader, &host); - if(result) - goto out; - - /* Setup the proxy-authorization header, if any */ - result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET, - hostheader, TRUE); - if(result) - goto out; - - httpv = (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? "1.0" : "1.1"; - - result = - Curl_dyn_addf(&ts->req, - "CONNECT %s HTTP/%s\r\n" - "%s" /* Host: */ - "%s", /* Proxy-Authorization */ - hostheader, - httpv, - host?host:"", - data->state.aptr.proxyuserpwd? - data->state.aptr.proxyuserpwd:""); - if(result) - goto out; - - if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) - && data->set.str[STRING_USERAGENT]) - result = Curl_dyn_addf(&ts->req, "User-Agent: %s\r\n", - data->set.str[STRING_USERAGENT]); - if(result) - goto out; - - if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) - result = Curl_dyn_addn(&ts->req, - STRCONST("Proxy-Connection: Keep-Alive\r\n")); - if(result) - goto out; - - result = Curl_add_custom_headers(data, TRUE, &ts->req); - if(result) - goto out; - - /* CRLF terminate the request */ - result = Curl_dyn_addn(&ts->req, STRCONST("\r\n")); - if(result) - goto out; - - /* Send the connect request to the proxy */ - result = Curl_buffer_send(&ts->req, data, &ts->CONNECT, - &data->info.request_size, 0, - ts->sockindex); - ts->headerlines = 0; - -out: - if(result) - failf(data, "Failed sending CONNECT to proxy"); - free(host); - free(hostheader); - return result; -} - -static CURLcode send_CONNECT(struct Curl_easy *data, - struct connectdata *conn, - struct h1_tunnel_state *ts, - bool *done) -{ - struct SingleRequest *k = &data->req; - struct HTTP *http = &ts->CONNECT; - CURLcode result = CURLE_OK; - - if(http->sending != HTTPSEND_REQUEST) - goto out; - - if(!ts->nsend) { - size_t fillcount; - k->upload_fromhere = data->state.ulbuf; - result = Curl_fillreadbuffer(data, data->set.upload_buffer_size, - &fillcount); - if(result) - goto out; - ts->nsend = fillcount; - } - if(ts->nsend) { - ssize_t bytes_written; - /* write to socket (send away data) */ - result = Curl_write(data, - conn->writesockfd, /* socket to send to */ - k->upload_fromhere, /* buffer pointer */ - ts->nsend, /* buffer size */ - &bytes_written); /* actually sent */ - if(result) - goto out; - /* send to debug callback! */ - Curl_debug(data, CURLINFO_HEADER_OUT, - k->upload_fromhere, bytes_written); - - ts->nsend -= bytes_written; - k->upload_fromhere += bytes_written; - } - if(!ts->nsend) - http->sending = HTTPSEND_NADA; - -out: - if(result) - failf(data, "Failed sending CONNECT to proxy"); - *done = (http->sending != HTTPSEND_REQUEST); - return result; -} - -static CURLcode on_resp_header(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h1_tunnel_state *ts, - const char *header) -{ - CURLcode result = CURLE_OK; - struct SingleRequest *k = &data->req; - (void)cf; - - if((checkprefix("WWW-Authenticate:", header) && - (401 == k->httpcode)) || - (checkprefix("Proxy-authenticate:", header) && - (407 == k->httpcode))) { - - bool proxy = (k->httpcode == 407) ? TRUE : FALSE; - char *auth = Curl_copy_header_value(header); - if(!auth) - return CURLE_OUT_OF_MEMORY; - - DEBUGF(LOG_CF(data, cf, "CONNECT: fwd auth header '%s'", header)); - result = Curl_http_input_auth(data, proxy, auth); - - free(auth); - - if(result) - return result; - } - else if(checkprefix("Content-Length:", header)) { - if(k->httpcode/100 == 2) { - /* A client MUST ignore any Content-Length or Transfer-Encoding - header fields received in a successful response to CONNECT. - "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */ - infof(data, "Ignoring Content-Length in CONNECT %03d response", - k->httpcode); - } - else { - (void)curlx_strtoofft(header + strlen("Content-Length:"), - NULL, 10, &ts->cl); - } - } - else if(Curl_compareheader(header, - STRCONST("Connection:"), STRCONST("close"))) - ts->close_connection = TRUE; - else if(checkprefix("Transfer-Encoding:", header)) { - if(k->httpcode/100 == 2) { - /* A client MUST ignore any Content-Length or Transfer-Encoding - header fields received in a successful response to CONNECT. - "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */ - infof(data, "Ignoring Transfer-Encoding in " - "CONNECT %03d response", k->httpcode); - } - else if(Curl_compareheader(header, - STRCONST("Transfer-Encoding:"), - STRCONST("chunked"))) { - infof(data, "CONNECT responded chunked"); - ts->chunked_encoding = TRUE; - /* init our chunky engine */ - Curl_httpchunk_init(data); - } - } - else if(Curl_compareheader(header, - STRCONST("Proxy-Connection:"), - STRCONST("close"))) - ts->close_connection = TRUE; - else if(!strncmp(header, "HTTP/1.", 7) && - ((header[7] == '0') || (header[7] == '1')) && - (header[8] == ' ') && - ISDIGIT(header[9]) && ISDIGIT(header[10]) && ISDIGIT(header[11]) && - !ISDIGIT(header[12])) { - /* store the HTTP code from the proxy */ - data->info.httpproxycode = k->httpcode = (header[9] - '0') * 100 + - (header[10] - '0') * 10 + (header[11] - '0'); - } - return result; -} - -static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h1_tunnel_state *ts, - bool *done) -{ - CURLcode result = CURLE_OK; - struct SingleRequest *k = &data->req; - curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data); - char *linep; - size_t perline; - int error; - -#define SELECT_OK 0 -#define SELECT_ERROR 1 - - error = SELECT_OK; - *done = FALSE; - - if(!Curl_conn_data_pending(data, ts->sockindex)) - return CURLE_OK; - - while(ts->keepon) { - ssize_t gotbytes; - char byte; - - /* Read one byte at a time to avoid a race condition. Wait at most one - second before looping to ensure continuous pgrsUpdates. */ - result = Curl_read(data, tunnelsocket, &byte, 1, &gotbytes); - if(result == CURLE_AGAIN) - /* socket buffer drained, return */ - return CURLE_OK; - - if(Curl_pgrsUpdate(data)) - return CURLE_ABORTED_BY_CALLBACK; - - if(result) { - ts->keepon = KEEPON_DONE; - break; - } - - if(gotbytes <= 0) { - if(data->set.proxyauth && data->state.authproxy.avail && - data->state.aptr.proxyuserpwd) { - /* proxy auth was requested and there was proxy auth available, - then deem this as "mere" proxy disconnect */ - ts->close_connection = TRUE; - infof(data, "Proxy CONNECT connection closed"); - } - else { - error = SELECT_ERROR; - failf(data, "Proxy CONNECT aborted"); - } - ts->keepon = KEEPON_DONE; - break; - } - - if(ts->keepon == KEEPON_IGNORE) { - /* This means we are currently ignoring a response-body */ - - if(ts->cl) { - /* A Content-Length based body: simply count down the counter - and make sure to break out of the loop when we're done! */ - ts->cl--; - if(ts->cl <= 0) { - ts->keepon = KEEPON_DONE; - break; - } - } - else { - /* chunked-encoded body, so we need to do the chunked dance - properly to know when the end of the body is reached */ - CHUNKcode r; - CURLcode extra; - ssize_t tookcareof = 0; - - /* now parse the chunked piece of data so that we can - properly tell when the stream ends */ - r = Curl_httpchunk_read(data, &byte, 1, &tookcareof, &extra); - if(r == CHUNKE_STOP) { - /* we're done reading chunks! */ - infof(data, "chunk reading DONE"); - ts->keepon = KEEPON_DONE; - } - } - continue; - } - - if(Curl_dyn_addn(&ts->rcvbuf, &byte, 1)) { - failf(data, "CONNECT response too large"); - return CURLE_RECV_ERROR; - } - - /* if this is not the end of a header line then continue */ - if(byte != 0x0a) - continue; - - ts->headerlines++; - linep = Curl_dyn_ptr(&ts->rcvbuf); - perline = Curl_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */ - - /* output debug if that is requested */ - Curl_debug(data, CURLINFO_HEADER_IN, linep, perline); - - if(!data->set.suppress_connect_headers) { - /* send the header to the callback */ - int writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT | - (data->set.include_header ? CLIENTWRITE_BODY : 0) | - (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0); - - result = Curl_client_write(data, writetype, linep, perline); - if(result) - return result; - } - - data->info.header_size += (long)perline; - - /* Newlines are CRLF, so the CR is ignored as the line isn't - really terminated until the LF comes. Treat a following CR - as end-of-headers as well.*/ - - if(('\r' == linep[0]) || - ('\n' == linep[0])) { - /* end of response-headers from the proxy */ - - if((407 == k->httpcode) && !data->state.authproblem) { - /* If we get a 407 response code with content length - when we have no auth problem, we must ignore the - whole response-body */ - ts->keepon = KEEPON_IGNORE; - - if(ts->cl) { - infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T - " bytes of response-body", ts->cl); - } - else if(ts->chunked_encoding) { - CHUNKcode r; - CURLcode extra; - - infof(data, "Ignore chunked response-body"); - - /* We set ignorebody true here since the chunked decoder - function will acknowledge that. Pay attention so that this is - cleared again when this function returns! */ - k->ignorebody = TRUE; - - if(linep[1] == '\n') - /* this can only be a LF if the letter at index 0 was a CR */ - linep++; - - /* now parse the chunked piece of data so that we can properly - tell when the stream ends */ - r = Curl_httpchunk_read(data, linep + 1, 1, &gotbytes, - &extra); - if(r == CHUNKE_STOP) { - /* we're done reading chunks! */ - infof(data, "chunk reading DONE"); - ts->keepon = KEEPON_DONE; - } - } - else { - /* without content-length or chunked encoding, we - can't keep the connection alive since the close is - the end signal so we bail out at once instead */ - DEBUGF(LOG_CF(data, cf, "CONNECT: no content-length or chunked")); - ts->keepon = KEEPON_DONE; - } - } - else { - ts->keepon = KEEPON_DONE; - } - - DEBUGASSERT(ts->keepon == KEEPON_IGNORE - || ts->keepon == KEEPON_DONE); - continue; - } - - result = on_resp_header(cf, data, ts, linep); - if(result) - return result; - - Curl_dyn_reset(&ts->rcvbuf); - } /* while there's buffer left and loop is requested */ - - if(error) - result = CURLE_RECV_ERROR; - *done = (ts->keepon == KEEPON_DONE); - if(!result && *done && data->info.httpproxycode/100 != 2) { - /* Deal with the possibly already received authenticate - headers. 'newurl' is set to a new URL if we must loop. */ - result = Curl_http_auth_act(data); - } - return result; -} - -#else /* USE_HYPER */ -/* The Hyper version of CONNECT */ -static CURLcode start_CONNECT(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h1_tunnel_state *ts) -{ - struct connectdata *conn = cf->conn; - struct hyptransfer *h = &data->hyp; - curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data); - hyper_io *io = NULL; - hyper_request *req = NULL; - hyper_headers *headers = NULL; - hyper_clientconn_options *options = NULL; - hyper_task *handshake = NULL; - hyper_task *task = NULL; /* for the handshake */ - hyper_clientconn *client = NULL; - hyper_task *sendtask = NULL; /* for the send */ - char *hostheader = NULL; /* for CONNECT */ - char *host = NULL; /* Host: */ - CURLcode result = CURLE_OUT_OF_MEMORY; - - io = hyper_io_new(); - if(!io) { - failf(data, "Couldn't create hyper IO"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - /* tell Hyper how to read/write network data */ - hyper_io_set_userdata(io, data); - hyper_io_set_read(io, Curl_hyper_recv); - hyper_io_set_write(io, Curl_hyper_send); - conn->sockfd = tunnelsocket; - - data->state.hconnect = TRUE; - - /* create an executor to poll futures */ - if(!h->exec) { - h->exec = hyper_executor_new(); - if(!h->exec) { - failf(data, "Couldn't create hyper executor"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - } - - options = hyper_clientconn_options_new(); - hyper_clientconn_options_set_preserve_header_case(options, 1); - hyper_clientconn_options_set_preserve_header_order(options, 1); - - if(!options) { - failf(data, "Couldn't create hyper client options"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - hyper_clientconn_options_exec(options, h->exec); - - /* "Both the `io` and the `options` are consumed in this function - call" */ - handshake = hyper_clientconn_handshake(io, options); - if(!handshake) { - failf(data, "Couldn't create hyper client handshake"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - io = NULL; - options = NULL; - - if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) { - failf(data, "Couldn't hyper_executor_push the handshake"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - handshake = NULL; /* ownership passed on */ - - task = hyper_executor_poll(h->exec); - if(!task) { - failf(data, "Couldn't hyper_executor_poll the handshake"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - client = hyper_task_value(task); - hyper_task_free(task); - req = hyper_request_new(); - if(!req) { - failf(data, "Couldn't hyper_request_new"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - if(hyper_request_set_method(req, (uint8_t *)"CONNECT", - strlen("CONNECT"))) { - failf(data, "error setting method"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - infof(data, "Establish HTTP proxy tunnel to %s:%d", - ts->hostname, ts->remote_port); - - /* This only happens if we've looped here due to authentication - reasons, and we don't really use the newly cloned URL here - then. Just free() it. */ - Curl_safefree(data->req.newurl); - - result = CONNECT_host(data, conn, ts->hostname, ts->remote_port, - &hostheader, &host); - if(result) - goto error; - - if(hyper_request_set_uri(req, (uint8_t *)hostheader, - strlen(hostheader))) { - failf(data, "error setting path"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - if(data->set.verbose) { - char *se = aprintf("CONNECT %s HTTP/1.1\r\n", hostheader); - if(!se) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - Curl_debug(data, CURLINFO_HEADER_OUT, se, strlen(se)); - free(se); - } - /* Setup the proxy-authorization header, if any */ - result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET, - hostheader, TRUE); - if(result) - goto error; - Curl_safefree(hostheader); - - /* default is 1.1 */ - if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) && - (HYPERE_OK != hyper_request_set_version(req, - HYPER_HTTP_VERSION_1_0))) { - failf(data, "error setting HTTP version"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - headers = hyper_request_headers(req); - if(!headers) { - failf(data, "hyper_request_headers"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - if(host) { - result = Curl_hyper_header(data, headers, host); - if(result) - goto error; - Curl_safefree(host); - } - - if(data->state.aptr.proxyuserpwd) { - result = Curl_hyper_header(data, headers, - data->state.aptr.proxyuserpwd); - if(result) - goto error; - } - - if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) && - data->set.str[STRING_USERAGENT]) { - struct dynbuf ua; - Curl_dyn_init(&ua, DYN_HTTP_REQUEST); - result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n", - data->set.str[STRING_USERAGENT]); - if(result) - goto error; - result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua)); - if(result) - goto error; - Curl_dyn_free(&ua); - } - - if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) { - result = Curl_hyper_header(data, headers, - "Proxy-Connection: Keep-Alive"); - if(result) - goto error; - } - - result = Curl_add_custom_headers(data, TRUE, headers); - if(result) - goto error; - - sendtask = hyper_clientconn_send(client, req); - if(!sendtask) { - failf(data, "hyper_clientconn_send"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) { - failf(data, "Couldn't hyper_executor_push the send"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - -error: - free(host); - free(hostheader); - if(io) - hyper_io_free(io); - if(options) - hyper_clientconn_options_free(options); - if(handshake) - hyper_task_free(handshake); - if(client) - hyper_clientconn_free(client); - return result; -} - -static CURLcode send_CONNECT(struct Curl_easy *data, - struct connectdata *conn, - struct h1_tunnel_state *ts, - bool *done) -{ - struct hyptransfer *h = &data->hyp; - hyper_task *task = NULL; - hyper_error *hypererr = NULL; - CURLcode result = CURLE_OK; - - (void)ts; - (void)conn; - do { - task = hyper_executor_poll(h->exec); - if(task) { - bool error = hyper_task_type(task) == HYPER_TASK_ERROR; - if(error) - hypererr = hyper_task_value(task); - hyper_task_free(task); - if(error) { - /* this could probably use a better error code? */ - result = CURLE_OUT_OF_MEMORY; - goto error; - } - } - } while(task); -error: - *done = (result == CURLE_OK); - if(hypererr) { - uint8_t errbuf[256]; - size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf)); - failf(data, "Hyper: %.*s", (int)errlen, errbuf); - hyper_error_free(hypererr); - } - return result; -} - -static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h1_tunnel_state *ts, - bool *done) -{ - struct hyptransfer *h = &data->hyp; - CURLcode result; - int didwhat; - - (void)ts; - *done = FALSE; - result = Curl_hyper_stream(data, cf->conn, &didwhat, done, - CURL_CSELECT_IN | CURL_CSELECT_OUT); - if(result || !*done) - return result; - if(h->exec) { - hyper_executor_free(h->exec); - h->exec = NULL; - } - if(h->read_waker) { - hyper_waker_free(h->read_waker); - h->read_waker = NULL; - } - if(h->write_waker) { - hyper_waker_free(h->write_waker); - h->write_waker = NULL; - } - return result; -} - -#endif /* USE_HYPER */ - -static CURLcode H1_CONNECT(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h1_tunnel_state *ts) -{ - struct connectdata *conn = cf->conn; - CURLcode result; - bool done; - - if(tunnel_is_established(ts)) - return CURLE_OK; - if(tunnel_is_failed(ts)) - return CURLE_RECV_ERROR; /* Need a cfilter close and new bootstrap */ - - do { - timediff_t check; - - check = Curl_timeleft(data, NULL, TRUE); - if(check <= 0) { - failf(data, "Proxy CONNECT aborted due to timeout"); - result = CURLE_OPERATION_TIMEDOUT; - goto out; - } - - switch(ts->tunnel_state) { - case H1_TUNNEL_INIT: - /* Prepare the CONNECT request and make a first attempt to send. */ - DEBUGF(LOG_CF(data, cf, "CONNECT start")); - result = start_CONNECT(cf, data, ts); - if(result) - goto out; - h1_tunnel_go_state(cf, ts, H1_TUNNEL_CONNECT, data); - /* FALLTHROUGH */ - - case H1_TUNNEL_CONNECT: - /* see that the request is completely sent */ - DEBUGF(LOG_CF(data, cf, "CONNECT send")); - result = send_CONNECT(data, cf->conn, ts, &done); - if(result || !done) - goto out; - h1_tunnel_go_state(cf, ts, H1_TUNNEL_RECEIVE, data); - /* FALLTHROUGH */ - - case H1_TUNNEL_RECEIVE: - /* read what is there */ - DEBUGF(LOG_CF(data, cf, "CONNECT receive")); - result = recv_CONNECT_resp(cf, data, ts, &done); - if(Curl_pgrsUpdate(data)) { - result = CURLE_ABORTED_BY_CALLBACK; - goto out; - } - /* error or not complete yet. return for more multi-multi */ - if(result || !done) - goto out; - /* got it */ - h1_tunnel_go_state(cf, ts, H1_TUNNEL_RESPONSE, data); - /* FALLTHROUGH */ - - case H1_TUNNEL_RESPONSE: - DEBUGF(LOG_CF(data, cf, "CONNECT response")); - if(data->req.newurl) { - /* not the "final" response, we need to do a follow up request. - * If the other side indicated a connection close, or if someone - * else told us to close this connection, do so now. - */ - if(ts->close_connection || conn->bits.close) { - /* Close this filter and the sub-chain, re-connect the - * sub-chain and continue. Closing this filter will - * reset our tunnel state. To avoid recursion, we return - * and expect to be called again. - */ - DEBUGF(LOG_CF(data, cf, "CONNECT need to close+open")); - infof(data, "Connect me again please"); - Curl_conn_cf_close(cf, data); - connkeep(conn, "HTTP proxy CONNECT"); - result = Curl_conn_cf_connect(cf->next, data, FALSE, &done); - goto out; - } - else { - /* staying on this connection, reset state */ - h1_tunnel_go_state(cf, ts, H1_TUNNEL_INIT, data); - } - } - break; - - default: - break; - } - - } while(data->req.newurl); - - DEBUGASSERT(ts->tunnel_state == H1_TUNNEL_RESPONSE); - if(data->info.httpproxycode/100 != 2) { - /* a non-2xx response and we have no next url to try. */ - Curl_safefree(data->req.newurl); - /* failure, close this connection to avoid re-use */ - streamclose(conn, "proxy CONNECT failure"); - h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); - failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode); - return CURLE_RECV_ERROR; - } - /* 2xx response, SUCCESS! */ - h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data); - infof(data, "CONNECT tunnel established, response %d", - data->info.httpproxycode); - result = CURLE_OK; - -out: - if(result) - h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); - return result; -} - -static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking, bool *done) -{ - CURLcode result; - struct h1_tunnel_state *ts = cf->ctx; - - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } - - DEBUGF(LOG_CF(data, cf, "connect")); - result = cf->next->cft->do_connect(cf->next, data, blocking, done); - if(result || !*done) - return result; - - *done = FALSE; - if(!ts) { - result = tunnel_init(&ts, data, cf->conn, cf->sockindex); - if(result) - return result; - cf->ctx = ts; - } - - /* TODO: can we do blocking? */ - /* We want "seamless" operations through HTTP proxy tunnel */ - - result = H1_CONNECT(cf, data, ts); - if(result) - goto out; - Curl_safefree(data->state.aptr.proxyuserpwd); - -out: - *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx); - if(*done) { - cf->connected = TRUE; - tunnel_free(cf, data); - } - return result; -} - -static int cf_h1_proxy_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) -{ - struct h1_tunnel_state *ts = cf->ctx; - int fds; - - fds = cf->next->cft->get_select_socks(cf->next, data, socks); - if(!fds && cf->next->connected && !cf->connected) { - /* If we are not connected, but the filter "below" is - * and not waiting on something, we are tunneling. */ - socks[0] = Curl_conn_cf_get_socket(cf, data); - if(ts) { - /* when we've sent a CONNECT to a proxy, we should rather either - wait for the socket to become readable to be able to get the - response headers or if we're still sending the request, wait - for write. */ - if(ts->CONNECT.sending == HTTPSEND_REQUEST) { - return GETSOCK_WRITESOCK(0); - } - return GETSOCK_READSOCK(0); - } - return GETSOCK_WRITESOCK(0); - } - return fds; -} - -static void cf_h1_proxy_destroy(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - DEBUGF(LOG_CF(data, cf, "destroy")); - tunnel_free(cf, data); -} - -static void cf_h1_proxy_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - DEBUGF(LOG_CF(data, cf, "close")); - cf->connected = FALSE; - if(cf->ctx) { - h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data); - } - if(cf->next) - cf->next->cft->do_close(cf->next, data); -} - - -struct Curl_cftype Curl_cft_h1_proxy = { - "H1-PROXY", - CF_TYPE_IP_CONNECT, - 0, - cf_h1_proxy_destroy, - cf_h1_proxy_connect, - cf_h1_proxy_close, - Curl_cf_http_proxy_get_host, - cf_h1_proxy_get_select_socks, - Curl_cf_def_data_pending, - Curl_cf_def_send, - Curl_cf_def_recv, - Curl_cf_def_cntrl, - Curl_cf_def_conn_is_alive, - Curl_cf_def_conn_keep_alive, - Curl_cf_def_query, -}; - -CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data) -{ - struct Curl_cfilter *cf; - CURLcode result; - - (void)data; - result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, NULL); - if(!result) - Curl_conn_cf_insert_after(cf_at, cf); - return result; -} - -#endif /* !CURL_DISABLE_PROXY && ! CURL_DISABLE_HTTP */ diff --git a/contrib/libs/curl/lib/cf-h1-proxy.h b/contrib/libs/curl/lib/cf-h1-proxy.h deleted file mode 100644 index ac5bed0b2b..0000000000 --- a/contrib/libs/curl/lib/cf-h1-proxy.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef HEADER_CURL_H1_PROXY_H -#define HEADER_CURL_H1_PROXY_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) - -CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf, - struct Curl_easy *data); - -extern struct Curl_cftype Curl_cft_h1_proxy; - - -#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ - -#endif /* HEADER_CURL_H1_PROXY_H */ diff --git a/contrib/libs/curl/lib/cf-h2-proxy.c b/contrib/libs/curl/lib/cf-h2-proxy.c deleted file mode 100644 index f6acfc5202..0000000000 --- a/contrib/libs/curl/lib/cf-h2-proxy.c +++ /dev/null @@ -1,1483 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#if defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY) - -#include <nghttp2/nghttp2.h> -#include "urldata.h" -#include "cfilters.h" -#include "connect.h" -#include "curl_log.h" -#include "bufq.h" -#include "dynbuf.h" -#include "dynhds.h" -#include "http1.h" -#include "http_proxy.h" -#include "multiif.h" -#include "cf-h2-proxy.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#define H2_CHUNK_SIZE (16*1024) - -#define PROXY_HTTP2_HUGE_WINDOW_SIZE (100 * 1024 * 1024) -#define H2_TUNNEL_WINDOW_SIZE (10 * 1024 * 1024) - -#define PROXY_H2_NW_RECV_CHUNKS (H2_TUNNEL_WINDOW_SIZE / H2_CHUNK_SIZE) -#define PROXY_H2_NW_SEND_CHUNKS 1 - -#define H2_TUNNEL_RECV_CHUNKS (H2_TUNNEL_WINDOW_SIZE / H2_CHUNK_SIZE) -#define H2_TUNNEL_SEND_CHUNKS ((128 * 1024) / H2_CHUNK_SIZE) - - -typedef enum { - H2_TUNNEL_INIT, /* init/default/no tunnel state */ - H2_TUNNEL_CONNECT, /* CONNECT request is being send */ - H2_TUNNEL_RESPONSE, /* CONNECT response received completely */ - H2_TUNNEL_ESTABLISHED, - H2_TUNNEL_FAILED -} h2_tunnel_state; - -struct tunnel_stream { - struct http_resp *resp; - struct bufq recvbuf; - struct bufq sendbuf; - char *authority; - int32_t stream_id; - uint32_t error; - size_t upload_blocked_len; - h2_tunnel_state state; - BIT(has_final_response); - BIT(closed); - BIT(reset); -}; - -static CURLcode tunnel_stream_init(struct Curl_cfilter *cf, - struct tunnel_stream *ts) -{ - const char *hostname; - int port; - bool ipv6_ip = cf->conn->bits.ipv6_ip; - - ts->state = H2_TUNNEL_INIT; - ts->stream_id = -1; - Curl_bufq_init2(&ts->recvbuf, H2_CHUNK_SIZE, H2_TUNNEL_RECV_CHUNKS, - BUFQ_OPT_SOFT_LIMIT); - Curl_bufq_init(&ts->sendbuf, H2_CHUNK_SIZE, H2_TUNNEL_SEND_CHUNKS); - - if(cf->conn->bits.conn_to_host) - hostname = cf->conn->conn_to_host.name; - else if(cf->sockindex == SECONDARYSOCKET) - hostname = cf->conn->secondaryhostname; - else - hostname = cf->conn->host.name; - - if(cf->sockindex == SECONDARYSOCKET) - port = cf->conn->secondary_port; - else if(cf->conn->bits.conn_to_port) - port = cf->conn->conn_to_port; - else - port = cf->conn->remote_port; - - if(hostname != cf->conn->host.name) - ipv6_ip = (strchr(hostname, ':') != NULL); - - ts->authority = /* host:port with IPv6 support */ - aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"", port); - if(!ts->authority) - return CURLE_OUT_OF_MEMORY; - - return CURLE_OK; -} - -static void tunnel_stream_clear(struct tunnel_stream *ts) -{ - Curl_http_resp_free(ts->resp); - Curl_bufq_free(&ts->recvbuf); - Curl_bufq_free(&ts->sendbuf); - Curl_safefree(ts->authority); - memset(ts, 0, sizeof(*ts)); - ts->state = H2_TUNNEL_INIT; -} - -static void h2_tunnel_go_state(struct Curl_cfilter *cf, - struct tunnel_stream *ts, - h2_tunnel_state new_state, - struct Curl_easy *data) -{ - (void)cf; - - if(ts->state == new_state) - return; - /* leaving this one */ - switch(ts->state) { - case H2_TUNNEL_CONNECT: - data->req.ignorebody = FALSE; - break; - default: - break; - } - /* entering this one */ - switch(new_state) { - case H2_TUNNEL_INIT: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'init'")); - tunnel_stream_clear(ts); - break; - - case H2_TUNNEL_CONNECT: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'connect'")); - ts->state = H2_TUNNEL_CONNECT; - break; - - case H2_TUNNEL_RESPONSE: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'response'")); - ts->state = H2_TUNNEL_RESPONSE; - break; - - case H2_TUNNEL_ESTABLISHED: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'established'")); - infof(data, "CONNECT phase completed"); - data->state.authproxy.done = TRUE; - data->state.authproxy.multipass = FALSE; - /* FALLTHROUGH */ - case H2_TUNNEL_FAILED: - if(new_state == H2_TUNNEL_FAILED) - DEBUGF(LOG_CF(data, cf, "new tunnel state 'failed'")); - ts->state = new_state; - /* If a proxy-authorization header was used for the proxy, then we should - make sure that it isn't accidentally used for the document request - after we've connected. So let's free and clear it here. */ - Curl_safefree(data->state.aptr.proxyuserpwd); - break; - } -} - -struct cf_h2_proxy_ctx { - nghttp2_session *h2; - /* The easy handle used in the current filter call, cleared at return */ - struct cf_call_data call_data; - - struct bufq inbufq; /* network receive buffer */ - struct bufq outbufq; /* network send buffer */ - - struct tunnel_stream tunnel; /* our tunnel CONNECT stream */ - int32_t goaway_error; - int32_t last_stream_id; - BIT(conn_closed); - BIT(goaway); - BIT(nw_out_blocked); -}; - -/* How to access `call_data` from a cf_h2 filter */ -#undef CF_CTX_CALL_DATA -#define CF_CTX_CALL_DATA(cf) \ - ((struct cf_h2_proxy_ctx *)(cf)->ctx)->call_data - -static void cf_h2_proxy_ctx_clear(struct cf_h2_proxy_ctx *ctx) -{ - struct cf_call_data save = ctx->call_data; - - if(ctx->h2) { - nghttp2_session_del(ctx->h2); - } - Curl_bufq_free(&ctx->inbufq); - Curl_bufq_free(&ctx->outbufq); - tunnel_stream_clear(&ctx->tunnel); - memset(ctx, 0, sizeof(*ctx)); - ctx->call_data = save; -} - -static void cf_h2_proxy_ctx_free(struct cf_h2_proxy_ctx *ctx) -{ - if(ctx) { - cf_h2_proxy_ctx_clear(ctx); - free(ctx); - } -} - -static void drain_tunnel(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct tunnel_stream *tunnel) -{ - unsigned char bits; - - (void)cf; - bits = CURL_CSELECT_IN; - if(!tunnel->closed && !tunnel->reset && tunnel->upload_blocked_len) - bits |= CURL_CSELECT_OUT; - if(data->state.dselect_bits != bits) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] DRAIN dselect_bits=%x", - tunnel->stream_id, bits)); - data->state.dselect_bits = bits; - Curl_expire(data, 0, EXPIRE_RUN_NOW); - } -} - -static ssize_t proxy_nw_in_reader(void *reader_ctx, - unsigned char *buf, size_t buflen, - CURLcode *err) -{ - struct Curl_cfilter *cf = reader_ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nread; - - nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, buflen, err); - DEBUGF(LOG_CF(data, cf, "nw_in_reader(len=%zu) -> %zd, %d", - buflen, nread, *err)); - return nread; -} - -static ssize_t proxy_h2_nw_out_writer(void *writer_ctx, - const unsigned char *buf, size_t buflen, - CURLcode *err) -{ - struct Curl_cfilter *cf = writer_ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nwritten; - - nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, err); - DEBUGF(LOG_CF(data, cf, "nw_out_writer(len=%zu) -> %zd, %d", - buflen, nwritten, *err)); - return nwritten; -} - -static int proxy_h2_client_new(struct Curl_cfilter *cf, - nghttp2_session_callbacks *cbs) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - nghttp2_option *o; - - int rc = nghttp2_option_new(&o); - if(rc) - return rc; - /* We handle window updates ourself to enforce buffer limits */ - nghttp2_option_set_no_auto_window_update(o, 1); -#if NGHTTP2_VERSION_NUM >= 0x013200 - /* with 1.50.0 */ - /* turn off RFC 9113 leading and trailing white spaces validation against - HTTP field value. */ - nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1); -#endif - rc = nghttp2_session_client_new2(&ctx->h2, cbs, cf, o); - nghttp2_option_del(o); - return rc; -} - -static ssize_t on_session_send(nghttp2_session *h2, - const uint8_t *buf, size_t blen, - int flags, void *userp); -static int proxy_h2_on_frame_recv(nghttp2_session *session, - const nghttp2_frame *frame, - void *userp); -static int proxy_h2_on_stream_close(nghttp2_session *session, - int32_t stream_id, - uint32_t error_code, void *userp); -static int proxy_h2_on_header(nghttp2_session *session, - const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - uint8_t flags, - void *userp); -static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - const uint8_t *mem, size_t len, void *userp); - -/* - * Initialize the cfilter context - */ -static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OUT_OF_MEMORY; - nghttp2_session_callbacks *cbs = NULL; - int rc; - - DEBUGASSERT(!ctx->h2); - memset(&ctx->tunnel, 0, sizeof(ctx->tunnel)); - - Curl_bufq_init(&ctx->inbufq, H2_CHUNK_SIZE, PROXY_H2_NW_RECV_CHUNKS); - Curl_bufq_init(&ctx->outbufq, H2_CHUNK_SIZE, PROXY_H2_NW_SEND_CHUNKS); - - if(tunnel_stream_init(cf, &ctx->tunnel)) - goto out; - - rc = nghttp2_session_callbacks_new(&cbs); - if(rc) { - failf(data, "Couldn't initialize nghttp2 callbacks"); - goto out; - } - - nghttp2_session_callbacks_set_send_callback(cbs, on_session_send); - nghttp2_session_callbacks_set_on_frame_recv_callback( - cbs, proxy_h2_on_frame_recv); - nghttp2_session_callbacks_set_on_data_chunk_recv_callback( - cbs, tunnel_recv_callback); - nghttp2_session_callbacks_set_on_stream_close_callback( - cbs, proxy_h2_on_stream_close); - nghttp2_session_callbacks_set_on_header_callback(cbs, proxy_h2_on_header); - - /* The nghttp2 session is not yet setup, do it */ - rc = proxy_h2_client_new(cf, cbs); - if(rc) { - failf(data, "Couldn't initialize nghttp2"); - goto out; - } - - { - nghttp2_settings_entry iv[3]; - - iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[0].value = Curl_multi_max_concurrent_streams(data->multi); - iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[1].value = H2_TUNNEL_WINDOW_SIZE; - iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; - iv[2].value = 0; - rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, iv, 3); - if(rc) { - failf(data, "nghttp2_submit_settings() failed: %s(%d)", - nghttp2_strerror(rc), rc); - result = CURLE_HTTP2; - goto out; - } - } - - rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0, - PROXY_HTTP2_HUGE_WINDOW_SIZE); - if(rc) { - failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", - nghttp2_strerror(rc), rc); - result = CURLE_HTTP2; - goto out; - } - - - /* all set, traffic will be send on connect */ - result = CURLE_OK; - -out: - if(cbs) - nghttp2_session_callbacks_del(cbs); - DEBUGF(LOG_CF(data, cf, "init proxy ctx -> %d", result)); - return result; -} - -static int should_close_session(struct cf_h2_proxy_ctx *ctx) -{ - return !nghttp2_session_want_read(ctx->h2) && - !nghttp2_session_want_write(ctx->h2); -} - -static CURLcode proxy_h2_nw_out_flush(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - ssize_t nwritten; - CURLcode result; - - (void)data; - if(Curl_bufq_is_empty(&ctx->outbufq)) - return CURLE_OK; - - nwritten = Curl_bufq_pass(&ctx->outbufq, proxy_h2_nw_out_writer, cf, - &result); - if(nwritten < 0) { - if(result == CURLE_AGAIN) { - DEBUGF(LOG_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN", - Curl_bufq_len(&ctx->outbufq))); - ctx->nw_out_blocked = 1; - } - return result; - } - DEBUGF(LOG_CF(data, cf, "nw send buffer flushed")); - return Curl_bufq_is_empty(&ctx->outbufq)? CURLE_OK: CURLE_AGAIN; -} - -/* - * Processes pending input left in network input buffer. - * This function returns 0 if it succeeds, or -1 and error code will - * be assigned to *err. - */ -static int proxy_h2_process_pending_input(struct Curl_cfilter *cf, - struct Curl_easy *data, - CURLcode *err) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - const unsigned char *buf; - size_t blen; - ssize_t rv; - - while(Curl_bufq_peek(&ctx->inbufq, &buf, &blen)) { - - rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)buf, blen); - DEBUGF(LOG_CF(data, cf, - "fed %zu bytes from nw to nghttp2 -> %zd", blen, rv)); - if(rv < 0) { - failf(data, - "process_pending_input: nghttp2_session_mem_recv() returned " - "%zd:%s", rv, nghttp2_strerror((int)rv)); - *err = CURLE_RECV_ERROR; - return -1; - } - Curl_bufq_skip(&ctx->inbufq, (size_t)rv); - if(Curl_bufq_is_empty(&ctx->inbufq)) { - DEBUGF(LOG_CF(data, cf, "all data in connection buffer processed")); - break; - } - else { - DEBUGF(LOG_CF(data, cf, "process_pending_input: %zu bytes left " - "in connection buffer", Curl_bufq_len(&ctx->inbufq))); - } - } - - return 0; -} - -static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - ssize_t nread; - - /* Process network input buffer fist */ - if(!Curl_bufq_is_empty(&ctx->inbufq)) { - DEBUGF(LOG_CF(data, cf, "Process %zu bytes in connection buffer", - Curl_bufq_len(&ctx->inbufq))); - if(proxy_h2_process_pending_input(cf, data, &result) < 0) - return result; - } - - /* Receive data from the "lower" filters, e.g. network until - * it is time to stop or we have enough data for this stream */ - while(!ctx->conn_closed && /* not closed the connection */ - !ctx->tunnel.closed && /* nor the tunnel */ - Curl_bufq_is_empty(&ctx->inbufq) && /* and we consumed our input */ - !Curl_bufq_is_full(&ctx->tunnel.recvbuf)) { - - nread = Curl_bufq_slurp(&ctx->inbufq, proxy_nw_in_reader, cf, &result); - DEBUGF(LOG_CF(data, cf, "read %zu bytes nw data -> %zd, %d", - Curl_bufq_len(&ctx->inbufq), nread, result)); - if(nread < 0) { - if(result != CURLE_AGAIN) { - failf(data, "Failed receiving HTTP2 data"); - return result; - } - break; - } - else if(nread == 0) { - ctx->conn_closed = TRUE; - break; - } - - if(proxy_h2_process_pending_input(cf, data, &result)) - return result; - } - - if(ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) { - connclose(cf->conn, "GOAWAY received"); - } - - return CURLE_OK; -} - -static CURLcode proxy_h2_progress_egress(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - int rv = 0; - - ctx->nw_out_blocked = 0; - while(!rv && !ctx->nw_out_blocked && nghttp2_session_want_write(ctx->h2)) - rv = nghttp2_session_send(ctx->h2); - - if(nghttp2_is_fatal(rv)) { - DEBUGF(LOG_CF(data, cf, "nghttp2_session_send error (%s)%d", - nghttp2_strerror(rv), rv)); - return CURLE_SEND_ERROR; - } - return proxy_h2_nw_out_flush(cf, data); -} - -static ssize_t on_session_send(nghttp2_session *h2, - const uint8_t *buf, size_t blen, int flags, - void *userp) -{ - struct Curl_cfilter *cf = userp; - struct cf_h2_proxy_ctx *ctx = cf->ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nwritten; - CURLcode result = CURLE_OK; - - (void)h2; - (void)flags; - DEBUGASSERT(data); - - nwritten = Curl_bufq_write_pass(&ctx->outbufq, buf, blen, - proxy_h2_nw_out_writer, cf, &result); - if(nwritten < 0) { - if(result == CURLE_AGAIN) { - return NGHTTP2_ERR_WOULDBLOCK; - } - failf(data, "Failed sending HTTP2 data"); - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - if(!nwritten) - return NGHTTP2_ERR_WOULDBLOCK; - - return nwritten; -} - -static int proxy_h2_on_frame_recv(nghttp2_session *session, - const nghttp2_frame *frame, - void *userp) -{ - struct Curl_cfilter *cf = userp; - struct cf_h2_proxy_ctx *ctx = cf->ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - int32_t stream_id = frame->hd.stream_id; - - (void)session; - DEBUGASSERT(data); - if(!stream_id) { - /* stream ID zero is for connection-oriented stuff */ - DEBUGASSERT(data); - switch(frame->hd.type) { - case NGHTTP2_SETTINGS: - /* we do not do anything with this for now */ - break; - case NGHTTP2_GOAWAY: - infof(data, "recveived GOAWAY, error=%d, last_stream=%u", - frame->goaway.error_code, frame->goaway.last_stream_id); - ctx->goaway = TRUE; - break; - case NGHTTP2_WINDOW_UPDATE: - DEBUGF(LOG_CF(data, cf, "recv frame WINDOW_UPDATE")); - break; - default: - DEBUGF(LOG_CF(data, cf, "recv frame %x on 0", frame->hd.type)); - } - return 0; - } - - if(stream_id != ctx->tunnel.stream_id) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] rcvd FRAME not for tunnel", - stream_id)); - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - switch(frame->hd.type) { - case NGHTTP2_DATA: - /* If body started on this stream, then receiving DATA is illegal. */ - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv frame DATA", stream_id)); - break; - case NGHTTP2_HEADERS: - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv frame HEADERS", stream_id)); - - /* nghttp2 guarantees that :status is received, and we store it to - stream->status_code. Fuzzing has proven this can still be reached - without status code having been set. */ - if(!ctx->tunnel.resp) - return NGHTTP2_ERR_CALLBACK_FAILURE; - /* Only final status code signals the end of header */ - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] got http status: %d", - stream_id, ctx->tunnel.resp->status)); - if(!ctx->tunnel.has_final_response) { - if(ctx->tunnel.resp->status / 100 != 1) { - ctx->tunnel.has_final_response = TRUE; - } - } - break; - case NGHTTP2_PUSH_PROMISE: - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv PUSH_PROMISE", stream_id)); - return NGHTTP2_ERR_CALLBACK_FAILURE; - case NGHTTP2_RST_STREAM: - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv RST", stream_id)); - ctx->tunnel.reset = TRUE; - break; - case NGHTTP2_WINDOW_UPDATE: - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv WINDOW_UPDATE", stream_id)); - if((data->req.keepon & KEEP_SEND_HOLD) && - (data->req.keepon & KEEP_SEND)) { - data->req.keepon &= ~KEEP_SEND_HOLD; - Curl_expire(data, 0, EXPIRE_RUN_NOW); - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] unpausing after win update", - stream_id)); - } - break; - default: - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv frame %x", - stream_id, frame->hd.type)); - break; - } - return 0; -} - -static int proxy_h2_on_header(nghttp2_session *session, - const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - uint8_t flags, - void *userp) -{ - struct Curl_cfilter *cf = userp; - struct cf_h2_proxy_ctx *ctx = cf->ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - int32_t stream_id = frame->hd.stream_id; - CURLcode result; - - (void)flags; - (void)data; - (void)session; - DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ - if(stream_id != ctx->tunnel.stream_id) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] header for non-tunnel stream: " - "%.*s: %.*s", stream_id, - (int)namelen, name, - (int)valuelen, value)); - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - if(frame->hd.type == NGHTTP2_PUSH_PROMISE) - return NGHTTP2_ERR_CALLBACK_FAILURE; - - if(ctx->tunnel.has_final_response) { - /* we do not do anything with trailers for tunnel streams */ - return 0; - } - - if(namelen == sizeof(HTTP_PSEUDO_STATUS) - 1 && - memcmp(HTTP_PSEUDO_STATUS, name, namelen) == 0) { - int http_status; - struct http_resp *resp; - - /* status: always comes first, we might get more than one response, - * link the previous ones for keepers */ - result = Curl_http_decode_status(&http_status, - (const char *)value, valuelen); - if(result) - return NGHTTP2_ERR_CALLBACK_FAILURE; - result = Curl_http_resp_make(&resp, http_status, NULL); - if(result) - return NGHTTP2_ERR_CALLBACK_FAILURE; - resp->prev = ctx->tunnel.resp; - ctx->tunnel.resp = resp; - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] status: HTTP/2 %03d", - stream_id, ctx->tunnel.resp->status)); - return 0; - } - - if(!ctx->tunnel.resp) - return NGHTTP2_ERR_CALLBACK_FAILURE; - - result = Curl_dynhds_add(&ctx->tunnel.resp->headers, - (const char *)name, namelen, - (const char *)value, valuelen); - if(result) - return NGHTTP2_ERR_CALLBACK_FAILURE; - - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] header: %.*s: %.*s", - stream_id, - (int)namelen, name, - (int)valuelen, value)); - - return 0; /* 0 is successful */ -} - -static ssize_t tunnel_send_callback(nghttp2_session *session, - int32_t stream_id, - uint8_t *buf, size_t length, - uint32_t *data_flags, - nghttp2_data_source *source, - void *userp) -{ - struct Curl_cfilter *cf = userp; - struct cf_h2_proxy_ctx *ctx = cf->ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - struct tunnel_stream *ts; - CURLcode result; - ssize_t nread; - - (void)source; - (void)data; - (void)ctx; - - if(!stream_id) - return NGHTTP2_ERR_INVALID_ARGUMENT; - - ts = nghttp2_session_get_stream_user_data(session, stream_id); - if(!ts) - return NGHTTP2_ERR_CALLBACK_FAILURE; - DEBUGASSERT(ts == &ctx->tunnel); - - nread = Curl_bufq_read(&ts->sendbuf, buf, length, &result); - if(nread < 0) { - if(result != CURLE_AGAIN) - return NGHTTP2_ERR_CALLBACK_FAILURE; - return NGHTTP2_ERR_DEFERRED; - } - if(ts->closed && Curl_bufq_is_empty(&ts->sendbuf)) - *data_flags = NGHTTP2_DATA_FLAG_EOF; - - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] tunnel_send_callback -> %zd", - ts->stream_id, nread)); - return nread; -} - -static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - const uint8_t *mem, size_t len, void *userp) -{ - struct Curl_cfilter *cf = userp; - struct cf_h2_proxy_ctx *ctx = cf->ctx; - ssize_t nwritten; - CURLcode result; - - (void)flags; - (void)session; - DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ - - if(stream_id != ctx->tunnel.stream_id) - return NGHTTP2_ERR_CALLBACK_FAILURE; - - nwritten = Curl_bufq_write(&ctx->tunnel.recvbuf, mem, len, &result); - if(nwritten < 0) { - if(result != CURLE_AGAIN) - return NGHTTP2_ERR_CALLBACK_FAILURE; - nwritten = 0; - } - DEBUGASSERT((size_t)nwritten == len); - return 0; -} - -static int proxy_h2_on_stream_close(nghttp2_session *session, - int32_t stream_id, - uint32_t error_code, void *userp) -{ - struct Curl_cfilter *cf = userp; - struct cf_h2_proxy_ctx *ctx = cf->ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - - (void)session; - (void)data; - - if(stream_id != ctx->tunnel.stream_id) - return 0; - - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] proxy_h2_on_stream_close, %s (err %d)", - stream_id, nghttp2_http2_strerror(error_code), error_code)); - ctx->tunnel.closed = TRUE; - ctx->tunnel.error = error_code; - - return 0; -} - -static CURLcode proxy_h2_submit(int32_t *pstream_id, - struct Curl_cfilter *cf, - struct Curl_easy *data, - nghttp2_session *h2, - struct httpreq *req, - const nghttp2_priority_spec *pri_spec, - void *stream_user_data, - nghttp2_data_source_read_callback read_callback, - void *read_ctx) -{ - struct dynhds h2_headers; - nghttp2_nv *nva = NULL; - unsigned int i; - int32_t stream_id = -1; - size_t nheader; - CURLcode result; - - (void)cf; - Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); - result = Curl_http_req_to_h2(&h2_headers, req, data); - if(result) - goto out; - - nheader = Curl_dynhds_count(&h2_headers); - nva = malloc(sizeof(nghttp2_nv) * nheader); - if(!nva) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - for(i = 0; i < nheader; ++i) { - struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); - nva[i].name = (unsigned char *)e->name; - nva[i].namelen = e->namelen; - nva[i].value = (unsigned char *)e->value; - nva[i].valuelen = e->valuelen; - nva[i].flags = NGHTTP2_NV_FLAG_NONE; - } - - if(read_callback) { - nghttp2_data_provider data_prd; - - data_prd.read_callback = read_callback; - data_prd.source.ptr = read_ctx; - stream_id = nghttp2_submit_request(h2, pri_spec, nva, nheader, - &data_prd, stream_user_data); - } - else { - stream_id = nghttp2_submit_request(h2, pri_spec, nva, nheader, - NULL, stream_user_data); - } - - if(stream_id < 0) { - failf(data, "nghttp2_session_upgrade2() failed: %s(%d)", - nghttp2_strerror(stream_id), stream_id); - result = CURLE_SEND_ERROR; - goto out; - } - result = CURLE_OK; - -out: - free(nva); - Curl_dynhds_free(&h2_headers); - *pstream_id = stream_id; - return result; -} - -static CURLcode submit_CONNECT(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct tunnel_stream *ts) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - CURLcode result; - struct httpreq *req = NULL; - - infof(data, "Establish HTTP/2 proxy tunnel to %s", ts->authority); - - result = Curl_http_req_make(&req, "CONNECT", sizeof("CONNECT")-1, - NULL, 0, ts->authority, strlen(ts->authority), - NULL, 0); - if(result) - goto out; - - /* Setup the proxy-authorization header, if any */ - result = Curl_http_output_auth(data, cf->conn, req->method, HTTPREQ_GET, - req->authority, TRUE); - if(result) - goto out; - - if(data->state.aptr.proxyuserpwd) { - result = Curl_dynhds_h1_cadd_line(&req->headers, - data->state.aptr.proxyuserpwd); - if(result) - goto out; - } - - if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent")) - && data->set.str[STRING_USERAGENT]) { - result = Curl_dynhds_cadd(&req->headers, "User-Agent", - data->set.str[STRING_USERAGENT]); - if(result) - goto out; - } - - result = Curl_dynhds_add_custom(data, TRUE, &req->headers); - if(result) - goto out; - - result = proxy_h2_submit(&ts->stream_id, cf, data, ctx->h2, req, - NULL, ts, tunnel_send_callback, cf); - if(result) { - DEBUGF(LOG_CF(data, cf, "send: nghttp2_submit_request error (%s)%u", - nghttp2_strerror(ts->stream_id), ts->stream_id)); - } - -out: - if(req) - Curl_http_req_free(req); - if(result) - failf(data, "Failed sending CONNECT to proxy"); - return result; -} - -static CURLcode inspect_response(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct tunnel_stream *ts) -{ - CURLcode result = CURLE_OK; - struct dynhds_entry *auth_reply = NULL; - (void)cf; - - DEBUGASSERT(ts->resp); - if(ts->resp->status/100 == 2) { - infof(data, "CONNECT tunnel established, response %d", ts->resp->status); - h2_tunnel_go_state(cf, ts, H2_TUNNEL_ESTABLISHED, data); - return CURLE_OK; - } - - if(ts->resp->status == 401) { - auth_reply = Curl_dynhds_cget(&ts->resp->headers, "WWW-Authenticate"); - } - else if(ts->resp->status == 407) { - auth_reply = Curl_dynhds_cget(&ts->resp->headers, "Proxy-Authenticate"); - } - - if(auth_reply) { - DEBUGF(LOG_CF(data, cf, "CONNECT: fwd auth header '%s'", - auth_reply->value)); - result = Curl_http_input_auth(data, ts->resp->status == 407, - auth_reply->value); - if(result) - return result; - if(data->req.newurl) { - /* Inidicator that we should try again */ - Curl_safefree(data->req.newurl); - h2_tunnel_go_state(cf, ts, H2_TUNNEL_INIT, data); - return CURLE_OK; - } - } - - /* Seems to have failed */ - return CURLE_RECV_ERROR; -} - -static CURLcode H2_CONNECT(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct tunnel_stream *ts) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - - DEBUGASSERT(ts); - DEBUGASSERT(ts->authority); - do { - switch(ts->state) { - case H2_TUNNEL_INIT: - /* Prepare the CONNECT request and make a first attempt to send. */ - DEBUGF(LOG_CF(data, cf, "CONNECT start for %s", ts->authority)); - result = submit_CONNECT(cf, data, ts); - if(result) - goto out; - h2_tunnel_go_state(cf, ts, H2_TUNNEL_CONNECT, data); - /* FALLTHROUGH */ - - case H2_TUNNEL_CONNECT: - /* see that the request is completely sent */ - result = proxy_h2_progress_ingress(cf, data); - if(!result) - result = proxy_h2_progress_egress(cf, data); - if(result && result != CURLE_AGAIN) { - h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data); - break; - } - - if(ts->has_final_response) { - h2_tunnel_go_state(cf, ts, H2_TUNNEL_RESPONSE, data); - } - else { - result = CURLE_OK; - goto out; - } - /* FALLTHROUGH */ - - case H2_TUNNEL_RESPONSE: - DEBUGASSERT(ts->has_final_response); - result = inspect_response(cf, data, ts); - if(result) - goto out; - break; - - case H2_TUNNEL_ESTABLISHED: - return CURLE_OK; - - case H2_TUNNEL_FAILED: - return CURLE_RECV_ERROR; - - default: - break; - } - - } while(ts->state == H2_TUNNEL_INIT); - -out: - if(result || ctx->tunnel.closed) - h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data); - return result; -} - -static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking, bool *done) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - struct cf_call_data save; - timediff_t check; - struct tunnel_stream *ts = &ctx->tunnel; - - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } - - /* Connect the lower filters first */ - if(!cf->next->connected) { - result = Curl_conn_cf_connect(cf->next, data, blocking, done); - if(result || !*done) - return result; - } - - *done = FALSE; - - CF_DATA_SAVE(save, cf, data); - if(!ctx->h2) { - result = cf_h2_proxy_ctx_init(cf, data); - if(result) - goto out; - } - DEBUGASSERT(ts->authority); - - check = Curl_timeleft(data, NULL, TRUE); - if(check <= 0) { - failf(data, "Proxy CONNECT aborted due to timeout"); - result = CURLE_OPERATION_TIMEDOUT; - goto out; - } - - /* for the secondary socket (FTP), use the "connect to host" - * but ignore the "connect to port" (use the secondary port) - */ - result = H2_CONNECT(cf, data, ts); - -out: - *done = (result == CURLE_OK) && (ts->state == H2_TUNNEL_ESTABLISHED); - cf->connected = *done; - CF_DATA_RESTORE(cf, save); - return result; -} - -static void cf_h2_proxy_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - - if(ctx) { - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - cf_h2_proxy_ctx_clear(ctx); - CF_DATA_RESTORE(cf, save); - } -} - -static void cf_h2_proxy_destroy(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - - (void)data; - if(ctx) { - cf_h2_proxy_ctx_free(ctx); - cf->ctx = NULL; - } -} - -static bool cf_h2_proxy_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - if((ctx && !Curl_bufq_is_empty(&ctx->inbufq)) || - (ctx && ctx->tunnel.state == H2_TUNNEL_ESTABLISHED && - !Curl_bufq_is_empty(&ctx->tunnel.recvbuf))) - return TRUE; - return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE; -} - -static int cf_h2_proxy_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *sock) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - int bitmap = GETSOCK_BLANK; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - sock[0] = Curl_conn_cf_get_socket(cf, data); - bitmap |= GETSOCK_READSOCK(0); - - /* HTTP/2 layer wants to send data) AND there's a window to send data in */ - if(nghttp2_session_want_write(ctx->h2) && - nghttp2_session_get_remote_window_size(ctx->h2)) - bitmap |= GETSOCK_WRITESOCK(0); - - CF_DATA_RESTORE(cf, save); - return bitmap; -} - -static ssize_t h2_handle_tunnel_close(struct Curl_cfilter *cf, - struct Curl_easy *data, - CURLcode *err) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - ssize_t rv = 0; - - if(ctx->tunnel.error == NGHTTP2_REFUSED_STREAM) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] REFUSED_STREAM, try again on a new " - "connection", ctx->tunnel.stream_id)); - connclose(cf->conn, "REFUSED_STREAM"); /* don't use this anymore */ - *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ - return -1; - } - else if(ctx->tunnel.error != NGHTTP2_NO_ERROR) { - failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)", - ctx->tunnel.stream_id, nghttp2_http2_strerror(ctx->tunnel.error), - ctx->tunnel.error); - *err = CURLE_HTTP2_STREAM; - return -1; - } - else if(ctx->tunnel.reset) { - failf(data, "HTTP/2 stream %u was reset", ctx->tunnel.stream_id); - *err = CURLE_RECV_ERROR; - return -1; - } - - *err = CURLE_OK; - rv = 0; - DEBUGF(LOG_CF(data, cf, "handle_tunnel_close -> %zd, %d", rv, *err)); - return rv; -} - -static ssize_t tunnel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - ssize_t nread = -1; - - *err = CURLE_AGAIN; - if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf)) { - nread = Curl_bufq_read(&ctx->tunnel.recvbuf, - (unsigned char *)buf, len, err); - if(nread < 0) - goto out; - DEBUGASSERT(nread > 0); - } - - if(nread < 0) { - if(ctx->tunnel.closed) { - nread = h2_handle_tunnel_close(cf, data, err); - } - else if(ctx->tunnel.reset || - (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) || - (ctx->goaway && ctx->last_stream_id < ctx->tunnel.stream_id)) { - *err = CURLE_RECV_ERROR; - nread = -1; - } - } - else if(nread == 0) { - *err = CURLE_AGAIN; - nread = -1; - } - -out: - DEBUGF(LOG_CF(data, cf, "tunnel_recv(len=%zu) -> %zd, %d", - len, nread, *err)); - return nread; -} - -static ssize_t cf_h2_proxy_recv(struct Curl_cfilter *cf, - struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - ssize_t nread = -1; - struct cf_call_data save; - CURLcode result; - - if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) { - *err = CURLE_RECV_ERROR; - return -1; - } - CF_DATA_SAVE(save, cf, data); - - if(Curl_bufq_is_empty(&ctx->tunnel.recvbuf)) { - *err = proxy_h2_progress_ingress(cf, data); - if(*err) - goto out; - } - - nread = tunnel_recv(cf, data, buf, len, err); - - if(nread > 0) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] increase window by %zd", - ctx->tunnel.stream_id, nread)); - nghttp2_session_consume(ctx->h2, ctx->tunnel.stream_id, (size_t)nread); - } - - result = proxy_h2_progress_egress(cf, data); - if(result && result != CURLE_AGAIN) { - *err = result; - nread = -1; - } - -out: - if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) && - (nread >= 0 || *err == CURLE_AGAIN)) { - /* data pending and no fatal error to report. Need to trigger - * draining to avoid stalling when no socket events happen. */ - drain_tunnel(cf, data, &ctx->tunnel); - } - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] cf_recv(len=%zu) -> %zd %d", - ctx->tunnel.stream_id, len, nread, *err)); - CF_DATA_RESTORE(cf, save); - return nread; -} - -static ssize_t cf_h2_proxy_send(struct Curl_cfilter *cf, - struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - struct cf_call_data save; - int rv; - ssize_t nwritten; - CURLcode result; - int blocked = 0; - - if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) { - *err = CURLE_SEND_ERROR; - return -1; - } - CF_DATA_SAVE(save, cf, data); - - if(ctx->tunnel.closed) { - nwritten = -1; - *err = CURLE_SEND_ERROR; - goto out; - } - else if(ctx->tunnel.upload_blocked_len) { - /* the data in `buf` has alread been submitted or added to the - * buffers, but have been EAGAINed on the last invocation. */ - DEBUGASSERT(len >= ctx->tunnel.upload_blocked_len); - if(len < ctx->tunnel.upload_blocked_len) { - /* Did we get called again with a smaller `len`? This should not - * happend. We are not prepared to handle that. */ - failf(data, "HTTP/2 proxy, send again with decreased length"); - *err = CURLE_HTTP2; - nwritten = -1; - goto out; - } - nwritten = (ssize_t)ctx->tunnel.upload_blocked_len; - ctx->tunnel.upload_blocked_len = 0; - } - else { - nwritten = Curl_bufq_write(&ctx->tunnel.sendbuf, buf, len, err); - if(nwritten < 0) { - if(*err != CURLE_AGAIN) - goto out; - nwritten = 0; - } - } - - if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) { - /* req body data is buffered, resume the potentially suspended stream */ - rv = nghttp2_session_resume_data(ctx->h2, ctx->tunnel.stream_id); - if(nghttp2_is_fatal(rv)) { - *err = CURLE_SEND_ERROR; - nwritten = -1; - goto out; - } - } - - /* Call the nghttp2 send loop and flush to write ALL buffered data, - * headers and/or request body completely out to the network */ - result = proxy_h2_progress_egress(cf, data); - if(result == CURLE_AGAIN) { - blocked = 1; - } - else if(result) { - *err = result; - nwritten = -1; - goto out; - } - else if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) { - /* although we wrote everything that nghttp2 wants to send now, - * there is data left in our stream send buffer unwritten. This may - * be due to the stream's HTTP/2 flow window being exhausted. */ - blocked = 1; - } - - if(blocked) { - /* Unable to send all data, due to connection blocked or H2 window - * exhaustion. Data is left in our stream buffer, or nghttp2's internal - * frame buffer or our network out buffer. */ - size_t rwin = nghttp2_session_get_stream_remote_window_size( - ctx->h2, ctx->tunnel.stream_id); - if(rwin == 0) { - /* H2 flow window exhaustion. - * FIXME: there is no way to HOLD all transfers that use this - * proxy connection AND to UNHOLD all of them again when the - * window increases. - * We *could* iterate over all data on this conn maybe? */ - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] remote flow " - "window is exhausted", ctx->tunnel.stream_id)); - } - - /* Whatever the cause, we need to return CURL_EAGAIN for this call. - * We have unwritten state that needs us being invoked again and EAGAIN - * is the only way to ensure that. */ - ctx->tunnel.upload_blocked_len = nwritten; - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_send(len=%zu) BLOCK: win %u/%zu " - "blocked_len=%zu", - ctx->tunnel.stream_id, len, - nghttp2_session_get_remote_window_size(ctx->h2), rwin, - nwritten)); - *err = CURLE_AGAIN; - nwritten = -1; - goto out; - } - else if(should_close_session(ctx)) { - /* nghttp2 thinks this session is done. If the stream has not been - * closed, this is an error state for out transfer */ - if(ctx->tunnel.closed) { - *err = CURLE_SEND_ERROR; - nwritten = -1; - } - else { - DEBUGF(LOG_CF(data, cf, "send: nothing to do in this session")); - *err = CURLE_HTTP2; - nwritten = -1; - } - } - -out: - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_send(len=%zu) -> %zd, %d, " - "h2 windows %d-%d (stream-conn), " - "buffers %zu-%zu (stream-conn)", - ctx->tunnel.stream_id, len, nwritten, *err, - nghttp2_session_get_stream_remote_window_size( - ctx->h2, ctx->tunnel.stream_id), - nghttp2_session_get_remote_window_size(ctx->h2), - Curl_bufq_len(&ctx->tunnel.sendbuf), - Curl_bufq_len(&ctx->outbufq))); - CF_DATA_RESTORE(cf, save); - return nwritten; -} - -static bool proxy_h2_connisalive(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *input_pending) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - bool alive = TRUE; - - *input_pending = FALSE; - if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) - return FALSE; - - if(*input_pending) { - /* This happens before we've sent off a request and the connection is - not in use by any other transfer, there shouldn't be any data here, - only "protocol frames" */ - CURLcode result; - ssize_t nread = -1; - - *input_pending = FALSE; - nread = Curl_bufq_slurp(&ctx->inbufq, proxy_nw_in_reader, cf, &result); - if(nread != -1) { - if(proxy_h2_process_pending_input(cf, data, &result) < 0) - /* immediate error, considered dead */ - alive = FALSE; - else { - alive = !should_close_session(ctx); - } - } - else if(result != CURLE_AGAIN) { - /* the read failed so let's say this is dead anyway */ - alive = FALSE; - } - } - - return alive; -} - -static bool cf_h2_proxy_is_alive(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *input_pending) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - CURLcode result; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - result = (ctx && ctx->h2 && proxy_h2_connisalive(cf, data, input_pending)); - DEBUGF(LOG_CF(data, cf, "conn alive -> %d, input_pending=%d", - result, *input_pending)); - CF_DATA_RESTORE(cf, save); - return result; -} - -struct Curl_cftype Curl_cft_h2_proxy = { - "H2-PROXY", - CF_TYPE_IP_CONNECT, - CURL_LOG_DEFAULT, - cf_h2_proxy_destroy, - cf_h2_proxy_connect, - cf_h2_proxy_close, - Curl_cf_http_proxy_get_host, - cf_h2_proxy_get_select_socks, - cf_h2_proxy_data_pending, - cf_h2_proxy_send, - cf_h2_proxy_recv, - Curl_cf_def_cntrl, - cf_h2_proxy_is_alive, - Curl_cf_def_conn_keep_alive, - Curl_cf_def_query, -}; - -CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct Curl_cfilter *cf_h2_proxy = NULL; - struct cf_h2_proxy_ctx *ctx; - CURLcode result = CURLE_OUT_OF_MEMORY; - - (void)data; - ctx = calloc(sizeof(*ctx), 1); - if(!ctx) - goto out; - - result = Curl_cf_create(&cf_h2_proxy, &Curl_cft_h2_proxy, ctx); - if(result) - goto out; - - Curl_conn_cf_insert_after(cf, cf_h2_proxy); - result = CURLE_OK; - -out: - if(result) - cf_h2_proxy_ctx_free(ctx); - return result; -} - -#endif /* defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY) */ diff --git a/contrib/libs/curl/lib/cf-h2-proxy.h b/contrib/libs/curl/lib/cf-h2-proxy.h deleted file mode 100644 index c01bf62133..0000000000 --- a/contrib/libs/curl/lib/cf-h2-proxy.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef HEADER_CURL_H2_PROXY_H -#define HEADER_CURL_H2_PROXY_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#if defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY) - -CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, - struct Curl_easy *data); - -extern struct Curl_cftype Curl_cft_h2_proxy; - - -#endif /* defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY) */ - -#endif /* HEADER_CURL_H2_PROXY_H */ diff --git a/contrib/libs/curl/lib/cf-haproxy.c b/contrib/libs/curl/lib/cf-haproxy.c deleted file mode 100644 index ec0100ca9e..0000000000 --- a/contrib/libs/curl/lib/cf-haproxy.c +++ /dev/null @@ -1,251 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#if !defined(CURL_DISABLE_PROXY) - -#include <curl/curl.h> -#include "urldata.h" -#include "cfilters.h" -#include "cf-haproxy.h" -#include "curl_log.h" -#include "multiif.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - - -typedef enum { - HAPROXY_INIT, /* init/default/no tunnel state */ - HAPROXY_SEND, /* data_out being sent */ - HAPROXY_DONE /* all work done */ -} haproxy_state; - -struct cf_haproxy_ctx { - int state; - struct dynbuf data_out; -}; - -static void cf_haproxy_ctx_reset(struct cf_haproxy_ctx *ctx) -{ - DEBUGASSERT(ctx); - ctx->state = HAPROXY_INIT; - Curl_dyn_reset(&ctx->data_out); -} - -static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx) -{ - if(ctx) { - Curl_dyn_free(&ctx->data_out); - free(ctx); - } -} - -static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf, - struct Curl_easy *data) -{ - struct cf_haproxy_ctx *ctx = cf->ctx; - CURLcode result; - const char *tcp_version; - const char *client_ip; - - DEBUGASSERT(ctx); - DEBUGASSERT(ctx->state == HAPROXY_INIT); -#ifdef USE_UNIX_SOCKETS - if(cf->conn->unix_domain_socket) - /* the buffer is large enough to hold this! */ - result = Curl_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n")); - else { -#endif /* USE_UNIX_SOCKETS */ - /* Emit the correct prefix for IPv6 */ - tcp_version = cf->conn->bits.ipv6 ? "TCP6" : "TCP4"; - if(data->set.str[STRING_HAPROXY_CLIENT_IP]) - client_ip = data->set.str[STRING_HAPROXY_CLIENT_IP]; - else - client_ip = data->info.conn_primary_ip; - - result = Curl_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n", - tcp_version, - data->info.conn_local_ip, - client_ip, - data->info.conn_local_port, - data->info.conn_primary_port); - -#ifdef USE_UNIX_SOCKETS - } -#endif /* USE_UNIX_SOCKETS */ - return result; -} - -static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking, bool *done) -{ - struct cf_haproxy_ctx *ctx = cf->ctx; - CURLcode result; - size_t len; - - DEBUGASSERT(ctx); - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } - - result = cf->next->cft->do_connect(cf->next, data, blocking, done); - if(result || !*done) - return result; - - switch(ctx->state) { - case HAPROXY_INIT: - result = cf_haproxy_date_out_set(cf, data); - if(result) - goto out; - ctx->state = HAPROXY_SEND; - /* FALLTHROUGH */ - case HAPROXY_SEND: - len = Curl_dyn_len(&ctx->data_out); - if(len > 0) { - ssize_t written = Curl_conn_send(data, cf->sockindex, - Curl_dyn_ptr(&ctx->data_out), - len, &result); - if(written < 0) - goto out; - Curl_dyn_tail(&ctx->data_out, len - (size_t)written); - if(Curl_dyn_len(&ctx->data_out) > 0) { - result = CURLE_OK; - goto out; - } - } - ctx->state = HAPROXY_DONE; - /* FALLTHROUGH */ - default: - Curl_dyn_free(&ctx->data_out); - break; - } - -out: - *done = (!result) && (ctx->state == HAPROXY_DONE); - cf->connected = *done; - return result; -} - -static void cf_haproxy_destroy(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - (void)data; - DEBUGF(LOG_CF(data, cf, "destroy")); - cf_haproxy_ctx_free(cf->ctx); -} - -static void cf_haproxy_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - DEBUGF(LOG_CF(data, cf, "close")); - cf->connected = FALSE; - cf_haproxy_ctx_reset(cf->ctx); - if(cf->next) - cf->next->cft->do_close(cf->next, data); -} - -static int cf_haproxy_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) -{ - int fds; - - fds = cf->next->cft->get_select_socks(cf->next, data, socks); - if(!fds && cf->next->connected && !cf->connected) { - /* If we are not connected, but the filter "below" is - * and not waiting on something, we are sending. */ - socks[0] = Curl_conn_cf_get_socket(cf, data); - return GETSOCK_WRITESOCK(0); - } - return fds; -} - - -struct Curl_cftype Curl_cft_haproxy = { - "HAPROXY", - 0, - 0, - cf_haproxy_destroy, - cf_haproxy_connect, - cf_haproxy_close, - Curl_cf_def_get_host, - cf_haproxy_get_select_socks, - Curl_cf_def_data_pending, - Curl_cf_def_send, - Curl_cf_def_recv, - Curl_cf_def_cntrl, - Curl_cf_def_conn_is_alive, - Curl_cf_def_conn_keep_alive, - Curl_cf_def_query, -}; - -static CURLcode cf_haproxy_create(struct Curl_cfilter **pcf, - struct Curl_easy *data) -{ - struct Curl_cfilter *cf = NULL; - struct cf_haproxy_ctx *ctx; - CURLcode result; - - (void)data; - ctx = calloc(sizeof(*ctx), 1); - if(!ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - ctx->state = HAPROXY_INIT; - Curl_dyn_init(&ctx->data_out, DYN_HAXPROXY); - - result = Curl_cf_create(&cf, &Curl_cft_haproxy, ctx); - if(result) - goto out; - ctx = NULL; - -out: - cf_haproxy_ctx_free(ctx); - *pcf = result? NULL : cf; - return result; -} - -CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data) -{ - struct Curl_cfilter *cf; - CURLcode result; - - result = cf_haproxy_create(&cf, data); - if(result) - goto out; - Curl_conn_cf_insert_after(cf_at, cf); - -out: - return result; -} - -#endif /* !CURL_DISABLE_PROXY */ diff --git a/contrib/libs/curl/lib/cf-haproxy.h b/contrib/libs/curl/lib/cf-haproxy.h deleted file mode 100644 index d02c323e7b..0000000000 --- a/contrib/libs/curl/lib/cf-haproxy.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef HEADER_CURL_CF_HAPROXY_H -#define HEADER_CURL_CF_HAPROXY_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" -#include "urldata.h" - -#if !defined(CURL_DISABLE_PROXY) - -CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data); - -extern struct Curl_cftype Curl_cft_haproxy; - -#endif /* !CURL_DISABLE_PROXY */ - -#endif /* HEADER_CURL_CF_HAPROXY_H */ diff --git a/contrib/libs/curl/lib/cf-https-connect.c b/contrib/libs/curl/lib/cf-https-connect.c deleted file mode 100644 index 4e4d4b117d..0000000000 --- a/contrib/libs/curl/lib/cf-https-connect.c +++ /dev/null @@ -1,551 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) - -#include "urldata.h" -#include <curl/curl.h> -#include "curl_log.h" -#include "cfilters.h" -#include "connect.h" -#include "multiif.h" -#include "cf-https-connect.h" -#include "http2.h" -#include "vquic/vquic.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - - -typedef enum { - CF_HC_INIT, - CF_HC_CONNECT, - CF_HC_SUCCESS, - CF_HC_FAILURE -} cf_hc_state; - -struct cf_hc_baller { - const char *name; - struct Curl_cfilter *cf; - CURLcode result; - struct curltime started; - int reply_ms; - bool enabled; -}; - -static void cf_hc_baller_reset(struct cf_hc_baller *b, - struct Curl_easy *data) -{ - if(b->cf) { - Curl_conn_cf_close(b->cf, data); - Curl_conn_cf_discard_chain(&b->cf, data); - b->cf = NULL; - } - b->result = CURLE_OK; - b->reply_ms = -1; -} - -static bool cf_hc_baller_is_active(struct cf_hc_baller *b) -{ - return b->enabled && b->cf && !b->result; -} - -static bool cf_hc_baller_has_started(struct cf_hc_baller *b) -{ - return !!b->cf; -} - -static int cf_hc_baller_reply_ms(struct cf_hc_baller *b, - struct Curl_easy *data) -{ - if(b->reply_ms < 0) - b->cf->cft->query(b->cf, data, CF_QUERY_CONNECT_REPLY_MS, - &b->reply_ms, NULL); - return b->reply_ms; -} - -static bool cf_hc_baller_data_pending(struct cf_hc_baller *b, - const struct Curl_easy *data) -{ - return b->cf && !b->result && b->cf->cft->has_data_pending(b->cf, data); -} - -struct cf_hc_ctx { - cf_hc_state state; - const struct Curl_dns_entry *remotehost; - struct curltime started; /* when connect started */ - CURLcode result; /* overall result */ - struct cf_hc_baller h3_baller; - struct cf_hc_baller h21_baller; - int soft_eyeballs_timeout_ms; - int hard_eyeballs_timeout_ms; -}; - -static void cf_hc_baller_init(struct cf_hc_baller *b, - struct Curl_cfilter *cf, - struct Curl_easy *data, - const char *name, - int transport) -{ - struct cf_hc_ctx *ctx = cf->ctx; - struct Curl_cfilter *save = cf->next; - - b->name = name; - cf->next = NULL; - b->started = Curl_now(); - b->result = Curl_cf_setup_insert_after(cf, data, ctx->remotehost, - transport, CURL_CF_SSL_ENABLE); - b->cf = cf->next; - cf->next = save; -} - -static CURLcode cf_hc_baller_connect(struct cf_hc_baller *b, - struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) -{ - struct Curl_cfilter *save = cf->next; - - cf->next = b->cf; - b->result = Curl_conn_cf_connect(cf->next, data, FALSE, done); - b->cf = cf->next; /* it might mutate */ - cf->next = save; - return b->result; -} - -static void cf_hc_reset(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_hc_ctx *ctx = cf->ctx; - - if(ctx) { - cf_hc_baller_reset(&ctx->h3_baller, data); - cf_hc_baller_reset(&ctx->h21_baller, data); - ctx->state = CF_HC_INIT; - ctx->result = CURLE_OK; - ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout; - ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 2; - } -} - -static CURLcode baller_connected(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct cf_hc_baller *winner) -{ - struct cf_hc_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - - DEBUGASSERT(winner->cf); - if(winner != &ctx->h3_baller) - cf_hc_baller_reset(&ctx->h3_baller, data); - if(winner != &ctx->h21_baller) - cf_hc_baller_reset(&ctx->h21_baller, data); - - DEBUGF(LOG_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms", - winner->name, (int)Curl_timediff(Curl_now(), winner->started), - cf_hc_baller_reply_ms(winner, data))); - cf->next = winner->cf; - winner->cf = NULL; - - switch(cf->conn->alpn) { - case CURL_HTTP_VERSION_3: - infof(data, "using HTTP/3"); - break; - case CURL_HTTP_VERSION_2: -#ifdef USE_NGHTTP2 - /* Using nghttp2, we add the filter "below" us, so when the conn - * closes, we tear it down for a fresh reconnect */ - result = Curl_http2_switch_at(cf, data); - if(result) { - ctx->state = CF_HC_FAILURE; - ctx->result = result; - return result; - } -#endif - infof(data, "using HTTP/2"); - break; - case CURL_HTTP_VERSION_1_1: - infof(data, "using HTTP/1.1"); - break; - default: - infof(data, "using HTTP/1.x"); - break; - } - ctx->state = CF_HC_SUCCESS; - cf->connected = TRUE; - Curl_conn_cf_cntrl(cf->next, data, TRUE, - CF_CTRL_CONN_INFO_UPDATE, 0, NULL); - return result; -} - - -static bool time_to_start_h21(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct curltime now) -{ - struct cf_hc_ctx *ctx = cf->ctx; - timediff_t elapsed_ms; - - if(!ctx->h21_baller.enabled || cf_hc_baller_has_started(&ctx->h21_baller)) - return FALSE; - - if(!ctx->h3_baller.enabled || !cf_hc_baller_is_active(&ctx->h3_baller)) - return TRUE; - - elapsed_ms = Curl_timediff(now, ctx->started); - if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) { - DEBUGF(LOG_CF(data, cf, "hard timeout of %dms reached, starting h21", - ctx->hard_eyeballs_timeout_ms)); - return TRUE; - } - - if(elapsed_ms >= ctx->soft_eyeballs_timeout_ms) { - if(cf_hc_baller_reply_ms(&ctx->h3_baller, data) < 0) { - DEBUGF(LOG_CF(data, cf, "soft timeout of %dms reached, h3 has not " - "seen any data, starting h21", - ctx->soft_eyeballs_timeout_ms)); - return TRUE; - } - /* set the effective hard timeout again */ - Curl_expire(data, ctx->hard_eyeballs_timeout_ms - elapsed_ms, - EXPIRE_ALPN_EYEBALLS); - } - return FALSE; -} - -static CURLcode cf_hc_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking, bool *done) -{ - struct cf_hc_ctx *ctx = cf->ctx; - struct curltime now; - CURLcode result = CURLE_OK; - - (void)blocking; - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } - - *done = FALSE; - now = Curl_now(); - switch(ctx->state) { - case CF_HC_INIT: - DEBUGASSERT(!ctx->h3_baller.cf); - DEBUGASSERT(!ctx->h21_baller.cf); - DEBUGASSERT(!cf->next); - DEBUGF(LOG_CF(data, cf, "connect, init")); - ctx->started = now; - if(ctx->h3_baller.enabled) { - cf_hc_baller_init(&ctx->h3_baller, cf, data, "h3", TRNSPRT_QUIC); - if(ctx->h21_baller.enabled) - Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS); - } - else if(ctx->h21_baller.enabled) - cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21", - cf->conn->transport); - ctx->state = CF_HC_CONNECT; - /* FALLTHROUGH */ - - case CF_HC_CONNECT: - if(cf_hc_baller_is_active(&ctx->h3_baller)) { - result = cf_hc_baller_connect(&ctx->h3_baller, cf, data, done); - if(!result && *done) { - result = baller_connected(cf, data, &ctx->h3_baller); - goto out; - } - } - - if(time_to_start_h21(cf, data, now)) { - cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21", - cf->conn->transport); - } - - if(cf_hc_baller_is_active(&ctx->h21_baller)) { - DEBUGF(LOG_CF(data, cf, "connect, check h21")); - result = cf_hc_baller_connect(&ctx->h21_baller, cf, data, done); - if(!result && *done) { - result = baller_connected(cf, data, &ctx->h21_baller); - goto out; - } - } - - if((!ctx->h3_baller.enabled || ctx->h3_baller.result) && - (!ctx->h21_baller.enabled || ctx->h21_baller.result)) { - /* both failed or disabled. we give up */ - DEBUGF(LOG_CF(data, cf, "connect, all failed")); - result = ctx->result = ctx->h3_baller.enabled? - ctx->h3_baller.result : ctx->h21_baller.result; - ctx->state = CF_HC_FAILURE; - goto out; - } - result = CURLE_OK; - *done = FALSE; - break; - - case CF_HC_FAILURE: - result = ctx->result; - cf->connected = FALSE; - *done = FALSE; - break; - - case CF_HC_SUCCESS: - result = CURLE_OK; - cf->connected = TRUE; - *done = TRUE; - break; - } - -out: - DEBUGF(LOG_CF(data, cf, "connect -> %d, done=%d", result, *done)); - return result; -} - -static int cf_hc_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) -{ - struct cf_hc_ctx *ctx = cf->ctx; - size_t i, j, s; - int brc, rc = GETSOCK_BLANK; - curl_socket_t bsocks[MAX_SOCKSPEREASYHANDLE]; - struct cf_hc_baller *ballers[2]; - - if(cf->connected) - return cf->next->cft->get_select_socks(cf->next, data, socks); - - ballers[0] = &ctx->h3_baller; - ballers[1] = &ctx->h21_baller; - for(i = s = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) { - struct cf_hc_baller *b = ballers[i]; - if(!cf_hc_baller_is_active(b)) - continue; - brc = Curl_conn_cf_get_select_socks(b->cf, data, bsocks); - DEBUGF(LOG_CF(data, cf, "get_selected_socks(%s) -> %x", b->name, brc)); - if(!brc) - continue; - for(j = 0; j < MAX_SOCKSPEREASYHANDLE && s < MAX_SOCKSPEREASYHANDLE; ++j) { - if((brc & GETSOCK_WRITESOCK(j)) || (brc & GETSOCK_READSOCK(j))) { - socks[s] = bsocks[j]; - if(brc & GETSOCK_WRITESOCK(j)) - rc |= GETSOCK_WRITESOCK(s); - if(brc & GETSOCK_READSOCK(j)) - rc |= GETSOCK_READSOCK(s); - s++; - } - } - } - DEBUGF(LOG_CF(data, cf, "get_selected_socks -> %x", rc)); - return rc; -} - -static bool cf_hc_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data) -{ - struct cf_hc_ctx *ctx = cf->ctx; - - if(cf->connected) - return cf->next->cft->has_data_pending(cf->next, data); - - DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data_pending")); - return cf_hc_baller_data_pending(&ctx->h3_baller, data) - || cf_hc_baller_data_pending(&ctx->h21_baller, data); -} - -static struct curltime cf_get_max_baller_time(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query) -{ - struct cf_hc_ctx *ctx = cf->ctx; - struct Curl_cfilter *cfb; - struct curltime t, tmax; - - memset(&tmax, 0, sizeof(tmax)); - memset(&t, 0, sizeof(t)); - cfb = ctx->h21_baller.enabled? ctx->h21_baller.cf : NULL; - if(cfb && !cfb->cft->query(cfb, data, query, NULL, &t)) { - if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0) - tmax = t; - } - memset(&t, 0, sizeof(t)); - cfb = ctx->h3_baller.enabled? ctx->h3_baller.cf : NULL; - if(cfb && !cfb->cft->query(cfb, data, query, NULL, &t)) { - if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0) - tmax = t; - } - return tmax; -} - -static CURLcode cf_hc_query(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query, int *pres1, void *pres2) -{ - if(!cf->connected) { - switch(query) { - case CF_QUERY_TIMER_CONNECT: { - struct curltime *when = pres2; - *when = cf_get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT); - return CURLE_OK; - } - case CF_QUERY_TIMER_APPCONNECT: { - struct curltime *when = pres2; - *when = cf_get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT); - return CURLE_OK; - } - default: - break; - } - } - return cf->next? - cf->next->cft->query(cf->next, data, query, pres1, pres2) : - CURLE_UNKNOWN_OPTION; -} - -static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - DEBUGF(LOG_CF(data, cf, "close")); - cf_hc_reset(cf, data); - cf->connected = FALSE; - - if(cf->next) { - cf->next->cft->do_close(cf->next, data); - Curl_conn_cf_discard_chain(&cf->next, data); - } -} - -static void cf_hc_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_hc_ctx *ctx = cf->ctx; - - (void)data; - DEBUGF(LOG_CF(data, cf, "destroy")); - cf_hc_reset(cf, data); - Curl_safefree(ctx); -} - -struct Curl_cftype Curl_cft_http_connect = { - "HTTPS-CONNECT", - 0, - CURL_LOG_DEFAULT, - cf_hc_destroy, - cf_hc_connect, - cf_hc_close, - Curl_cf_def_get_host, - cf_hc_get_select_socks, - cf_hc_data_pending, - Curl_cf_def_send, - Curl_cf_def_recv, - Curl_cf_def_cntrl, - Curl_cf_def_conn_is_alive, - Curl_cf_def_conn_keep_alive, - cf_hc_query, -}; - -static CURLcode cf_hc_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - const struct Curl_dns_entry *remotehost, - bool try_h3, bool try_h21) -{ - struct Curl_cfilter *cf = NULL; - struct cf_hc_ctx *ctx; - CURLcode result = CURLE_OK; - - (void)data; - ctx = calloc(sizeof(*ctx), 1); - if(!ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - ctx->remotehost = remotehost; - ctx->h3_baller.enabled = try_h3; - ctx->h21_baller.enabled = try_h21; - - result = Curl_cf_create(&cf, &Curl_cft_http_connect, ctx); - if(result) - goto out; - ctx = NULL; - cf_hc_reset(cf, data); - -out: - *pcf = result? NULL : cf; - free(ctx); - return result; -} - -static CURLcode cf_http_connect_add(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - const struct Curl_dns_entry *remotehost, - bool try_h3, bool try_h21) -{ - struct Curl_cfilter *cf; - CURLcode result = CURLE_OK; - - DEBUGASSERT(data); - result = cf_hc_create(&cf, data, remotehost, try_h3, try_h21); - if(result) - goto out; - Curl_conn_cf_add(data, conn, sockindex, cf); -out: - return result; -} - -CURLcode Curl_cf_https_setup(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - const struct Curl_dns_entry *remotehost) -{ - bool try_h3 = FALSE, try_h21 = TRUE; /* defaults, for now */ - CURLcode result = CURLE_OK; - - (void)sockindex; - (void)remotehost; - - if(!conn->bits.tls_enable_alpn) - goto out; - - if(data->state.httpwant == CURL_HTTP_VERSION_3ONLY) { - result = Curl_conn_may_http3(data, conn); - if(result) /* can't do it */ - goto out; - try_h3 = TRUE; - try_h21 = FALSE; - } - else if(data->state.httpwant >= CURL_HTTP_VERSION_3) { - /* We assume that silently not even trying H3 is ok here */ - /* TODO: should we fail instead? */ - try_h3 = (Curl_conn_may_http3(data, conn) == CURLE_OK); - try_h21 = TRUE; - } - - result = cf_http_connect_add(data, conn, sockindex, remotehost, - try_h3, try_h21); -out: - return result; -} - -#endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */ diff --git a/contrib/libs/curl/lib/cf-https-connect.h b/contrib/libs/curl/lib/cf-https-connect.h deleted file mode 100644 index 6a39527317..0000000000 --- a/contrib/libs/curl/lib/cf-https-connect.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef HEADER_CURL_CF_HTTP_H -#define HEADER_CURL_CF_HTTP_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ -#include "curl_setup.h" - -#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) - -struct Curl_cfilter; -struct Curl_easy; -struct connectdata; -struct Curl_cftype; -struct Curl_dns_entry; - -extern struct Curl_cftype Curl_cft_http_connect; - -CURLcode Curl_cf_http_connect_add(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - const struct Curl_dns_entry *remotehost, - bool try_h3, bool try_h21); - -CURLcode -Curl_cf_http_connect_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data, - const struct Curl_dns_entry *remotehost, - bool try_h3, bool try_h21); - - -CURLcode Curl_cf_https_setup(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - const struct Curl_dns_entry *remotehost); - - -#endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */ -#endif /* HEADER_CURL_CF_HTTP_H */ diff --git a/contrib/libs/curl/lib/cf-socket.c b/contrib/libs/curl/lib/cf-socket.c deleted file mode 100644 index 5729fe07fa..0000000000 --- a/contrib/libs/curl/lib/cf-socket.c +++ /dev/null @@ -1,1925 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifdef HAVE_NETINET_IN_H -#include <netinet/in.h> /* <netinet/tcp.h> may need it */ -#endif -#ifdef HAVE_SYS_UN_H -#include <sys/un.h> /* for sockaddr_un */ -#endif -#ifdef HAVE_LINUX_TCP_H -#include <linux/tcp.h> -#elif defined(HAVE_NETINET_TCP_H) -#include <netinet/tcp.h> -#endif -#ifdef HAVE_SYS_IOCTL_H -#include <sys/ioctl.h> -#endif -#ifdef HAVE_NETDB_H -#include <netdb.h> -#endif -#ifdef HAVE_FCNTL_H -#include <fcntl.h> -#endif -#ifdef HAVE_ARPA_INET_H -#include <arpa/inet.h> -#endif - -#ifdef __VMS -#include <in.h> -#include <inet.h> -#endif - -#include "urldata.h" -#include "bufq.h" -#include "sendf.h" -#include "if2ip.h" -#include "strerror.h" -#include "cfilters.h" -#include "cf-socket.h" -#include "connect.h" -#include "select.h" -#include "url.h" /* for Curl_safefree() */ -#include "multiif.h" -#include "sockaddr.h" /* required for Curl_sockaddr_storage */ -#include "inet_ntop.h" -#include "inet_pton.h" -#include "progress.h" -#include "warnless.h" -#include "conncache.h" -#include "multihandle.h" -#include "share.h" -#include "version_win32.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - - -#if defined(ENABLE_IPV6) && defined(IPV6_V6ONLY) && defined(WIN32) -/* It makes support for IPv4-mapped IPv6 addresses. - * Linux kernel, NetBSD, FreeBSD and Darwin: default is off; - * Windows Vista and later: default is on; - * DragonFly BSD: acts like off, and dummy setting; - * OpenBSD and earlier Windows: unsupported. - * Linux: controlled by /proc/sys/net/ipv6/bindv6only. - */ -static void set_ipv6_v6only(curl_socket_t sockfd, int on) -{ - (void)setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(on)); -} -#else -#define set_ipv6_v6only(x,y) -#endif - -static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd) -{ -#if defined(TCP_NODELAY) - curl_socklen_t onoff = (curl_socklen_t) 1; - int level = IPPROTO_TCP; -#if !defined(CURL_DISABLE_VERBOSE_STRINGS) - char buffer[STRERROR_LEN]; -#else - (void) data; -#endif - - if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff, - sizeof(onoff)) < 0) - infof(data, "Could not set TCP_NODELAY: %s", - Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); -#else - (void)data; - (void)sockfd; -#endif -} - -#ifdef SO_NOSIGPIPE -/* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when - sending data to a dead peer (instead of relying on the 4th argument to send - being MSG_NOSIGNAL). Possibly also existing and in use on other BSD - systems? */ -static void nosigpipe(struct Curl_easy *data, - curl_socket_t sockfd) -{ - int onoff = 1; - if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff, - sizeof(onoff)) < 0) { -#if !defined(CURL_DISABLE_VERBOSE_STRINGS) - char buffer[STRERROR_LEN]; - infof(data, "Could not set SO_NOSIGPIPE: %s", - Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); -#endif - } -} -#else -#define nosigpipe(x,y) Curl_nop_stmt -#endif - -#if defined(__DragonFly__) || defined(HAVE_WINSOCK2_H) -/* DragonFlyBSD and Windows use millisecond units */ -#define KEEPALIVE_FACTOR(x) (x *= 1000) -#else -#define KEEPALIVE_FACTOR(x) -#endif - -#if defined(HAVE_WINSOCK2_H) && !defined(SIO_KEEPALIVE_VALS) -#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4) - -struct tcp_keepalive { - u_long onoff; - u_long keepalivetime; - u_long keepaliveinterval; -}; -#endif - -static void -tcpkeepalive(struct Curl_easy *data, - curl_socket_t sockfd) -{ - int optval = data->set.tcp_keepalive?1:0; - - /* only set IDLE and INTVL if setting KEEPALIVE is successful */ - if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, - (void *)&optval, sizeof(optval)) < 0) { - infof(data, "Failed to set SO_KEEPALIVE on fd %d", sockfd); - } - else { -#if defined(SIO_KEEPALIVE_VALS) - struct tcp_keepalive vals; - DWORD dummy; - vals.onoff = 1; - optval = curlx_sltosi(data->set.tcp_keepidle); - KEEPALIVE_FACTOR(optval); - vals.keepalivetime = optval; - optval = curlx_sltosi(data->set.tcp_keepintvl); - KEEPALIVE_FACTOR(optval); - vals.keepaliveinterval = optval; - if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals), - NULL, 0, &dummy, NULL, NULL) != 0) { - infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd %d: %d", - (int)sockfd, WSAGetLastError()); - } -#else -#ifdef TCP_KEEPIDLE - optval = curlx_sltosi(data->set.tcp_keepidle); - KEEPALIVE_FACTOR(optval); - if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, - (void *)&optval, sizeof(optval)) < 0) { - infof(data, "Failed to set TCP_KEEPIDLE on fd %d", sockfd); - } -#elif defined(TCP_KEEPALIVE) - /* Mac OS X style */ - optval = curlx_sltosi(data->set.tcp_keepidle); - KEEPALIVE_FACTOR(optval); - if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE, - (void *)&optval, sizeof(optval)) < 0) { - infof(data, "Failed to set TCP_KEEPALIVE on fd %d", sockfd); - } -#endif -#ifdef TCP_KEEPINTVL - optval = curlx_sltosi(data->set.tcp_keepintvl); - KEEPALIVE_FACTOR(optval); - if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, - (void *)&optval, sizeof(optval)) < 0) { - infof(data, "Failed to set TCP_KEEPINTVL on fd %d", sockfd); - } -#endif -#endif - } -} - -/** - * Assign the address `ai` to the Curl_sockaddr_ex `dest` and - * set the transport used. - */ -void Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest, - const struct Curl_addrinfo *ai, - int transport) -{ - /* - * The Curl_sockaddr_ex structure is basically libcurl's external API - * curl_sockaddr structure with enough space available to directly hold - * any protocol-specific address structures. The variable declared here - * will be used to pass / receive data to/from the fopensocket callback - * if this has been set, before that, it is initialized from parameters. - */ - dest->family = ai->ai_family; - switch(transport) { - case TRNSPRT_TCP: - dest->socktype = SOCK_STREAM; - dest->protocol = IPPROTO_TCP; - break; - case TRNSPRT_UNIX: - dest->socktype = SOCK_STREAM; - dest->protocol = IPPROTO_IP; - break; - default: /* UDP and QUIC */ - dest->socktype = SOCK_DGRAM; - dest->protocol = IPPROTO_UDP; - break; - } - dest->addrlen = ai->ai_addrlen; - - if(dest->addrlen > sizeof(struct Curl_sockaddr_storage)) - dest->addrlen = sizeof(struct Curl_sockaddr_storage); - memcpy(&dest->sa_addr, ai->ai_addr, dest->addrlen); -} - -static CURLcode socket_open(struct Curl_easy *data, - struct Curl_sockaddr_ex *addr, - curl_socket_t *sockfd) -{ - DEBUGASSERT(data); - DEBUGASSERT(data->conn); - if(data->set.fopensocket) { - /* - * If the opensocket callback is set, all the destination address - * information is passed to the callback. Depending on this information the - * callback may opt to abort the connection, this is indicated returning - * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When - * the callback returns a valid socket the destination address information - * might have been changed and this 'new' address will actually be used - * here to connect. - */ - Curl_set_in_callback(data, true); - *sockfd = data->set.fopensocket(data->set.opensocket_client, - CURLSOCKTYPE_IPCXN, - (struct curl_sockaddr *)addr); - Curl_set_in_callback(data, false); - } - else { - /* opensocket callback not set, so simply create the socket now */ - *sockfd = socket(addr->family, addr->socktype, addr->protocol); - } - - if(*sockfd == CURL_SOCKET_BAD) - /* no socket, no connection */ - return CURLE_COULDNT_CONNECT; - -#if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) - if(data->conn->scope_id && (addr->family == AF_INET6)) { - struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr; - sa6->sin6_scope_id = data->conn->scope_id; - } -#endif - return CURLE_OK; -} - -/* - * Create a socket based on info from 'conn' and 'ai'. - * - * 'addr' should be a pointer to the correct struct to get data back, or NULL. - * 'sockfd' must be a pointer to a socket descriptor. - * - * If the open socket callback is set, used that! - * - */ -CURLcode Curl_socket_open(struct Curl_easy *data, - const struct Curl_addrinfo *ai, - struct Curl_sockaddr_ex *addr, - int transport, - curl_socket_t *sockfd) -{ - struct Curl_sockaddr_ex dummy; - - if(!addr) - /* if the caller doesn't want info back, use a local temp copy */ - addr = &dummy; - - Curl_sock_assign_addr(addr, ai, transport); - return socket_open(data, addr, sockfd); -} - -static int socket_close(struct Curl_easy *data, struct connectdata *conn, - int use_callback, curl_socket_t sock) -{ - if(use_callback && conn && conn->fclosesocket) { - int rc; - Curl_multi_closed(data, sock); - Curl_set_in_callback(data, true); - rc = conn->fclosesocket(conn->closesocket_client, sock); - Curl_set_in_callback(data, false); - return rc; - } - - if(conn) - /* tell the multi-socket code about this */ - Curl_multi_closed(data, sock); - - sclose(sock); - - return 0; -} - -/* - * Close a socket. - * - * 'conn' can be NULL, beware! - */ -int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t sock) -{ - return socket_close(data, conn, FALSE, sock); -} - -#ifdef USE_WINSOCK -/* When you run a program that uses the Windows Sockets API, you may - experience slow performance when you copy data to a TCP server. - - https://support.microsoft.com/kb/823764 - - Work-around: Make the Socket Send Buffer Size Larger Than the Program Send - Buffer Size - - The problem described in this knowledge-base is applied only to pre-Vista - Windows. Following function trying to detect OS version and skips - SO_SNDBUF adjustment for Windows Vista and above. -*/ -#define DETECT_OS_NONE 0 -#define DETECT_OS_PREVISTA 1 -#define DETECT_OS_VISTA_OR_LATER 2 - -void Curl_sndbufset(curl_socket_t sockfd) -{ - int val = CURL_MAX_WRITE_SIZE + 32; - int curval = 0; - int curlen = sizeof(curval); - - static int detectOsState = DETECT_OS_NONE; - - if(detectOsState == DETECT_OS_NONE) { - if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT, - VERSION_GREATER_THAN_EQUAL)) - detectOsState = DETECT_OS_VISTA_OR_LATER; - else - detectOsState = DETECT_OS_PREVISTA; - } - - if(detectOsState == DETECT_OS_VISTA_OR_LATER) - return; - - if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0) - if(curval > val) - return; - - setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val)); -} -#endif - -static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t sockfd, int af, unsigned int scope) -{ - struct Curl_sockaddr_storage sa; - struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */ - curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */ - struct sockaddr_in *si4 = (struct sockaddr_in *)&sa; -#ifdef ENABLE_IPV6 - struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa; -#endif - - struct Curl_dns_entry *h = NULL; - unsigned short port = data->set.localport; /* use this port number, 0 for - "random" */ - /* how many port numbers to try to bind to, increasing one at a time */ - int portnum = data->set.localportrange; - const char *dev = data->set.str[STRING_DEVICE]; - int error; -#ifdef IP_BIND_ADDRESS_NO_PORT - int on = 1; -#endif -#ifndef ENABLE_IPV6 - (void)scope; -#endif - - /************************************************************* - * Select device to bind socket to - *************************************************************/ - if(!dev && !port) - /* no local kind of binding was requested */ - return CURLE_OK; - - memset(&sa, 0, sizeof(struct Curl_sockaddr_storage)); - - if(dev && (strlen(dev)<255) ) { - char myhost[256] = ""; - int done = 0; /* -1 for error, 1 for address found */ - bool is_interface = FALSE; - bool is_host = FALSE; - static const char *if_prefix = "if!"; - static const char *host_prefix = "host!"; - - if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) { - dev += strlen(if_prefix); - is_interface = TRUE; - } - else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) { - dev += strlen(host_prefix); - is_host = TRUE; - } - - /* interface */ - if(!is_host) { -#ifdef SO_BINDTODEVICE - /* I am not sure any other OSs than Linux that provide this feature, - * and at the least I cannot test. --Ben - * - * This feature allows one to tightly bind the local socket to a - * particular interface. This will force even requests to other - * local interfaces to go out the external interface. - * - * - * Only bind to the interface when specified as interface, not just - * as a hostname or ip address. - * - * interface might be a VRF, eg: vrf-blue, which means it cannot be - * converted to an IP address and would fail Curl_if2ip. Simply try - * to use it straight away. - */ - if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, - dev, (curl_socklen_t)strlen(dev) + 1) == 0) { - /* This is typically "errno 1, error: Operation not permitted" if - * you're not running as root or another suitable privileged - * user. - * If it succeeds it means the parameter was a valid interface and - * not an IP address. Return immediately. - */ - return CURLE_OK; - } -#endif - - switch(Curl_if2ip(af, -#ifdef ENABLE_IPV6 - scope, conn->scope_id, -#endif - dev, myhost, sizeof(myhost))) { - case IF2IP_NOT_FOUND: - if(is_interface) { - /* Do not fall back to treating it as a host name */ - failf(data, "Couldn't bind to interface '%s'", dev); - return CURLE_INTERFACE_FAILED; - } - break; - case IF2IP_AF_NOT_SUPPORTED: - /* Signal the caller to try another address family if available */ - return CURLE_UNSUPPORTED_PROTOCOL; - case IF2IP_FOUND: - is_interface = TRUE; - /* - * We now have the numerical IP address in the 'myhost' buffer - */ - infof(data, "Local Interface %s is ip %s using address family %i", - dev, myhost, af); - done = 1; - break; - } - } - if(!is_interface) { - /* - * This was not an interface, resolve the name as a host name - * or IP number - * - * Temporarily force name resolution to use only the address type - * of the connection. The resolve functions should really be changed - * to take a type parameter instead. - */ - unsigned char ipver = conn->ip_version; - int rc; - - if(af == AF_INET) - conn->ip_version = CURL_IPRESOLVE_V4; -#ifdef ENABLE_IPV6 - else if(af == AF_INET6) - conn->ip_version = CURL_IPRESOLVE_V6; -#endif - - rc = Curl_resolv(data, dev, 80, FALSE, &h); - if(rc == CURLRESOLV_PENDING) - (void)Curl_resolver_wait_resolv(data, &h); - conn->ip_version = ipver; - - if(h) { - /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */ - Curl_printable_address(h->addr, myhost, sizeof(myhost)); - infof(data, "Name '%s' family %i resolved to '%s' family %i", - dev, af, myhost, h->addr->ai_family); - Curl_resolv_unlock(data, h); - if(af != h->addr->ai_family) { - /* bad IP version combo, signal the caller to try another address - family if available */ - return CURLE_UNSUPPORTED_PROTOCOL; - } - done = 1; - } - else { - /* - * provided dev was no interface (or interfaces are not supported - * e.g. solaris) no ip address and no domain we fail here - */ - done = -1; - } - } - - if(done > 0) { -#ifdef ENABLE_IPV6 - /* IPv6 address */ - if(af == AF_INET6) { -#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID - char *scope_ptr = strchr(myhost, '%'); - if(scope_ptr) - *(scope_ptr++) = '\0'; -#endif - if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) { - si6->sin6_family = AF_INET6; - si6->sin6_port = htons(port); -#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID - if(scope_ptr) { - /* The "myhost" string either comes from Curl_if2ip or from - Curl_printable_address. The latter returns only numeric scope - IDs and the former returns none at all. So the scope ID, if - present, is known to be numeric */ - unsigned long scope_id = strtoul(scope_ptr, NULL, 10); - if(scope_id > UINT_MAX) - return CURLE_UNSUPPORTED_PROTOCOL; - - si6->sin6_scope_id = (unsigned int)scope_id; - } -#endif - } - sizeof_sa = sizeof(struct sockaddr_in6); - } - else -#endif - /* IPv4 address */ - if((af == AF_INET) && - (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) { - si4->sin_family = AF_INET; - si4->sin_port = htons(port); - sizeof_sa = sizeof(struct sockaddr_in); - } - } - - if(done < 1) { - /* errorbuf is set false so failf will overwrite any message already in - the error buffer, so the user receives this error message instead of a - generic resolve error. */ - data->state.errorbuf = FALSE; - failf(data, "Couldn't bind to '%s'", dev); - return CURLE_INTERFACE_FAILED; - } - } - else { - /* no device was given, prepare sa to match af's needs */ -#ifdef ENABLE_IPV6 - if(af == AF_INET6) { - si6->sin6_family = AF_INET6; - si6->sin6_port = htons(port); - sizeof_sa = sizeof(struct sockaddr_in6); - } - else -#endif - if(af == AF_INET) { - si4->sin_family = AF_INET; - si4->sin_port = htons(port); - sizeof_sa = sizeof(struct sockaddr_in); - } - } -#ifdef IP_BIND_ADDRESS_NO_PORT - (void)setsockopt(sockfd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &on, sizeof(on)); -#endif - for(;;) { - if(bind(sockfd, sock, sizeof_sa) >= 0) { - /* we succeeded to bind */ - struct Curl_sockaddr_storage add; - curl_socklen_t size = sizeof(add); - memset(&add, 0, sizeof(struct Curl_sockaddr_storage)); - if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) { - char buffer[STRERROR_LEN]; - data->state.os_errno = error = SOCKERRNO; - failf(data, "getsockname() failed with errno %d: %s", - error, Curl_strerror(error, buffer, sizeof(buffer))); - return CURLE_INTERFACE_FAILED; - } - infof(data, "Local port: %hu", port); - conn->bits.bound = TRUE; - return CURLE_OK; - } - - if(--portnum > 0) { - port++; /* try next port */ - if(port == 0) - break; - infof(data, "Bind to local port %hu failed, trying next", port - 1); - /* We re-use/clobber the port variable here below */ - if(sock->sa_family == AF_INET) - si4->sin_port = ntohs(port); -#ifdef ENABLE_IPV6 - else - si6->sin6_port = ntohs(port); -#endif - } - else - break; - } - { - char buffer[STRERROR_LEN]; - data->state.os_errno = error = SOCKERRNO; - failf(data, "bind failed with errno %d: %s", - error, Curl_strerror(error, buffer, sizeof(buffer))); - } - - return CURLE_INTERFACE_FAILED; -} - -/* - * verifyconnect() returns TRUE if the connect really has happened. - */ -static bool verifyconnect(curl_socket_t sockfd, int *error) -{ - bool rc = TRUE; -#ifdef SO_ERROR - int err = 0; - curl_socklen_t errSize = sizeof(err); - -#ifdef WIN32 - /* - * In October 2003 we effectively nullified this function on Windows due to - * problems with it using all CPU in multi-threaded cases. - * - * In May 2004, we bring it back to offer more info back on connect failures. - * Gisle Vanem could reproduce the former problems with this function, but - * could avoid them by adding this SleepEx() call below: - * - * "I don't have Rational Quantify, but the hint from his post was - * ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe - * just Sleep(0) would be enough?) would release whatever - * mutex/critical-section the ntdll call is waiting on. - * - * Someone got to verify this on Win-NT 4.0, 2000." - */ - -#ifdef _WIN32_WCE - Sleep(0); -#else - SleepEx(0, FALSE); -#endif - -#endif - - if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize)) - err = SOCKERRNO; -#ifdef _WIN32_WCE - /* Old WinCE versions don't support SO_ERROR */ - if(WSAENOPROTOOPT == err) { - SET_SOCKERRNO(0); - err = 0; - } -#endif -#if defined(EBADIOCTL) && defined(__minix) - /* Minix 3.1.x doesn't support getsockopt on UDP sockets */ - if(EBADIOCTL == err) { - SET_SOCKERRNO(0); - err = 0; - } -#endif - if((0 == err) || (EISCONN == err)) - /* we are connected, awesome! */ - rc = TRUE; - else - /* This wasn't a successful connect */ - rc = FALSE; - if(error) - *error = err; -#else - (void)sockfd; - if(error) - *error = SOCKERRNO; -#endif - return rc; -} - -/** - * Determine the curl code for a socket connect() == -1 with errno. - */ -static CURLcode socket_connect_result(struct Curl_easy *data, - const char *ipaddress, int error) -{ - char buffer[STRERROR_LEN]; - - switch(error) { - case EINPROGRESS: - case EWOULDBLOCK: -#if defined(EAGAIN) -#if (EAGAIN) != (EWOULDBLOCK) - /* On some platforms EAGAIN and EWOULDBLOCK are the - * same value, and on others they are different, hence - * the odd #if - */ - case EAGAIN: -#endif -#endif - return CURLE_OK; - - default: - /* unknown error, fallthrough and try another address! */ - infof(data, "Immediate connect fail for %s: %s", - ipaddress, Curl_strerror(error, buffer, sizeof(buffer))); - data->state.os_errno = error; - /* connect failed */ - return CURLE_COULDNT_CONNECT; - } -} - -/* We have a recv buffer to enhance reads with len < NW_SMALL_READS. - * This happens often on TLS connections where the TLS implementation - * tries to read the head of a TLS record, determine the length of the - * full record and then make a subsequent read for that. - * On large reads, we will not fill the buffer to avoid the double copy. */ -#define NW_RECV_CHUNK_SIZE (64 * 1024) -#define NW_RECV_CHUNKS 1 -#define NW_SMALL_READS (1024) - -struct cf_socket_ctx { - int transport; - struct Curl_sockaddr_ex addr; /* address to connect to */ - curl_socket_t sock; /* current attempt socket */ - struct bufq recvbuf; /* used when `buffer_recv` is set */ - char r_ip[MAX_IPADR_LEN]; /* remote IP as string */ - int r_port; /* remote port number */ - char l_ip[MAX_IPADR_LEN]; /* local IP as string */ - int l_port; /* local port number */ - struct curltime started_at; /* when socket was created */ - struct curltime connected_at; /* when socket connected/got first byte */ - struct curltime first_byte_at; /* when first byte was recvd */ - int error; /* errno of last failure or 0 */ - BIT(got_first_byte); /* if first byte was received */ - BIT(accepted); /* socket was accepted, not connected */ - BIT(active); - BIT(buffer_recv); -}; - -static void cf_socket_ctx_init(struct cf_socket_ctx *ctx, - const struct Curl_addrinfo *ai, - int transport) -{ - memset(ctx, 0, sizeof(*ctx)); - ctx->sock = CURL_SOCKET_BAD; - ctx->transport = transport; - Curl_sock_assign_addr(&ctx->addr, ai, transport); - Curl_bufq_init(&ctx->recvbuf, NW_RECV_CHUNK_SIZE, NW_RECV_CHUNKS); -} - -struct reader_ctx { - struct Curl_cfilter *cf; - struct Curl_easy *data; -}; - -static ssize_t nw_in_read(void *reader_ctx, - unsigned char *buf, size_t len, - CURLcode *err) -{ - struct reader_ctx *rctx = reader_ctx; - struct cf_socket_ctx *ctx = rctx->cf->ctx; - ssize_t nread; - - *err = CURLE_OK; - nread = sread(ctx->sock, buf, len); - - if(-1 == nread) { - int sockerr = SOCKERRNO; - - if( -#ifdef WSAEWOULDBLOCK - /* This is how Windows does it */ - (WSAEWOULDBLOCK == sockerr) -#else - /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned - due to its inability to send off data without blocking. We therefore - treat both error codes the same here */ - (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr) -#endif - ) { - /* this is just a case of EWOULDBLOCK */ - *err = CURLE_AGAIN; - nread = -1; - } - else { - char buffer[STRERROR_LEN]; - - failf(rctx->data, "Recv failure: %s", - Curl_strerror(sockerr, buffer, sizeof(buffer))); - rctx->data->state.os_errno = sockerr; - *err = CURLE_RECV_ERROR; - nread = -1; - } - } - DEBUGF(LOG_CF(rctx->data, rctx->cf, "nw_in_read(len=%zu) -> %d, err=%d", - len, (int)nread, *err)); - return nread; -} - -static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_socket_ctx *ctx = cf->ctx; - - if(ctx && CURL_SOCKET_BAD != ctx->sock) { - if(ctx->active) { - /* We share our socket at cf->conn->sock[cf->sockindex] when active. - * If it is no longer there, someone has stolen (and hopefully - * closed it) and we just forget about it. - */ - if(ctx->sock == cf->conn->sock[cf->sockindex]) { - DEBUGF(LOG_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T - ", active)", ctx->sock)); - socket_close(data, cf->conn, !ctx->accepted, ctx->sock); - cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; - } - else { - DEBUGF(LOG_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T - ") no longer at conn->sock[], discarding", ctx->sock)); - /* TODO: we do not want this to happen. Need to check which - * code is messing with conn->sock[cf->sockindex] */ - } - ctx->sock = CURL_SOCKET_BAD; - if(cf->sockindex == FIRSTSOCKET) - cf->conn->remote_addr = NULL; - } - else { - /* this is our local socket, we did never publish it */ - DEBUGF(LOG_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T - ", not active)", ctx->sock)); - socket_close(data, cf->conn, !ctx->accepted, ctx->sock); - ctx->sock = CURL_SOCKET_BAD; - } - Curl_bufq_reset(&ctx->recvbuf); - ctx->active = FALSE; - ctx->buffer_recv = FALSE; - memset(&ctx->started_at, 0, sizeof(ctx->started_at)); - memset(&ctx->connected_at, 0, sizeof(ctx->connected_at)); - } - - cf->connected = FALSE; -} - -static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_socket_ctx *ctx = cf->ctx; - - cf_socket_close(cf, data); - DEBUGF(LOG_CF(data, cf, "destroy")); - Curl_bufq_free(&ctx->recvbuf); - free(ctx); - cf->ctx = NULL; -} - -static CURLcode set_local_ip(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_socket_ctx *ctx = cf->ctx; - -#ifdef HAVE_GETSOCKNAME - if(!(data->conn->handler->protocol & CURLPROTO_TFTP)) { - /* TFTP does not connect, so it cannot get the IP like this */ - - char buffer[STRERROR_LEN]; - struct Curl_sockaddr_storage ssloc; - curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage); - - memset(&ssloc, 0, sizeof(ssloc)); - if(getsockname(ctx->sock, (struct sockaddr*) &ssloc, &slen)) { - int error = SOCKERRNO; - failf(data, "getsockname() failed with errno %d: %s", - error, Curl_strerror(error, buffer, sizeof(buffer))); - return CURLE_FAILED_INIT; - } - if(!Curl_addr2string((struct sockaddr*)&ssloc, slen, - ctx->l_ip, &ctx->l_port)) { - failf(data, "ssloc inet_ntop() failed with errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - return CURLE_FAILED_INIT; - } - } -#else - (void)data; - ctx->l_ip[0] = 0; - ctx->l_port = -1; -#endif - return CURLE_OK; -} - -static CURLcode set_remote_ip(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_socket_ctx *ctx = cf->ctx; - - /* store remote address and port used in this connection attempt */ - if(!Curl_addr2string(&ctx->addr.sa_addr, ctx->addr.addrlen, - ctx->r_ip, &ctx->r_port)) { - char buffer[STRERROR_LEN]; - - ctx->error = errno; - /* malformed address or bug in inet_ntop, try next address */ - failf(data, "sa_addr inet_ntop() failed with errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - return CURLE_FAILED_INIT; - } - return CURLE_OK; -} - -static CURLcode cf_socket_open(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_socket_ctx *ctx = cf->ctx; - int error = 0; - bool isconnected = FALSE; - CURLcode result = CURLE_COULDNT_CONNECT; - bool is_tcp; - const char *ipmsg; - - (void)data; - DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD); - ctx->started_at = Curl_now(); - result = socket_open(data, &ctx->addr, &ctx->sock); - if(result) - goto out; - - result = set_remote_ip(cf, data); - if(result) - goto out; - -#ifdef ENABLE_IPV6 - if(ctx->addr.family == AF_INET6) { - set_ipv6_v6only(ctx->sock, 0); - ipmsg = " Trying [%s]:%d..."; - } - else -#endif - ipmsg = " Trying %s:%d..."; - infof(data, ipmsg, ctx->r_ip, ctx->r_port); - -#ifdef ENABLE_IPV6 - is_tcp = (ctx->addr.family == AF_INET - || ctx->addr.family == AF_INET6) && - ctx->addr.socktype == SOCK_STREAM; -#else - is_tcp = (ctx->addr.family == AF_INET) && - ctx->addr.socktype == SOCK_STREAM; -#endif - if(is_tcp && data->set.tcp_nodelay) - tcpnodelay(data, ctx->sock); - - nosigpipe(data, ctx->sock); - - Curl_sndbufset(ctx->sock); - - if(is_tcp && data->set.tcp_keepalive) - tcpkeepalive(data, ctx->sock); - - if(data->set.fsockopt) { - /* activate callback for setting socket options */ - Curl_set_in_callback(data, true); - error = data->set.fsockopt(data->set.sockopt_client, - ctx->sock, - CURLSOCKTYPE_IPCXN); - Curl_set_in_callback(data, false); - - if(error == CURL_SOCKOPT_ALREADY_CONNECTED) - isconnected = TRUE; - else if(error) { - result = CURLE_ABORTED_BY_CALLBACK; - goto out; - } - } - - /* possibly bind the local end to an IP, interface or port */ - if(ctx->addr.family == AF_INET -#ifdef ENABLE_IPV6 - || ctx->addr.family == AF_INET6 -#endif - ) { - result = bindlocal(data, cf->conn, ctx->sock, ctx->addr.family, - Curl_ipv6_scope(&ctx->addr.sa_addr)); - if(result) { - if(result == CURLE_UNSUPPORTED_PROTOCOL) { - /* The address family is not supported on this interface. - We can continue trying addresses */ - result = CURLE_COULDNT_CONNECT; - } - goto out; - } - } - - /* set socket non-blocking */ - (void)curlx_nonblock(ctx->sock, TRUE); - -out: - if(result) { - if(ctx->sock != CURL_SOCKET_BAD) { - socket_close(data, cf->conn, TRUE, ctx->sock); - ctx->sock = CURL_SOCKET_BAD; - } - } - else if(isconnected) { - set_local_ip(cf, data); - ctx->connected_at = Curl_now(); - cf->connected = TRUE; - } - DEBUGF(LOG_CF(data, cf, "cf_socket_open() -> %d, fd=%" CURL_FORMAT_SOCKET_T, - result, ctx->sock)); - return result; -} - -static int do_connect(struct Curl_cfilter *cf, struct Curl_easy *data, - bool is_tcp_fastopen) -{ - struct cf_socket_ctx *ctx = cf->ctx; -#ifdef TCP_FASTOPEN_CONNECT - int optval = 1; -#endif - int rc = -1; - - (void)data; - if(is_tcp_fastopen) { -#if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */ -# if defined(HAVE_BUILTIN_AVAILABLE) - /* while connectx function is available since macOS 10.11 / iOS 9, - it did not have the interface declared correctly until - Xcode 9 / macOS SDK 10.13 */ - if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) { - sa_endpoints_t endpoints; - endpoints.sae_srcif = 0; - endpoints.sae_srcaddr = NULL; - endpoints.sae_srcaddrlen = 0; - endpoints.sae_dstaddr = &ctx->addr.sa_addr; - endpoints.sae_dstaddrlen = ctx->addr.addrlen; - - rc = connectx(ctx->sock, &endpoints, SAE_ASSOCID_ANY, - CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT, - NULL, 0, NULL, NULL); - } - else { - rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); - } -# else - rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); -# endif /* HAVE_BUILTIN_AVAILABLE */ -#elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */ - if(setsockopt(ctx->sock, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, - (void *)&optval, sizeof(optval)) < 0) - infof(data, "Failed to enable TCP Fast Open on fd %" - CURL_FORMAT_SOCKET_T, ctx->sock); - - rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); -#elif defined(MSG_FASTOPEN) /* old Linux */ - if(cf->conn->given->flags & PROTOPT_SSL) - rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); - else - rc = 0; /* Do nothing */ -#endif - } - else { - rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); - } - return rc; -} - -static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking, bool *done) -{ - struct cf_socket_ctx *ctx = cf->ctx; - CURLcode result = CURLE_COULDNT_CONNECT; - int rc = 0; - - (void)data; - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } - - /* TODO: need to support blocking connect? */ - if(blocking) - return CURLE_UNSUPPORTED_PROTOCOL; - - *done = FALSE; /* a very negative world view is best */ - if(ctx->sock == CURL_SOCKET_BAD) { - - result = cf_socket_open(cf, data); - if(result) - goto out; - - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } - - /* Connect TCP socket */ - rc = do_connect(cf, data, cf->conn->bits.tcp_fastopen); - if(-1 == rc) { - result = socket_connect_result(data, ctx->r_ip, SOCKERRNO); - goto out; - } - } - -#ifdef mpeix - /* Call this function once now, and ignore the results. We do this to - "clear" the error state on the socket so that we can later read it - reliably. This is reported necessary on the MPE/iX operating - system. */ - (void)verifyconnect(ctx->sock, NULL); -#endif - /* check socket for connect */ - rc = SOCKET_WRITABLE(ctx->sock, 0); - - if(rc == 0) { /* no connection yet */ - DEBUGF(LOG_CF(data, cf, "not connected yet")); - return CURLE_OK; - } - else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) { - if(verifyconnect(ctx->sock, &ctx->error)) { - /* we are connected with TCP, awesome! */ - ctx->connected_at = Curl_now(); - set_local_ip(cf, data); - *done = TRUE; - cf->connected = TRUE; - DEBUGF(LOG_CF(data, cf, "connected")); - return CURLE_OK; - } - } - else if(rc & CURL_CSELECT_ERR) { - (void)verifyconnect(ctx->sock, &ctx->error); - result = CURLE_COULDNT_CONNECT; - } - -out: - if(result) { - if(ctx->error) { - data->state.os_errno = ctx->error; - SET_SOCKERRNO(ctx->error); -#ifndef CURL_DISABLE_VERBOSE_STRINGS - { - char buffer[STRERROR_LEN]; - infof(data, "connect to %s port %u failed: %s", - ctx->r_ip, ctx->r_port, - Curl_strerror(ctx->error, buffer, sizeof(buffer))); - } -#endif - } - if(ctx->sock != CURL_SOCKET_BAD) { - socket_close(data, cf->conn, TRUE, ctx->sock); - ctx->sock = CURL_SOCKET_BAD; - } - *done = FALSE; - } - return result; -} - -static void cf_socket_get_host(struct Curl_cfilter *cf, - struct Curl_easy *data, - const char **phost, - const char **pdisplay_host, - int *pport) -{ - (void)data; - *phost = cf->conn->host.name; - *pdisplay_host = cf->conn->host.dispname; - *pport = cf->conn->port; -} - -static int cf_socket_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) -{ - struct cf_socket_ctx *ctx = cf->ctx; - int rc = GETSOCK_BLANK; - - (void)data; - if(!cf->connected && ctx->sock != CURL_SOCKET_BAD) { - socks[0] = ctx->sock; - rc |= GETSOCK_WRITESOCK(0); - } - - return rc; -} - -static bool cf_socket_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data) -{ - struct cf_socket_ctx *ctx = cf->ctx; - int readable; - - (void)data; - if(!Curl_bufq_is_empty(&ctx->recvbuf)) - return TRUE; - - readable = SOCKET_READABLE(ctx->sock, 0); - return (readable > 0 && (readable & CURL_CSELECT_IN)); -} - -static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) -{ - struct cf_socket_ctx *ctx = cf->ctx; - curl_socket_t fdsave; - ssize_t nwritten; - - *err = CURLE_OK; - fdsave = cf->conn->sock[cf->sockindex]; - cf->conn->sock[cf->sockindex] = ctx->sock; - -#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */ - if(cf->conn->bits.tcp_fastopen) { - nwritten = sendto(ctx->sock, buf, len, MSG_FASTOPEN, - &cf->conn->remote_addr->sa_addr, - cf->conn->remote_addr->addrlen); - cf->conn->bits.tcp_fastopen = FALSE; - } - else -#endif - nwritten = swrite(ctx->sock, buf, len); - - if(-1 == nwritten) { - int sockerr = SOCKERRNO; - - if( -#ifdef WSAEWOULDBLOCK - /* This is how Windows does it */ - (WSAEWOULDBLOCK == sockerr) -#else - /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned - due to its inability to send off data without blocking. We therefore - treat both error codes the same here */ - (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr) || - (EINPROGRESS == sockerr) -#endif - ) { - /* this is just a case of EWOULDBLOCK */ - *err = CURLE_AGAIN; - } - else { - char buffer[STRERROR_LEN]; - failf(data, "Send failure: %s", - Curl_strerror(sockerr, buffer, sizeof(buffer))); - data->state.os_errno = sockerr; - *err = CURLE_SEND_ERROR; - } - } - - DEBUGF(LOG_CF(data, cf, "send(len=%zu) -> %d, err=%d", - len, (int)nwritten, *err)); - cf->conn->sock[cf->sockindex] = fdsave; - return nwritten; -} - -static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) -{ - struct cf_socket_ctx *ctx = cf->ctx; - curl_socket_t fdsave; - ssize_t nread; - - *err = CURLE_OK; - - fdsave = cf->conn->sock[cf->sockindex]; - cf->conn->sock[cf->sockindex] = ctx->sock; - - if(ctx->buffer_recv && !Curl_bufq_is_empty(&ctx->recvbuf)) { - DEBUGF(LOG_CF(data, cf, "recv from buffer")); - nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err); - } - else { - struct reader_ctx rctx; - - rctx.cf = cf; - rctx.data = data; - - /* "small" reads may trigger filling our buffer, "large" reads - * are probably not worth the additional copy */ - if(ctx->buffer_recv && len < NW_SMALL_READS) { - ssize_t nwritten; - nwritten = Curl_bufq_slurp(&ctx->recvbuf, nw_in_read, &rctx, err); - if(nwritten < 0 && !Curl_bufq_is_empty(&ctx->recvbuf)) { - /* we have a partial read with an error. need to deliver - * what we got, return the error later. */ - DEBUGF(LOG_CF(data, cf, "partial read: empty buffer first")); - nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err); - } - else if(nwritten < 0) { - nread = -1; - goto out; - } - else if(nwritten == 0) { - /* eof */ - *err = CURLE_OK; - nread = 0; - } - else { - DEBUGF(LOG_CF(data, cf, "buffered %zd additional bytes", nwritten)); - nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err); - } - } - else { - nread = nw_in_read(&rctx, (unsigned char *)buf, len, err); - } - } - -out: - DEBUGF(LOG_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread, - *err)); - if(nread > 0 && !ctx->got_first_byte) { - ctx->first_byte_at = Curl_now(); - ctx->got_first_byte = TRUE; - } - cf->conn->sock[cf->sockindex] = fdsave; - return nread; -} - -static void conn_set_primary_ip(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ -#ifdef HAVE_GETPEERNAME - struct cf_socket_ctx *ctx = cf->ctx; - if(!(data->conn->handler->protocol & CURLPROTO_TFTP)) { - /* TFTP does not connect the endpoint: getpeername() failed with errno - 107: Transport endpoint is not connected */ - - char buffer[STRERROR_LEN]; - struct Curl_sockaddr_storage ssrem; - curl_socklen_t plen; - int port; - - plen = sizeof(ssrem); - memset(&ssrem, 0, plen); - if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) { - int error = SOCKERRNO; - failf(data, "getpeername() failed with errno %d: %s", - error, Curl_strerror(error, buffer, sizeof(buffer))); - return; - } - if(!Curl_addr2string((struct sockaddr*)&ssrem, plen, - cf->conn->primary_ip, &port)) { - failf(data, "ssrem inet_ntop() failed with errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - return; - } - } -#else - cf->conn->primary_ip[0] = 0; - (void)data; -#endif -} - -static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_socket_ctx *ctx = cf->ctx; - - /* use this socket from now on */ - cf->conn->sock[cf->sockindex] = ctx->sock; - /* the first socket info gets set at conn and data */ - if(cf->sockindex == FIRSTSOCKET) { - cf->conn->remote_addr = &ctx->addr; - #ifdef ENABLE_IPV6 - cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE; - #endif - conn_set_primary_ip(cf, data); - set_local_ip(cf, data); - Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port); - /* buffering is currently disabled by default because we have stalls - * in parallel transfers where not all buffered data is consumed and no - * socket events happen. - */ - ctx->buffer_recv = FALSE; - } - ctx->active = TRUE; -} - -static CURLcode cf_socket_cntrl(struct Curl_cfilter *cf, - struct Curl_easy *data, - int event, int arg1, void *arg2) -{ - struct cf_socket_ctx *ctx = cf->ctx; - - (void)arg1; - (void)arg2; - switch(event) { - case CF_CTRL_CONN_INFO_UPDATE: - cf_socket_active(cf, data); - break; - case CF_CTRL_DATA_SETUP: - Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port); - break; - } - return CURLE_OK; -} - -static bool cf_socket_conn_is_alive(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *input_pending) -{ - struct cf_socket_ctx *ctx = cf->ctx; - struct pollfd pfd[1]; - int r; - - *input_pending = FALSE; - (void)data; - if(!ctx || ctx->sock == CURL_SOCKET_BAD) - return FALSE; - - /* Check with 0 timeout if there are any events pending on the socket */ - pfd[0].fd = ctx->sock; - pfd[0].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI; - pfd[0].revents = 0; - - r = Curl_poll(pfd, 1, 0); - if(r < 0) { - DEBUGF(LOG_CF(data, cf, "is_alive: poll error, assume dead")); - return FALSE; - } - else if(r == 0) { - DEBUGF(LOG_CF(data, cf, "is_alive: poll timeout, assume alive")); - return TRUE; - } - else if(pfd[0].revents & (POLLERR|POLLHUP|POLLPRI|POLLNVAL)) { - DEBUGF(LOG_CF(data, cf, "is_alive: err/hup/etc events, assume dead")); - return FALSE; - } - - DEBUGF(LOG_CF(data, cf, "is_alive: valid events, looks alive")); - *input_pending = TRUE; - return TRUE; -} - -static CURLcode cf_socket_query(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query, int *pres1, void *pres2) -{ - struct cf_socket_ctx *ctx = cf->ctx; - - switch(query) { - case CF_QUERY_SOCKET: - DEBUGASSERT(pres2); - *((curl_socket_t *)pres2) = ctx->sock; - return CURLE_OK; - case CF_QUERY_CONNECT_REPLY_MS: - if(ctx->got_first_byte) { - timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at); - *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX; - } - else - *pres1 = -1; - return CURLE_OK; - case CF_QUERY_TIMER_CONNECT: { - struct curltime *when = pres2; - switch(ctx->transport) { - case TRNSPRT_UDP: - case TRNSPRT_QUIC: - /* Since UDP connected sockets work different from TCP, we use the - * time of the first byte from the peer as the "connect" time. */ - if(ctx->got_first_byte) { - *when = ctx->first_byte_at; - break; - } - /* FALLTHROUGH */ - default: - *when = ctx->connected_at; - break; - } - return CURLE_OK; - } - default: - break; - } - return cf->next? - cf->next->cft->query(cf->next, data, query, pres1, pres2) : - CURLE_UNKNOWN_OPTION; -} - -struct Curl_cftype Curl_cft_tcp = { - "TCP", - CF_TYPE_IP_CONNECT, - CURL_LOG_DEFAULT, - cf_socket_destroy, - cf_tcp_connect, - cf_socket_close, - cf_socket_get_host, - cf_socket_get_select_socks, - cf_socket_data_pending, - cf_socket_send, - cf_socket_recv, - cf_socket_cntrl, - cf_socket_conn_is_alive, - Curl_cf_def_conn_keep_alive, - cf_socket_query, -}; - -CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai, - int transport) -{ - struct cf_socket_ctx *ctx = NULL; - struct Curl_cfilter *cf = NULL; - CURLcode result; - - (void)data; - (void)conn; - DEBUGASSERT(transport == TRNSPRT_TCP); - ctx = calloc(sizeof(*ctx), 1); - if(!ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - cf_socket_ctx_init(ctx, ai, transport); - - result = Curl_cf_create(&cf, &Curl_cft_tcp, ctx); - -out: - *pcf = (!result)? cf : NULL; - if(result) { - Curl_safefree(cf); - Curl_safefree(ctx); - } - - return result; -} - -static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_socket_ctx *ctx = cf->ctx; - int rc; - - /* QUIC needs a connected socket, nonblocking */ - DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD); - - rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); - if(-1 == rc) { - return socket_connect_result(data, ctx->r_ip, SOCKERRNO); - } - set_local_ip(cf, data); - DEBUGF(LOG_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T - " connected: [%s:%d] -> [%s:%d]", - (ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP", - ctx->sock, ctx->l_ip, ctx->l_port, ctx->r_ip, ctx->r_port)); - - (void)curlx_nonblock(ctx->sock, TRUE); - switch(ctx->addr.family) { -#if defined(__linux__) && defined(IP_MTU_DISCOVER) - case AF_INET: { - int val = IP_PMTUDISC_DO; - (void)setsockopt(ctx->sock, IPPROTO_IP, IP_MTU_DISCOVER, &val, - sizeof(val)); - break; - } -#endif -#if defined(__linux__) && defined(IPV6_MTU_DISCOVER) - case AF_INET6: { - int val = IPV6_PMTUDISC_DO; - (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, - sizeof(val)); - break; - } -#endif - } - return CURLE_OK; -} - -static CURLcode cf_udp_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking, bool *done) -{ - struct cf_socket_ctx *ctx = cf->ctx; - CURLcode result = CURLE_COULDNT_CONNECT; - - (void)blocking; - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } - *done = FALSE; - if(ctx->sock == CURL_SOCKET_BAD) { - result = cf_socket_open(cf, data); - if(result) { - DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), open failed -> %d", result)); - goto out; - } - - if(ctx->transport == TRNSPRT_QUIC) { - result = cf_udp_setup_quic(cf, data); - if(result) - goto out; - DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), opened socket=%" - CURL_FORMAT_SOCKET_T " (%s:%d)", - ctx->sock, ctx->l_ip, ctx->l_port)); - } - else { - DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), opened socket=%" - CURL_FORMAT_SOCKET_T " (unconnected)", ctx->sock)); - } - *done = TRUE; - cf->connected = TRUE; - } -out: - return result; -} - -struct Curl_cftype Curl_cft_udp = { - "UDP", - CF_TYPE_IP_CONNECT, - CURL_LOG_DEFAULT, - cf_socket_destroy, - cf_udp_connect, - cf_socket_close, - cf_socket_get_host, - cf_socket_get_select_socks, - cf_socket_data_pending, - cf_socket_send, - cf_socket_recv, - cf_socket_cntrl, - cf_socket_conn_is_alive, - Curl_cf_def_conn_keep_alive, - cf_socket_query, -}; - -CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai, - int transport) -{ - struct cf_socket_ctx *ctx = NULL; - struct Curl_cfilter *cf = NULL; - CURLcode result; - - (void)data; - (void)conn; - DEBUGASSERT(transport == TRNSPRT_UDP || transport == TRNSPRT_QUIC); - ctx = calloc(sizeof(*ctx), 1); - if(!ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - cf_socket_ctx_init(ctx, ai, transport); - - result = Curl_cf_create(&cf, &Curl_cft_udp, ctx); - -out: - *pcf = (!result)? cf : NULL; - if(result) { - Curl_safefree(cf); - Curl_safefree(ctx); - } - - return result; -} - -/* this is the TCP filter which can also handle this case */ -struct Curl_cftype Curl_cft_unix = { - "UNIX", - CF_TYPE_IP_CONNECT, - CURL_LOG_DEFAULT, - cf_socket_destroy, - cf_tcp_connect, - cf_socket_close, - cf_socket_get_host, - cf_socket_get_select_socks, - cf_socket_data_pending, - cf_socket_send, - cf_socket_recv, - cf_socket_cntrl, - cf_socket_conn_is_alive, - Curl_cf_def_conn_keep_alive, - cf_socket_query, -}; - -CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai, - int transport) -{ - struct cf_socket_ctx *ctx = NULL; - struct Curl_cfilter *cf = NULL; - CURLcode result; - - (void)data; - (void)conn; - DEBUGASSERT(transport == TRNSPRT_UNIX); - ctx = calloc(sizeof(*ctx), 1); - if(!ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - cf_socket_ctx_init(ctx, ai, transport); - - result = Curl_cf_create(&cf, &Curl_cft_unix, ctx); - -out: - *pcf = (!result)? cf : NULL; - if(result) { - Curl_safefree(cf); - Curl_safefree(ctx); - } - - return result; -} - -static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking, bool *done) -{ - /* we start accepted, if we ever close, we cannot go on */ - (void)data; - (void)blocking; - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } - return CURLE_FAILED_INIT; -} - -struct Curl_cftype Curl_cft_tcp_accept = { - "TCP-ACCEPT", - CF_TYPE_IP_CONNECT, - CURL_LOG_DEFAULT, - cf_socket_destroy, - cf_tcp_accept_connect, - cf_socket_close, - cf_socket_get_host, /* TODO: not accurate */ - cf_socket_get_select_socks, - cf_socket_data_pending, - cf_socket_send, - cf_socket_recv, - cf_socket_cntrl, - cf_socket_conn_is_alive, - Curl_cf_def_conn_keep_alive, - cf_socket_query, -}; - -CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, curl_socket_t *s) -{ - CURLcode result; - struct Curl_cfilter *cf = NULL; - struct cf_socket_ctx *ctx = NULL; - - /* replace any existing */ - Curl_conn_cf_discard_all(data, conn, sockindex); - DEBUGASSERT(conn->sock[sockindex] == CURL_SOCKET_BAD); - - ctx = calloc(sizeof(*ctx), 1); - if(!ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - ctx->transport = conn->transport; - ctx->sock = *s; - ctx->accepted = FALSE; - result = Curl_cf_create(&cf, &Curl_cft_tcp_accept, ctx); - if(result) - goto out; - Curl_conn_cf_add(data, conn, sockindex, cf); - - conn->sock[sockindex] = ctx->sock; - set_local_ip(cf, data); - ctx->active = TRUE; - ctx->connected_at = Curl_now(); - cf->connected = TRUE; - DEBUGF(LOG_CF(data, cf, "Curl_conn_tcp_listen_set(%" - CURL_FORMAT_SOCKET_T ")", ctx->sock)); - -out: - if(result) { - Curl_safefree(cf); - Curl_safefree(ctx); - } - return result; -} - -static void set_accepted_remote_ip(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_socket_ctx *ctx = cf->ctx; -#ifdef HAVE_GETPEERNAME - char buffer[STRERROR_LEN]; - struct Curl_sockaddr_storage ssrem; - curl_socklen_t plen; - - ctx->r_ip[0] = 0; - ctx->r_port = 0; - plen = sizeof(ssrem); - memset(&ssrem, 0, plen); - if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) { - int error = SOCKERRNO; - failf(data, "getpeername() failed with errno %d: %s", - error, Curl_strerror(error, buffer, sizeof(buffer))); - return; - } - if(!Curl_addr2string((struct sockaddr*)&ssrem, plen, - ctx->r_ip, &ctx->r_port)) { - failf(data, "ssrem inet_ntop() failed with errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - return; - } -#else - ctx->r_ip[0] = 0; - ctx->r_port = 0; - (void)data; -#endif -} - -CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, curl_socket_t *s) -{ - struct Curl_cfilter *cf = NULL; - struct cf_socket_ctx *ctx = NULL; - - cf = conn->cfilter[sockindex]; - if(!cf || cf->cft != &Curl_cft_tcp_accept) - return CURLE_FAILED_INIT; - - ctx = cf->ctx; - /* discard the listen socket */ - socket_close(data, conn, TRUE, ctx->sock); - ctx->sock = *s; - conn->sock[sockindex] = ctx->sock; - set_accepted_remote_ip(cf, data); - set_local_ip(cf, data); - ctx->active = TRUE; - ctx->accepted = TRUE; - ctx->connected_at = Curl_now(); - cf->connected = TRUE; - DEBUGF(LOG_CF(data, cf, "accepted_set(sock=%" CURL_FORMAT_SOCKET_T - ", remote=%s port=%d)", - ctx->sock, ctx->r_ip, ctx->r_port)); - - return CURLE_OK; -} - -/** - * Return TRUE iff `cf` is a socket filter. - */ -static bool cf_is_socket(struct Curl_cfilter *cf) -{ - return cf && (cf->cft == &Curl_cft_tcp || - cf->cft == &Curl_cft_udp || - cf->cft == &Curl_cft_unix || - cf->cft == &Curl_cft_tcp_accept); -} - -CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *psock, - const struct Curl_sockaddr_ex **paddr, - const char **pr_ip_str, int *pr_port, - const char **pl_ip_str, int *pl_port) -{ - if(cf_is_socket(cf) && cf->ctx) { - struct cf_socket_ctx *ctx = cf->ctx; - - if(psock) - *psock = ctx->sock; - if(paddr) - *paddr = &ctx->addr; - if(pr_ip_str) - *pr_ip_str = ctx->r_ip; - if(pr_port) - *pr_port = ctx->r_port; - if(pl_port ||pl_ip_str) { - set_local_ip(cf, data); - if(pl_ip_str) - *pl_ip_str = ctx->l_ip; - if(pl_port) - *pl_port = ctx->l_port; - } - return CURLE_OK; - } - return CURLE_FAILED_INIT; -} - diff --git a/contrib/libs/curl/lib/cf-socket.h b/contrib/libs/curl/lib/cf-socket.h deleted file mode 100644 index 1d40df737f..0000000000 --- a/contrib/libs/curl/lib/cf-socket.h +++ /dev/null @@ -1,191 +0,0 @@ -#ifndef HEADER_CURL_CF_SOCKET_H -#define HEADER_CURL_CF_SOCKET_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ -#include "curl_setup.h" - -#include "nonblock.h" /* for curlx_nonblock(), formerly Curl_nonblock() */ -#include "sockaddr.h" - -struct Curl_addrinfo; -struct Curl_cfilter; -struct Curl_easy; -struct connectdata; -struct Curl_sockaddr_ex; - -#ifndef SIZEOF_CURL_SOCKET_T -/* configure and cmake check and set the define */ -# ifdef _WIN64 -# define SIZEOF_CURL_SOCKET_T 8 -# else -/* default guess */ -# define SIZEOF_CURL_SOCKET_T 4 -# endif -#endif - -#if SIZEOF_CURL_SOCKET_T < 8 -# define CURL_FORMAT_SOCKET_T "d" -#else -# define CURL_FORMAT_SOCKET_T "qd" -#endif - - -/* - * The Curl_sockaddr_ex structure is basically libcurl's external API - * curl_sockaddr structure with enough space available to directly hold any - * protocol-specific address structures. The variable declared here will be - * used to pass / receive data to/from the fopensocket callback if this has - * been set, before that, it is initialized from parameters. - */ -struct Curl_sockaddr_ex { - int family; - int socktype; - int protocol; - unsigned int addrlen; - union { - struct sockaddr addr; - struct Curl_sockaddr_storage buff; - } _sa_ex_u; -}; -#define sa_addr _sa_ex_u.addr - - -/* - * Create a socket based on info from 'conn' and 'ai'. - * - * Fill in 'addr' and 'sockfd' accordingly if OK is returned. If the open - * socket callback is set, used that! - * - */ -CURLcode Curl_socket_open(struct Curl_easy *data, - const struct Curl_addrinfo *ai, - struct Curl_sockaddr_ex *addr, - int transport, - curl_socket_t *sockfd); - -int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t sock); - -#ifdef USE_WINSOCK -/* When you run a program that uses the Windows Sockets API, you may - experience slow performance when you copy data to a TCP server. - - https://support.microsoft.com/kb/823764 - - Work-around: Make the Socket Send Buffer Size Larger Than the Program Send - Buffer Size - -*/ -void Curl_sndbufset(curl_socket_t sockfd); -#else -#define Curl_sndbufset(y) Curl_nop_stmt -#endif - -/** - * Assign the address `ai` to the Curl_sockaddr_ex `dest` and - * set the transport used. - */ -void Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest, - const struct Curl_addrinfo *ai, - int transport); - -/** - * Creates a cfilter that opens a TCP socket to the given address - * when calling its `connect` implementation. - * The filter will not touch any connection/data flags and can be - * used in happy eyeballing. Once selected for use, its `_active()` - * method needs to be called. - */ -CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai, - int transport); - -/** - * Creates a cfilter that opens a UDP socket to the given address - * when calling its `connect` implementation. - * The filter will not touch any connection/data flags and can be - * used in happy eyeballing. Once selected for use, its `_active()` - * method needs to be called. - */ -CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai, - int transport); - -/** - * Creates a cfilter that opens a UNIX socket to the given address - * when calling its `connect` implementation. - * The filter will not touch any connection/data flags and can be - * used in happy eyeballing. Once selected for use, its `_active()` - * method needs to be called. - */ -CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai, - int transport); - -/** - * Creates a cfilter that keeps a listening socket. - */ -CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - curl_socket_t *s); - -/** - * Replace the listen socket with the accept()ed one. - */ -CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - curl_socket_t *s); - -/** - * Peek at the socket and remote ip/port the socket filter is using. - * The filter owns all returned values. - * @param psock pointer to hold socket descriptor or NULL - * @param paddr pointer to hold addr reference or NULL - * @param pr_ip_str pointer to hold remote addr as string or NULL - * @param pr_port pointer to hold remote port number or NULL - * @param pl_ip_str pointer to hold local addr as string or NULL - * @param pl_port pointer to hold local port number or NULL - * Returns error if the filter is of invalid type. - */ -CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *psock, - const struct Curl_sockaddr_ex **paddr, - const char **pr_ip_str, int *pr_port, - const char **pl_ip_str, int *pl_port); - -extern struct Curl_cftype Curl_cft_tcp; -extern struct Curl_cftype Curl_cft_udp; -extern struct Curl_cftype Curl_cft_unix; -extern struct Curl_cftype Curl_cft_tcp_accept; - -#endif /* HEADER_CURL_CF_SOCKET_H */ diff --git a/contrib/libs/curl/lib/cfilters.c b/contrib/libs/curl/lib/cfilters.c deleted file mode 100644 index 216d0b4f68..0000000000 --- a/contrib/libs/curl/lib/cfilters.c +++ /dev/null @@ -1,649 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#include "urldata.h" -#include "strerror.h" -#include "cfilters.h" -#include "connect.h" -#include "url.h" /* for Curl_safefree() */ -#include "sendf.h" -#include "sockaddr.h" /* required for Curl_sockaddr_storage */ -#include "multiif.h" -#include "progress.h" -#include "warnless.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#ifndef ARRAYSIZE -#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) -#endif - -#ifdef DEBUGBUILD -/* used by unit2600.c */ -void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - cf->connected = FALSE; - if(cf->next) - cf->next->cft->do_close(cf->next, data); -} -#endif - -static void conn_report_connect_stats(struct Curl_easy *data, - struct connectdata *conn); - -void Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data, - const char **phost, const char **pdisplay_host, - int *pport) -{ - if(cf->next) - cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport); - else { - *phost = cf->conn->host.name; - *pdisplay_host = cf->conn->host.dispname; - *pport = cf->conn->port; - } -} - -int Curl_cf_def_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) -{ - return cf->next? - cf->next->cft->get_select_socks(cf->next, data, socks) : 0; -} - -bool Curl_cf_def_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data) -{ - return cf->next? - cf->next->cft->has_data_pending(cf->next, data) : FALSE; -} - -ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) -{ - return cf->next? - cf->next->cft->do_send(cf->next, data, buf, len, err) : - CURLE_RECV_ERROR; -} - -ssize_t Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) -{ - return cf->next? - cf->next->cft->do_recv(cf->next, data, buf, len, err) : - CURLE_SEND_ERROR; -} - -bool Curl_cf_def_conn_is_alive(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *input_pending) -{ - return cf->next? - cf->next->cft->is_alive(cf->next, data, input_pending) : - FALSE; /* pessimistic in absence of data */ -} - -CURLcode Curl_cf_def_conn_keep_alive(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - return cf->next? - cf->next->cft->keep_alive(cf->next, data) : - CURLE_OK; -} - -CURLcode Curl_cf_def_query(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query, int *pres1, void *pres2) -{ - return cf->next? - cf->next->cft->query(cf->next, data, query, pres1, pres2) : - CURLE_UNKNOWN_OPTION; -} - -void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf, - struct Curl_easy *data) -{ - struct Curl_cfilter *cfn, *cf = *pcf; - - if(cf) { - *pcf = NULL; - while(cf) { - cfn = cf->next; - /* prevent destroying filter to mess with its sub-chain, since - * we have the reference now and will call destroy on it. - */ - cf->next = NULL; - cf->cft->destroy(cf, data); - free(cf); - cf = cfn; - } - } -} - -void Curl_conn_cf_discard_all(struct Curl_easy *data, - struct connectdata *conn, int index) -{ - Curl_conn_cf_discard_chain(&conn->cfilter[index], data); -} - -void Curl_conn_close(struct Curl_easy *data, int index) -{ - struct Curl_cfilter *cf; - - DEBUGASSERT(data->conn); - /* it is valid to call that without filters being present */ - cf = data->conn->cfilter[index]; - if(cf) { - cf->cft->do_close(cf, data); - } -} - -ssize_t Curl_conn_recv(struct Curl_easy *data, int num, char *buf, - size_t len, CURLcode *code) -{ - struct Curl_cfilter *cf; - - DEBUGASSERT(data); - DEBUGASSERT(data->conn); - cf = data->conn->cfilter[num]; - while(cf && !cf->connected) { - cf = cf->next; - } - if(cf) { - return cf->cft->do_recv(cf, data, buf, len, code); - } - failf(data, "recv: no filter connected"); - *code = CURLE_FAILED_INIT; - return -1; -} - -ssize_t Curl_conn_send(struct Curl_easy *data, int num, - const void *mem, size_t len, CURLcode *code) -{ - struct Curl_cfilter *cf; - - DEBUGASSERT(data); - DEBUGASSERT(data->conn); - cf = data->conn->cfilter[num]; - while(cf && !cf->connected) { - cf = cf->next; - } - if(cf) { - return cf->cft->do_send(cf, data, mem, len, code); - } - failf(data, "send: no filter connected"); - DEBUGASSERT(0); - *code = CURLE_FAILED_INIT; - return -1; -} - -CURLcode Curl_cf_create(struct Curl_cfilter **pcf, - const struct Curl_cftype *cft, - void *ctx) -{ - struct Curl_cfilter *cf; - CURLcode result = CURLE_OUT_OF_MEMORY; - - DEBUGASSERT(cft); - cf = calloc(sizeof(*cf), 1); - if(!cf) - goto out; - - cf->cft = cft; - cf->ctx = ctx; - result = CURLE_OK; -out: - *pcf = cf; - return result; -} - -void Curl_conn_cf_add(struct Curl_easy *data, - struct connectdata *conn, - int index, - struct Curl_cfilter *cf) -{ - (void)data; - DEBUGASSERT(conn); - DEBUGASSERT(!cf->conn); - DEBUGASSERT(!cf->next); - - cf->next = conn->cfilter[index]; - cf->conn = conn; - cf->sockindex = index; - conn->cfilter[index] = cf; - DEBUGF(LOG_CF(data, cf, "added")); -} - -void Curl_conn_cf_insert_after(struct Curl_cfilter *cf_at, - struct Curl_cfilter *cf_new) -{ - struct Curl_cfilter *tail, **pnext; - - DEBUGASSERT(cf_at); - DEBUGASSERT(cf_new); - DEBUGASSERT(!cf_new->conn); - - tail = cf_at->next; - cf_at->next = cf_new; - do { - cf_new->conn = cf_at->conn; - cf_new->sockindex = cf_at->sockindex; - pnext = &cf_new->next; - cf_new = cf_new->next; - } while(cf_new); - *pnext = tail; -} - -bool Curl_conn_cf_discard_sub(struct Curl_cfilter *cf, - struct Curl_cfilter *discard, - struct Curl_easy *data, - bool destroy_always) -{ - struct Curl_cfilter **pprev = &cf->next; - bool found = FALSE; - - /* remove from sub-chain and destroy */ - DEBUGASSERT(cf); - while(*pprev) { - if(*pprev == cf) { - *pprev = discard->next; - discard->next = NULL; - found = TRUE; - break; - } - pprev = &((*pprev)->next); - } - if(found || destroy_always) { - discard->next = NULL; - discard->cft->destroy(discard, data); - free(discard); - } - return found; -} - -CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking, bool *done) -{ - if(cf) - return cf->cft->do_connect(cf, data, blocking, done); - return CURLE_FAILED_INIT; -} - -void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - if(cf) - cf->cft->do_close(cf, data); -} - -int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) -{ - if(cf) - return cf->cft->get_select_socks(cf, data, socks); - return 0; -} - -ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) -{ - if(cf) - return cf->cft->do_send(cf, data, buf, len, err); - *err = CURLE_SEND_ERROR; - return -1; -} - -ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) -{ - if(cf) - return cf->cft->do_recv(cf, data, buf, len, err); - *err = CURLE_RECV_ERROR; - return -1; -} - -CURLcode Curl_conn_connect(struct Curl_easy *data, - int sockindex, - bool blocking, - bool *done) -{ - struct Curl_cfilter *cf; - CURLcode result = CURLE_OK; - - DEBUGASSERT(data); - DEBUGASSERT(data->conn); - - cf = data->conn->cfilter[sockindex]; - DEBUGASSERT(cf); - if(!cf) - return CURLE_FAILED_INIT; - - *done = cf->connected; - if(!*done) { - result = cf->cft->do_connect(cf, data, blocking, done); - if(!result && *done) { - Curl_conn_ev_update_info(data, data->conn); - conn_report_connect_stats(data, data->conn); - data->conn->keepalive = Curl_now(); - } - else if(result) { - conn_report_connect_stats(data, data->conn); - } - } - - return result; -} - -bool Curl_conn_is_connected(struct connectdata *conn, int sockindex) -{ - struct Curl_cfilter *cf; - - cf = conn->cfilter[sockindex]; - return cf && cf->connected; -} - -bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex) -{ - struct Curl_cfilter *cf; - - cf = data->conn->cfilter[sockindex]; - while(cf) { - if(cf->connected) - return TRUE; - if(cf->cft->flags & CF_TYPE_IP_CONNECT) - return FALSE; - cf = cf->next; - } - return FALSE; -} - -bool Curl_conn_cf_is_ssl(struct Curl_cfilter *cf) -{ - for(; cf; cf = cf->next) { - if(cf->cft->flags & CF_TYPE_SSL) - return TRUE; - if(cf->cft->flags & CF_TYPE_IP_CONNECT) - return FALSE; - } - return FALSE; -} - -bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex) -{ - return conn? Curl_conn_cf_is_ssl(conn->cfilter[sockindex]) : FALSE; -} - -bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex) -{ - struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL; - - for(; cf; cf = cf->next) { - if(cf->cft->flags & CF_TYPE_MULTIPLEX) - return TRUE; - if(cf->cft->flags & CF_TYPE_IP_CONNECT - || cf->cft->flags & CF_TYPE_SSL) - return FALSE; - } - return FALSE; -} - -bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex) -{ - struct Curl_cfilter *cf; - - (void)data; - DEBUGASSERT(data); - DEBUGASSERT(data->conn); - - cf = data->conn->cfilter[sockindex]; - while(cf && !cf->connected) { - cf = cf->next; - } - if(cf) { - return cf->cft->has_data_pending(cf, data); - } - return FALSE; -} - -int Curl_conn_get_select_socks(struct Curl_easy *data, int sockindex, - curl_socket_t *socks) -{ - struct Curl_cfilter *cf; - - DEBUGASSERT(data); - DEBUGASSERT(data->conn); - cf = data->conn->cfilter[sockindex]; - - /* if the next one is not yet connected, that's the one we want */ - while(cf && cf->next && !cf->next->connected) - cf = cf->next; - if(cf) { - return cf->cft->get_select_socks(cf, data, socks); - } - return GETSOCK_BLANK; -} - -void Curl_conn_get_host(struct Curl_easy *data, int sockindex, - const char **phost, const char **pdisplay_host, - int *pport) -{ - struct Curl_cfilter *cf; - - DEBUGASSERT(data->conn); - cf = data->conn->cfilter[sockindex]; - if(cf) { - cf->cft->get_host(cf, data, phost, pdisplay_host, pport); - } - else { - /* Some filter ask during shutdown for this, mainly for debugging - * purposes. We hand out the defaults, however this is not always - * accurate, as the connection might be tunneled, etc. But all that - * state is already gone here. */ - *phost = data->conn->host.name; - *pdisplay_host = data->conn->host.dispname; - *pport = data->conn->remote_port; - } -} - -CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf, - struct Curl_easy *data, - int event, int arg1, void *arg2) -{ - (void)cf; - (void)data; - (void)event; - (void)arg1; - (void)arg2; - return CURLE_OK; -} - -CURLcode Curl_conn_cf_cntrl(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool ignore_result, - int event, int arg1, void *arg2) -{ - CURLcode result = CURLE_OK; - - for(; cf; cf = cf->next) { - if(Curl_cf_def_cntrl == cf->cft->cntrl) - continue; - result = cf->cft->cntrl(cf, data, event, arg1, arg2); - if(!ignore_result && result) - break; - } - return result; -} - -curl_socket_t Curl_conn_cf_get_socket(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - curl_socket_t sock; - if(cf && !cf->cft->query(cf, data, CF_QUERY_SOCKET, NULL, &sock)) - return sock; - return CURL_SOCKET_BAD; -} - -curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex) -{ - struct Curl_cfilter *cf; - - cf = data->conn? data->conn->cfilter[sockindex] : NULL; - /* if the top filter has not connected, ask it (and its sub-filters) - * for the socket. Otherwise conn->sock[sockindex] should have it. - */ - if(cf && !cf->connected) - return Curl_conn_cf_get_socket(cf, data); - return data->conn? data->conn->sock[sockindex] : CURL_SOCKET_BAD; -} - -static CURLcode cf_cntrl_all(struct connectdata *conn, - struct Curl_easy *data, - bool ignore_result, - int event, int arg1, void *arg2) -{ - CURLcode result = CURLE_OK; - size_t i; - - for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) { - result = Curl_conn_cf_cntrl(conn->cfilter[i], data, ignore_result, - event, arg1, arg2); - if(!ignore_result && result) - break; - } - return result; -} - -void Curl_conn_ev_data_attach(struct connectdata *conn, - struct Curl_easy *data) -{ - cf_cntrl_all(conn, data, TRUE, CF_CTRL_DATA_ATTACH, 0, NULL); -} - -void Curl_conn_ev_data_detach(struct connectdata *conn, - struct Curl_easy *data) -{ - cf_cntrl_all(conn, data, TRUE, CF_CTRL_DATA_DETACH, 0, NULL); -} - -CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data) -{ - return cf_cntrl_all(data->conn, data, FALSE, - CF_CTRL_DATA_SETUP, 0, NULL); -} - -CURLcode Curl_conn_ev_data_idle(struct Curl_easy *data) -{ - return cf_cntrl_all(data->conn, data, FALSE, - CF_CTRL_DATA_IDLE, 0, NULL); -} - -/** - * Notify connection filters that the transfer represented by `data` - * is donw with sending data (e.g. has uploaded everything). - */ -void Curl_conn_ev_data_done_send(struct Curl_easy *data) -{ - cf_cntrl_all(data->conn, data, TRUE, CF_CTRL_DATA_DONE_SEND, 0, NULL); -} - -/** - * Notify connection filters that the transfer represented by `data` - * is finished - eventually premature, e.g. before being complete. - */ -void Curl_conn_ev_data_done(struct Curl_easy *data, bool premature) -{ - cf_cntrl_all(data->conn, data, TRUE, CF_CTRL_DATA_DONE, premature, NULL); -} - -CURLcode Curl_conn_ev_data_pause(struct Curl_easy *data, bool do_pause) -{ - return cf_cntrl_all(data->conn, data, FALSE, - CF_CTRL_DATA_PAUSE, do_pause, NULL); -} - -void Curl_conn_ev_update_info(struct Curl_easy *data, - struct connectdata *conn) -{ - cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL); -} - -/** - * Update connection statistics - */ -static void conn_report_connect_stats(struct Curl_easy *data, - struct connectdata *conn) -{ - struct Curl_cfilter *cf = conn->cfilter[FIRSTSOCKET]; - if(cf) { - struct curltime connected; - struct curltime appconnected; - - memset(&connected, 0, sizeof(connected)); - cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected); - if(connected.tv_sec || connected.tv_usec) - Curl_pgrsTimeWas(data, TIMER_CONNECT, connected); - - memset(&appconnected, 0, sizeof(appconnected)); - cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected); - if(appconnected.tv_sec || appconnected.tv_usec) - Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected); - } -} - -bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn, - bool *input_pending) -{ - struct Curl_cfilter *cf = conn->cfilter[FIRSTSOCKET]; - return cf && !cf->conn->bits.close && - cf->cft->is_alive(cf, data, input_pending); -} - -CURLcode Curl_conn_keep_alive(struct Curl_easy *data, - struct connectdata *conn, - int sockindex) -{ - struct Curl_cfilter *cf = conn->cfilter[sockindex]; - return cf? cf->cft->keep_alive(cf, data) : CURLE_OK; -} - -size_t Curl_conn_get_max_concurrent(struct Curl_easy *data, - struct connectdata *conn, - int sockindex) -{ - CURLcode result; - int n = 0; - - struct Curl_cfilter *cf = conn->cfilter[sockindex]; - result = cf? cf->cft->query(cf, data, CF_QUERY_MAX_CONCURRENT, - &n, NULL) : CURLE_UNKNOWN_OPTION; - return (result || n <= 0)? 1 : (size_t)n; -} - diff --git a/contrib/libs/curl/lib/cfilters.h b/contrib/libs/curl/lib/cfilters.h deleted file mode 100644 index 2c65264d98..0000000000 --- a/contrib/libs/curl/lib/cfilters.h +++ /dev/null @@ -1,539 +0,0 @@ -#ifndef HEADER_CURL_CFILTERS_H -#define HEADER_CURL_CFILTERS_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - - -struct Curl_cfilter; -struct Curl_easy; -struct Curl_dns_entry; -struct connectdata; - -/* Callback to destroy resources held by this filter instance. - * Implementations MUST NOT chain calls to cf->next. - */ -typedef void Curl_cft_destroy_this(struct Curl_cfilter *cf, - struct Curl_easy *data); - -typedef void Curl_cft_close(struct Curl_cfilter *cf, - struct Curl_easy *data); - -typedef CURLcode Curl_cft_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking, bool *done); - -/* Return the hostname and port the connection goes to. - * This may change with the connection state of filters when tunneling - * is involved. - * @param cf the filter to ask - * @param data the easy handle currently active - * @param phost on return, points to the relevant, real hostname. - * this is owned by the connection. - * @param pdisplay_host on return, points to the printable hostname. - * this is owned by the connection. - * @param pport on return, contains the port number - */ -typedef void Curl_cft_get_host(struct Curl_cfilter *cf, - struct Curl_easy *data, - const char **phost, - const char **pdisplay_host, - int *pport); - -/* Filters may return sockets and fdset flags they are waiting for. - * The passes array has room for up to MAX_SOCKSPEREASYHANDLE sockets. - * @return read/write fdset for index in socks - * or GETSOCK_BLANK when nothing to wait on - */ -typedef int Curl_cft_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks); - -typedef bool Curl_cft_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data); - -typedef ssize_t Curl_cft_send(struct Curl_cfilter *cf, - struct Curl_easy *data, /* transfer */ - const void *buf, /* data to write */ - size_t len, /* amount to write */ - CURLcode *err); /* error to return */ - -typedef ssize_t Curl_cft_recv(struct Curl_cfilter *cf, - struct Curl_easy *data, /* transfer */ - char *buf, /* store data here */ - size_t len, /* amount to read */ - CURLcode *err); /* error to return */ - -typedef bool Curl_cft_conn_is_alive(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *input_pending); - -typedef CURLcode Curl_cft_conn_keep_alive(struct Curl_cfilter *cf, - struct Curl_easy *data); - -/** - * Events/controls for connection filters, their arguments and - * return code handling. Filter callbacks are invoked "top down". - * Return code handling: - * "first fail" meaning that the first filter returning != CURLE_OK, will - * abort further event distribution and determine the result. - * "ignored" meaning return values are ignored and the event is distributed - * to all filters in the chain. Overall result is always CURLE_OK. - */ -/* data event arg1 arg2 return */ -#define CF_CTRL_DATA_ATTACH 1 /* 0 NULL ignored */ -#define CF_CTRL_DATA_DETACH 2 /* 0 NULL ignored */ -#define CF_CTRL_DATA_SETUP 4 /* 0 NULL first fail */ -#define CF_CTRL_DATA_IDLE 5 /* 0 NULL first fail */ -#define CF_CTRL_DATA_PAUSE 6 /* on/off NULL first fail */ -#define CF_CTRL_DATA_DONE 7 /* premature NULL ignored */ -#define CF_CTRL_DATA_DONE_SEND 8 /* 0 NULL ignored */ -/* update conn info at connection and data */ -#define CF_CTRL_CONN_INFO_UPDATE (256+0) /* 0 NULL ignored */ - -/** - * Handle event/control for the filter. - * Implementations MUST NOT chain calls to cf->next. - */ -typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf, - struct Curl_easy *data, - int event, int arg1, void *arg2); - - -/** - * Queries to ask via a `Curl_cft_query *query` method on a cfilter chain. - * - MAX_CONCURRENT: the maximum number of parallel transfers the filter - * chain expects to handle at the same time. - * default: 1 if no filter overrides. - * - CONNECT_REPLY_MS: milliseconds until the first indication of a server - * response was received on a connect. For TCP, this - * reflects the time until the socket connected. On UDP - * this gives the time the first bytes from the server - * were received. - * -1 if not determined yet. - * - CF_QUERY_SOCKET: the socket used by the filter chain - */ -/* query res1 res2 */ -#define CF_QUERY_MAX_CONCURRENT 1 /* number - */ -#define CF_QUERY_CONNECT_REPLY_MS 2 /* number - */ -#define CF_QUERY_SOCKET 3 /* - curl_socket_t */ -#define CF_QUERY_TIMER_CONNECT 4 /* - struct curltime */ -#define CF_QUERY_TIMER_APPCONNECT 5 /* - struct curltime */ - -/** - * Query the cfilter for properties. Filters ignorant of a query will - * pass it "down" the filter chain. - */ -typedef CURLcode Curl_cft_query(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query, int *pres1, void *pres2); - -/** - * Type flags for connection filters. A filter can have none, one or - * many of those. Use to evaluate state/capabilities of a filter chain. - * - * CF_TYPE_IP_CONNECT: provides an IP connection or sth equivalent, like - * a CONNECT tunnel, a UNIX domain socket, a QUIC - * connection, etc. - * CF_TYPE_SSL: provide SSL/TLS - * CF_TYPE_MULTIPLEX: provides multiplexing of easy handles - */ -#define CF_TYPE_IP_CONNECT (1 << 0) -#define CF_TYPE_SSL (1 << 1) -#define CF_TYPE_MULTIPLEX (1 << 2) - -/* A connection filter type, e.g. specific implementation. */ -struct Curl_cftype { - const char *name; /* name of the filter type */ - int flags; /* flags of filter type */ - int log_level; /* log level for such filters */ - Curl_cft_destroy_this *destroy; /* destroy resources of this cf */ - Curl_cft_connect *do_connect; /* establish connection */ - Curl_cft_close *do_close; /* close conn */ - Curl_cft_get_host *get_host; /* host filter talks to */ - Curl_cft_get_select_socks *get_select_socks;/* sockets to select on */ - Curl_cft_data_pending *has_data_pending;/* conn has data pending */ - Curl_cft_send *do_send; /* send data */ - Curl_cft_recv *do_recv; /* receive data */ - Curl_cft_cntrl *cntrl; /* events/control */ - Curl_cft_conn_is_alive *is_alive; /* FALSE if conn is dead, Jim! */ - Curl_cft_conn_keep_alive *keep_alive; /* try to keep it alive */ - Curl_cft_query *query; /* query filter chain */ -}; - -/* A connection filter instance, e.g. registered at a connection */ -struct Curl_cfilter { - const struct Curl_cftype *cft; /* the type providing implementation */ - struct Curl_cfilter *next; /* next filter in chain */ - void *ctx; /* filter type specific settings */ - struct connectdata *conn; /* the connection this filter belongs to */ - int sockindex; /* the index the filter is installed at */ - BIT(connected); /* != 0 iff this filter is connected */ -}; - -/* Default implementations for the type functions, implementing nop. */ -void Curl_cf_def_destroy_this(struct Curl_cfilter *cf, - struct Curl_easy *data); - -/* Default implementations for the type functions, implementing pass-through - * the filter chain. */ -void Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data, - const char **phost, const char **pdisplay_host, - int *pport); -int Curl_cf_def_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks); -bool Curl_cf_def_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data); -ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err); -ssize_t Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err); -CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf, - struct Curl_easy *data, - int event, int arg1, void *arg2); -bool Curl_cf_def_conn_is_alive(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *input_pending); -CURLcode Curl_cf_def_conn_keep_alive(struct Curl_cfilter *cf, - struct Curl_easy *data); -CURLcode Curl_cf_def_query(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query, int *pres1, void *pres2); - -/** - * Create a new filter instance, unattached to the filter chain. - * Use Curl_conn_cf_add() to add it to the chain. - * @param pcf on success holds the created instance - * @param cft the filter type - * @param ctx the type specific context to use - */ -CURLcode Curl_cf_create(struct Curl_cfilter **pcf, - const struct Curl_cftype *cft, - void *ctx); - -/** - * Add a filter instance to the `sockindex` filter chain at connection - * `conn`. The filter must not already be attached. It is inserted at - * the start of the chain (top). - */ -void Curl_conn_cf_add(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - struct Curl_cfilter *cf); - -/** - * Insert a filter (chain) after `cf_at`. - * `cf_new` must not already be attached. - */ -void Curl_conn_cf_insert_after(struct Curl_cfilter *cf_at, - struct Curl_cfilter *cf_new); - -/** - * Discard, e.g. remove and destroy `discard` iff - * it still is in the filter chain below `cf`. If `discard` - * is no longer found beneath `cf` return FALSE. - * if `destroy_always` is TRUE, will call `discard`s destroy - * function and free it even if not found in the subchain. - */ -bool Curl_conn_cf_discard_sub(struct Curl_cfilter *cf, - struct Curl_cfilter *discard, - struct Curl_easy *data, - bool destroy_always); - -/** - * Discard all cfilters starting with `*pcf` and clearing it afterwards. - */ -void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf, - struct Curl_easy *data); - -/** - * Remove and destroy all filters at chain `sockindex` on connection `conn`. - */ -void Curl_conn_cf_discard_all(struct Curl_easy *data, - struct connectdata *conn, - int sockindex); - - -CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking, bool *done); -void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data); -int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks); -ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err); -ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err); -CURLcode Curl_conn_cf_cntrl(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool ignore_result, - int event, int arg1, void *arg2); - -/** - * Determine if the connection filter chain is using SSL to the remote host - * (or will be once connected). - */ -bool Curl_conn_cf_is_ssl(struct Curl_cfilter *cf); - -/** - * Get the socket used by the filter chain starting at `cf`. - * Returns CURL_SOCKET_BAD if not available. - */ -curl_socket_t Curl_conn_cf_get_socket(struct Curl_cfilter *cf, - struct Curl_easy *data); - - -#define CURL_CF_SSL_DEFAULT -1 -#define CURL_CF_SSL_DISABLE 0 -#define CURL_CF_SSL_ENABLE 1 - -/** - * Bring the filter chain at `sockindex` for connection `data->conn` into - * connected state. Which will set `*done` to TRUE. - * This can be called on an already connected chain with no side effects. - * When not `blocking`, calls may return without error and `*done != TRUE`, - * while the individual filters negotiated the connection. - */ -CURLcode Curl_conn_connect(struct Curl_easy *data, int sockindex, - bool blocking, bool *done); - -/** - * Check if the filter chain at `sockindex` for connection `conn` is - * completely connected. - */ -bool Curl_conn_is_connected(struct connectdata *conn, int sockindex); - -/** - * Determine if we have reached the remote host on IP level, e.g. - * have a TCP connection. This turns TRUE before a possible SSL - * handshake has been started/done. - */ -bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex); - -/** - * Determine if the connection is using SSL to the remote host - * (or will be once connected). This will return FALSE, if SSL - * is only used in proxying and not for the tunnel itself. - */ -bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex); - -/** - * Connection provides multiplexing of easy handles at `socketindex`. - */ -bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex); - -/** - * Close the filter chain at `sockindex` for connection `data->conn`. - * Filters remain in place and may be connected again afterwards. - */ -void Curl_conn_close(struct Curl_easy *data, int sockindex); - -/** - * Return if data is pending in some connection filter at chain - * `sockindex` for connection `data->conn`. - */ -bool Curl_conn_data_pending(struct Curl_easy *data, - int sockindex); - -/** - * Return the socket used on data's connection for the index. - * Returns CURL_SOCKET_BAD if not available. - */ -curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex); - -/** - * Get any select fd flags and the socket filters at chain `sockindex` - * at connection `conn` might be waiting for. - */ -int Curl_conn_get_select_socks(struct Curl_easy *data, int sockindex, - curl_socket_t *socks); - -/** - * Receive data through the filter chain at `sockindex` for connection - * `data->conn`. Copy at most `len` bytes into `buf`. Return the - * actuel number of bytes copied or a negative value on error. - * The error code is placed into `*code`. - */ -ssize_t Curl_conn_recv(struct Curl_easy *data, int sockindex, char *buf, - size_t len, CURLcode *code); - -/** - * Send `len` bytes of data from `buf` through the filter chain `sockindex` - * at connection `data->conn`. Return the actual number of bytes written - * or a negative value on error. - * The error code is placed into `*code`. - */ -ssize_t Curl_conn_send(struct Curl_easy *data, int sockindex, - const void *buf, size_t len, CURLcode *code); - -/** - * The easy handle `data` is being attached to `conn`. This does - * not mean that data will actually do a transfer. Attachment is - * also used for temporary actions on the connection. - */ -void Curl_conn_ev_data_attach(struct connectdata *conn, - struct Curl_easy *data); - -/** - * The easy handle `data` is being detached (no longer served) - * by connection `conn`. All filters are informed to release any resources - * related to `data`. - * Note: there may be several `data` attached to a connection at the same - * time. - */ -void Curl_conn_ev_data_detach(struct connectdata *conn, - struct Curl_easy *data); - -/** - * Notify connection filters that they need to setup data for - * a transfer. - */ -CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data); - -/** - * Notify connection filters that now would be a good time to - * perform any idle, e.g. time related, actions. - */ -CURLcode Curl_conn_ev_data_idle(struct Curl_easy *data); - -/** - * Notify connection filters that the transfer represented by `data` - * is donw with sending data (e.g. has uploaded everything). - */ -void Curl_conn_ev_data_done_send(struct Curl_easy *data); - -/** - * Notify connection filters that the transfer represented by `data` - * is finished - eventually premature, e.g. before being complete. - */ -void Curl_conn_ev_data_done(struct Curl_easy *data, bool premature); - -/** - * Notify connection filters that the transfer of data is paused/unpaused. - */ -CURLcode Curl_conn_ev_data_pause(struct Curl_easy *data, bool do_pause); - -/** - * Inform connection filters to update their info in `conn`. - */ -void Curl_conn_ev_update_info(struct Curl_easy *data, - struct connectdata *conn); - -/** - * Check if FIRSTSOCKET's cfilter chain deems connection alive. - */ -bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn, - bool *input_pending); - -/** - * Try to upkeep the connection filters at sockindex. - */ -CURLcode Curl_conn_keep_alive(struct Curl_easy *data, - struct connectdata *conn, - int sockindex); - -void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data); -void Curl_conn_get_host(struct Curl_easy *data, int sockindex, - const char **phost, const char **pdisplay_host, - int *pport); - -/** - * Get the maximum number of parallel transfers the connection - * expects to be able to handle at `sockindex`. - */ -size_t Curl_conn_get_max_concurrent(struct Curl_easy *data, - struct connectdata *conn, - int sockindex); - - -/** - * Types and macros used to keep the current easy handle in filter calls, - * allowing for nested invocations. See #10336. - * - * `cf_call_data` is intended to be a member of the cfilter's `ctx` type. - * A filter defines the macro `CF_CTX_CALL_DATA` to give access to that. - * - * With all values 0, the default, this indicates that there is no cfilter - * call with `data` ongoing. - * Macro `CF_DATA_SAVE` preserves the current `cf_call_data` in a local - * variable and sets the `data` given, incrementing the `depth` counter. - * - * Macro `CF_DATA_RESTORE` restores the old values from the local variable, - * while checking that `depth` values are as expected (debug build), catching - * cases where a "lower" RESTORE was not called. - * - * Finally, macro `CF_DATA_CURRENT` gives the easy handle of the current - * invocation. - */ -struct cf_call_data { - struct Curl_easy *data; -#ifdef DEBUGBUILD - int depth; -#endif -}; - -/** - * define to access the `struct cf_call_data for a cfilter. Normally - * a member in the cfilter's `ctx`. - * - * #define CF_CTX_CALL_DATA(cf) -> struct cf_call_data instance -*/ - -#ifdef DEBUGBUILD - -#define CF_DATA_SAVE(save, cf, data) \ - do { \ - (save) = CF_CTX_CALL_DATA(cf); \ - DEBUGASSERT((save).data == NULL || (save).depth > 0); \ - CF_CTX_CALL_DATA(cf).depth++; \ - CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)data; \ - } while(0) - -#define CF_DATA_RESTORE(cf, save) \ - do { \ - DEBUGASSERT(CF_CTX_CALL_DATA(cf).depth == (save).depth + 1); \ - DEBUGASSERT((save).data == NULL || (save).depth > 0); \ - CF_CTX_CALL_DATA(cf) = (save); \ - } while(0) - -#else /* DEBUGBUILD */ - -#define CF_DATA_SAVE(save, cf, data) \ - do { \ - (save) = CF_CTX_CALL_DATA(cf); \ - CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)data; \ - } while(0) - -#define CF_DATA_RESTORE(cf, save) \ - do { \ - CF_CTX_CALL_DATA(cf) = (save); \ - } while(0) - -#endif /* !DEBUGBUILD */ - -#define CF_DATA_CURRENT(cf) \ - ((cf)? (CF_CTX_CALL_DATA(cf).data) : NULL) - -#endif /* HEADER_CURL_CFILTERS_H */ diff --git a/contrib/libs/curl/lib/conncache.c b/contrib/libs/curl/lib/conncache.c index a21409c13f..a557ac6dc9 100644 --- a/contrib/libs/curl/lib/conncache.c +++ b/contrib/libs/curl/lib/conncache.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se> - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2016, Linus Nielsen Feltzing, <linus@haxx.se> + * Copyright (C) 2012 - 2022, 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 @@ -45,6 +45,13 @@ #define HASHKEY_SIZE 128 +static void conn_llist_dtor(void *user, void *element) +{ + struct connectdata *conn = element; + (void)user; + conn->bundle = NULL; +} + static CURLcode bundle_create(struct connectbundle **bundlep) { DEBUGASSERT(*bundlep == NULL); @@ -55,12 +62,17 @@ static CURLcode bundle_create(struct connectbundle **bundlep) (*bundlep)->num_connections = 0; (*bundlep)->multiuse = BUNDLE_UNKNOWN; - Curl_llist_init(&(*bundlep)->conn_list, NULL); + Curl_llist_init(&(*bundlep)->conn_list, (Curl_llist_dtor) conn_llist_dtor); return CURLE_OK; } static void bundle_destroy(struct connectbundle *bundle) { + if(!bundle) + return; + + Curl_llist_destroy(&bundle->conn_list, NULL); + free(bundle); } @@ -246,7 +258,7 @@ CURLcode Curl_conncache_add_conn(struct Curl_easy *data) "The cache now contains %zu members", conn->connection_id, connc->num_conn)); -unlock: + unlock: CONNCACHE_UNLOCK(data); return result; diff --git a/contrib/libs/curl/lib/conncache.h b/contrib/libs/curl/lib/conncache.h index c60f8449ee..94664bc357 100644 --- a/contrib/libs/curl/lib/conncache.h +++ b/contrib/libs/curl/lib/conncache.h @@ -7,8 +7,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se> + * Copyright (C) 2015 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2014, Linus Nielsen Feltzing, <linus@haxx.se> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -39,8 +39,7 @@ struct connectdata; struct conncache { struct Curl_hash hash; size_t num_conn; - curl_off_t next_connection_id; - curl_off_t next_easy_id; + long next_connection_id; struct curltime last_cleanup; /* handle used for closing cached connections */ struct Curl_easy *closure_handle; diff --git a/contrib/libs/curl/lib/connect.c b/contrib/libs/curl/lib/connect.c index dc93533f6c..ac007c61b0 100644 --- a/contrib/libs/curl/lib/connect.c +++ b/contrib/libs/curl/lib/connect.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -48,6 +48,13 @@ #include <arpa/inet.h> #endif +#if (defined(HAVE_IOCTL_FIONBIO) && defined(NETWARE)) +#include <sys/filio.h> +#endif +#ifdef NETWARE +#undef in_addr_t +#define in_addr_t unsigned long +#endif #ifdef __VMS #include <in.h> #include <inet.h> @@ -57,26 +64,21 @@ #include "sendf.h" #include "if2ip.h" #include "strerror.h" -#include "cfilters.h" #include "connect.h" -#include "cf-haproxy.h" -#include "cf-https-connect.h" -#include "cf-socket.h" #include "select.h" #include "url.h" /* for Curl_safefree() */ #include "multiif.h" #include "sockaddr.h" /* required for Curl_sockaddr_storage */ #include "inet_ntop.h" #include "inet_pton.h" -#include "vtls/vtls.h" /* for vtsl cfilters */ +#include "vtls/vtls.h" /* for Curl_ssl_check_cxn() */ #include "progress.h" #include "warnless.h" #include "conncache.h" #include "multihandle.h" #include "share.h" #include "version_win32.h" -#include "vquic/vquic.h" /* for quic cfilters */ -#include "http_proxy.h" +#include "quic.h" #include "socks.h" /* The last 3 #include files should be in this order */ @@ -84,6 +86,86 @@ #include "curl_memory.h" #include "memdebug.h" +static bool verifyconnect(curl_socket_t sockfd, int *error); + +#if defined(__DragonFly__) || defined(HAVE_WINSOCK2_H) +/* DragonFlyBSD and Windows use millisecond units */ +#define KEEPALIVE_FACTOR(x) (x *= 1000) +#else +#define KEEPALIVE_FACTOR(x) +#endif + +#if defined(HAVE_WINSOCK2_H) && !defined(SIO_KEEPALIVE_VALS) +#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4) + +struct tcp_keepalive { + u_long onoff; + u_long keepalivetime; + u_long keepaliveinterval; +}; +#endif + +static void +tcpkeepalive(struct Curl_easy *data, + curl_socket_t sockfd) +{ + int optval = data->set.tcp_keepalive?1:0; + + /* only set IDLE and INTVL if setting KEEPALIVE is successful */ + if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, + (void *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set SO_KEEPALIVE on fd %d", sockfd); + } + else { +#if defined(SIO_KEEPALIVE_VALS) + struct tcp_keepalive vals; + DWORD dummy; + vals.onoff = 1; + optval = curlx_sltosi(data->set.tcp_keepidle); + KEEPALIVE_FACTOR(optval); + vals.keepalivetime = optval; + optval = curlx_sltosi(data->set.tcp_keepintvl); + KEEPALIVE_FACTOR(optval); + vals.keepaliveinterval = optval; + if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals), + NULL, 0, &dummy, NULL, NULL) != 0) { + infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd %d: %d", + (int)sockfd, WSAGetLastError()); + } +#else +#ifdef TCP_KEEPIDLE + optval = curlx_sltosi(data->set.tcp_keepidle); + KEEPALIVE_FACTOR(optval); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, + (void *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set TCP_KEEPIDLE on fd %d", sockfd); + } +#elif defined(TCP_KEEPALIVE) + /* Mac OS X style */ + optval = curlx_sltosi(data->set.tcp_keepidle); + KEEPALIVE_FACTOR(optval); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE, + (void *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set TCP_KEEPALIVE on fd %d", sockfd); + } +#endif +#ifdef TCP_KEEPINTVL + optval = curlx_sltosi(data->set.tcp_keepintvl); + KEEPALIVE_FACTOR(optval); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, + (void *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set TCP_KEEPINTVL on fd %d", sockfd); + } +#endif +#endif + } +} + +static CURLcode +singleipconnect(struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai, /* start connecting to this */ + int tempindex); /* 0 or 1 among the temp ones */ /* * Curl_timeleft() returns the amount of milliseconds left allowed for the @@ -155,6 +237,383 @@ timediff_t Curl_timeleft(struct Curl_easy *data, return timeout_ms; } +static CURLcode bindlocal(struct Curl_easy *data, + curl_socket_t sockfd, int af, unsigned int scope) +{ + struct connectdata *conn = data->conn; + struct Curl_sockaddr_storage sa; + struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */ + curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */ + struct sockaddr_in *si4 = (struct sockaddr_in *)&sa; +#ifdef ENABLE_IPV6 + struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa; +#endif + + struct Curl_dns_entry *h = NULL; + unsigned short port = data->set.localport; /* use this port number, 0 for + "random" */ + /* how many port numbers to try to bind to, increasing one at a time */ + int portnum = data->set.localportrange; + const char *dev = data->set.str[STRING_DEVICE]; + int error; +#ifdef IP_BIND_ADDRESS_NO_PORT + int on = 1; +#endif +#ifndef ENABLE_IPV6 + (void)scope; +#endif + + /************************************************************* + * Select device to bind socket to + *************************************************************/ + if(!dev && !port) + /* no local kind of binding was requested */ + return CURLE_OK; + + memset(&sa, 0, sizeof(struct Curl_sockaddr_storage)); + + if(dev && (strlen(dev)<255) ) { + char myhost[256] = ""; + int done = 0; /* -1 for error, 1 for address found */ + bool is_interface = FALSE; + bool is_host = FALSE; + static const char *if_prefix = "if!"; + static const char *host_prefix = "host!"; + + if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) { + dev += strlen(if_prefix); + is_interface = TRUE; + } + else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) { + dev += strlen(host_prefix); + is_host = TRUE; + } + + /* interface */ + if(!is_host) { +#ifdef SO_BINDTODEVICE + /* I am not sure any other OSs than Linux that provide this feature, + * and at the least I cannot test. --Ben + * + * This feature allows one to tightly bind the local socket to a + * particular interface. This will force even requests to other + * local interfaces to go out the external interface. + * + * + * Only bind to the interface when specified as interface, not just + * as a hostname or ip address. + * + * interface might be a VRF, eg: vrf-blue, which means it cannot be + * converted to an IP address and would fail Curl_if2ip. Simply try + * to use it straight away. + */ + if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, + dev, (curl_socklen_t)strlen(dev) + 1) == 0) { + /* This is typically "errno 1, error: Operation not permitted" if + * you're not running as root or another suitable privileged + * user. + * If it succeeds it means the parameter was a valid interface and + * not an IP address. Return immediately. + */ + return CURLE_OK; + } +#endif + + switch(Curl_if2ip(af, +#ifdef ENABLE_IPV6 + scope, conn->scope_id, +#endif + dev, myhost, sizeof(myhost))) { + case IF2IP_NOT_FOUND: + if(is_interface) { + /* Do not fall back to treating it as a host name */ + failf(data, "Couldn't bind to interface '%s'", dev); + return CURLE_INTERFACE_FAILED; + } + break; + case IF2IP_AF_NOT_SUPPORTED: + /* Signal the caller to try another address family if available */ + return CURLE_UNSUPPORTED_PROTOCOL; + case IF2IP_FOUND: + is_interface = TRUE; + /* + * We now have the numerical IP address in the 'myhost' buffer + */ + infof(data, "Local Interface %s is ip %s using address family %i", + dev, myhost, af); + done = 1; + break; + } + } + if(!is_interface) { + /* + * This was not an interface, resolve the name as a host name + * or IP number + * + * Temporarily force name resolution to use only the address type + * of the connection. The resolve functions should really be changed + * to take a type parameter instead. + */ + unsigned char ipver = conn->ip_version; + int rc; + + if(af == AF_INET) + conn->ip_version = CURL_IPRESOLVE_V4; +#ifdef ENABLE_IPV6 + else if(af == AF_INET6) + conn->ip_version = CURL_IPRESOLVE_V6; +#endif + + rc = Curl_resolv(data, dev, 0, FALSE, &h); + if(rc == CURLRESOLV_PENDING) + (void)Curl_resolver_wait_resolv(data, &h); + conn->ip_version = ipver; + + if(h) { + /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */ + Curl_printable_address(h->addr, myhost, sizeof(myhost)); + infof(data, "Name '%s' family %i resolved to '%s' family %i", + dev, af, myhost, h->addr->ai_family); + Curl_resolv_unlock(data, h); + if(af != h->addr->ai_family) { + /* bad IP version combo, signal the caller to try another address + family if available */ + return CURLE_UNSUPPORTED_PROTOCOL; + } + done = 1; + } + else { + /* + * provided dev was no interface (or interfaces are not supported + * e.g. solaris) no ip address and no domain we fail here + */ + done = -1; + } + } + + if(done > 0) { +#ifdef ENABLE_IPV6 + /* IPv6 address */ + if(af == AF_INET6) { +#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID + char *scope_ptr = strchr(myhost, '%'); + if(scope_ptr) + *(scope_ptr++) = 0; +#endif + if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) { + si6->sin6_family = AF_INET6; + si6->sin6_port = htons(port); +#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID + if(scope_ptr) + /* The "myhost" string either comes from Curl_if2ip or from + Curl_printable_address. The latter returns only numeric scope + IDs and the former returns none at all. So the scope ID, if + present, is known to be numeric */ + si6->sin6_scope_id = atoi(scope_ptr); +#endif + } + sizeof_sa = sizeof(struct sockaddr_in6); + } + else +#endif + /* IPv4 address */ + if((af == AF_INET) && + (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) { + si4->sin_family = AF_INET; + si4->sin_port = htons(port); + sizeof_sa = sizeof(struct sockaddr_in); + } + } + + if(done < 1) { + /* errorbuf is set false so failf will overwrite any message already in + the error buffer, so the user receives this error message instead of a + generic resolve error. */ + data->state.errorbuf = FALSE; + failf(data, "Couldn't bind to '%s'", dev); + return CURLE_INTERFACE_FAILED; + } + } + else { + /* no device was given, prepare sa to match af's needs */ +#ifdef ENABLE_IPV6 + if(af == AF_INET6) { + si6->sin6_family = AF_INET6; + si6->sin6_port = htons(port); + sizeof_sa = sizeof(struct sockaddr_in6); + } + else +#endif + if(af == AF_INET) { + si4->sin_family = AF_INET; + si4->sin_port = htons(port); + sizeof_sa = sizeof(struct sockaddr_in); + } + } +#ifdef IP_BIND_ADDRESS_NO_PORT + (void)setsockopt(sockfd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &on, sizeof(on)); +#endif + for(;;) { + if(bind(sockfd, sock, sizeof_sa) >= 0) { + /* we succeeded to bind */ + struct Curl_sockaddr_storage add; + curl_socklen_t size = sizeof(add); + memset(&add, 0, sizeof(struct Curl_sockaddr_storage)); + if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) { + char buffer[STRERROR_LEN]; + data->state.os_errno = error = SOCKERRNO; + failf(data, "getsockname() failed with errno %d: %s", + error, Curl_strerror(error, buffer, sizeof(buffer))); + return CURLE_INTERFACE_FAILED; + } + infof(data, "Local port: %hu", port); + conn->bits.bound = TRUE; + return CURLE_OK; + } + + if(--portnum > 0) { + port++; /* try next port */ + if(port == 0) + break; + infof(data, "Bind to local port %hu failed, trying next", port - 1); + /* We re-use/clobber the port variable here below */ + if(sock->sa_family == AF_INET) + si4->sin_port = ntohs(port); +#ifdef ENABLE_IPV6 + else + si6->sin6_port = ntohs(port); +#endif + } + else + break; + } + { + char buffer[STRERROR_LEN]; + data->state.os_errno = error = SOCKERRNO; + failf(data, "bind failed with errno %d: %s", + error, Curl_strerror(error, buffer, sizeof(buffer))); + } + + return CURLE_INTERFACE_FAILED; +} + +/* + * verifyconnect() returns TRUE if the connect really has happened. + */ +static bool verifyconnect(curl_socket_t sockfd, int *error) +{ + bool rc = TRUE; +#ifdef SO_ERROR + int err = 0; + curl_socklen_t errSize = sizeof(err); + +#ifdef WIN32 + /* + * In October 2003 we effectively nullified this function on Windows due to + * problems with it using all CPU in multi-threaded cases. + * + * In May 2004, we bring it back to offer more info back on connect failures. + * Gisle Vanem could reproduce the former problems with this function, but + * could avoid them by adding this SleepEx() call below: + * + * "I don't have Rational Quantify, but the hint from his post was + * ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe + * just Sleep(0) would be enough?) would release whatever + * mutex/critical-section the ntdll call is waiting on. + * + * Someone got to verify this on Win-NT 4.0, 2000." + */ + +#ifdef _WIN32_WCE + Sleep(0); +#else + SleepEx(0, FALSE); +#endif + +#endif + + if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize)) + err = SOCKERRNO; +#ifdef _WIN32_WCE + /* Old WinCE versions don't support SO_ERROR */ + if(WSAENOPROTOOPT == err) { + SET_SOCKERRNO(0); + err = 0; + } +#endif +#if defined(EBADIOCTL) && defined(__minix) + /* Minix 3.1.x doesn't support getsockopt on UDP sockets */ + if(EBADIOCTL == err) { + SET_SOCKERRNO(0); + err = 0; + } +#endif + if((0 == err) || (EISCONN == err)) + /* we are connected, awesome! */ + rc = TRUE; + else + /* This wasn't a successful connect */ + rc = FALSE; + if(error) + *error = err; +#else + (void)sockfd; + if(error) + *error = SOCKERRNO; +#endif + return rc; +} + +/* update tempaddr[tempindex] (to the next entry), makes sure to stick + to the correct family */ +static struct Curl_addrinfo *ainext(struct connectdata *conn, + int tempindex, + bool next) /* use next entry? */ +{ + struct Curl_addrinfo *ai = conn->tempaddr[tempindex]; + if(ai && next) + ai = ai->ai_next; + while(ai && (ai->ai_family != conn->tempfamily[tempindex])) + ai = ai->ai_next; + conn->tempaddr[tempindex] = ai; + return ai; +} + +/* Used within the multi interface. Try next IP address, returns error if no + more address exists or error */ +static CURLcode trynextip(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + int tempindex) +{ + CURLcode result = CURLE_COULDNT_CONNECT; + + /* First clean up after the failed socket. + Don't close it yet to ensure that the next IP's socket gets a different + file descriptor, which can prevent bugs when the curl_multi_socket_action + interface is used with certain select() replacements such as kqueue. */ + curl_socket_t fd_to_close = conn->tempsock[tempindex]; + conn->tempsock[tempindex] = CURL_SOCKET_BAD; + + if(sockindex == FIRSTSOCKET) { + struct Curl_addrinfo *ai = conn->tempaddr[tempindex]; + + while(ai) { + result = singleipconnect(data, conn, ai, tempindex); + if(result == CURLE_COULDNT_CONNECT) { + ai = ainext(conn, tempindex, TRUE); + continue; + } + break; + } + } + + if(fd_to_close != CURL_SOCKET_BAD) + Curl_closesocket(data, conn, fd_to_close); + + return result; +} + /* Copies connection info into the transfer handle to make it available when the transfer handle is no longer associated with the connection. */ void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn, @@ -173,28 +632,6 @@ void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn, data->info.conn_local_port = local_port; } -static const struct Curl_addrinfo * -addr_first_match(const struct Curl_addrinfo *addr, int family) -{ - while(addr) { - if(addr->ai_family == family) - return addr; - addr = addr->ai_next; - } - return NULL; -} - -static const struct Curl_addrinfo * -addr_next_match(const struct Curl_addrinfo *addr, int family) -{ - while(addr && addr->ai_next) { - addr = addr->ai_next; - if(addr->ai_family == family) - return addr; - } - return NULL; -} - /* retrieves ip address and port from a sockaddr structure. note it calls Curl_inet_ntop which sets errno on fail, not SOCKERRNO. */ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, @@ -252,494 +689,708 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, return FALSE; } -struct connfind { - curl_off_t id_tofind; - struct connectdata *found; -}; - -static int conn_is_conn(struct Curl_easy *data, - struct connectdata *conn, void *param) +/* retrieves the start/end point information of a socket of an established + connection */ +void Curl_conninfo_remote(struct Curl_easy *data, + struct connectdata *conn, curl_socket_t sockfd) { - struct connfind *f = (struct connfind *)param; +#ifdef HAVE_GETPEERNAME + char buffer[STRERROR_LEN]; + struct Curl_sockaddr_storage ssrem; + curl_socklen_t plen; + int port; + plen = sizeof(struct Curl_sockaddr_storage); + memset(&ssrem, 0, sizeof(ssrem)); + if(getpeername(sockfd, (struct sockaddr*) &ssrem, &plen)) { + int error = SOCKERRNO; + failf(data, "getpeername() failed with errno %d: %s", + error, Curl_strerror(error, buffer, sizeof(buffer))); + return; + } + if(!Curl_addr2string((struct sockaddr*)&ssrem, plen, + conn->primary_ip, &port)) { + failf(data, "ssrem inet_ntop() failed with errno %d: %s", + errno, Curl_strerror(errno, buffer, sizeof(buffer))); + return; + } +#else (void)data; - if(conn->connection_id == f->id_tofind) { - f->found = conn; - return 1; + (void)conn; + (void)sockfd; +#endif +} + +/* retrieves the start/end point information of a socket of an established + connection */ +void Curl_conninfo_local(struct Curl_easy *data, curl_socket_t sockfd, + char *local_ip, int *local_port) +{ +#ifdef HAVE_GETSOCKNAME + char buffer[STRERROR_LEN]; + struct Curl_sockaddr_storage ssloc; + curl_socklen_t slen; + slen = sizeof(struct Curl_sockaddr_storage); + memset(&ssloc, 0, sizeof(ssloc)); + if(getsockname(sockfd, (struct sockaddr*) &ssloc, &slen)) { + int error = SOCKERRNO; + failf(data, "getsockname() failed with errno %d: %s", + error, Curl_strerror(error, buffer, sizeof(buffer))); + return; } - return 0; + if(!Curl_addr2string((struct sockaddr*)&ssloc, slen, + local_ip, local_port)) { + failf(data, "ssloc inet_ntop() failed with errno %d: %s", + errno, Curl_strerror(errno, buffer, sizeof(buffer))); + return; + } +#else + (void)data; + (void)sockfd; + (void)local_ip; + (void)local_port; +#endif } -/* - * Used to extract socket and connectdata struct for the most recent - * transfer on the given Curl_easy. - * - * The returned socket will be CURL_SOCKET_BAD in case of failure! - */ -curl_socket_t Curl_getconnectinfo(struct Curl_easy *data, - struct connectdata **connp) +/* retrieves the start/end point information of a socket of an established + connection */ +void Curl_updateconninfo(struct Curl_easy *data, struct connectdata *conn, + curl_socket_t sockfd) { - DEBUGASSERT(data); + /* 'local_ip' and 'local_port' get filled with local's numerical + ip address and port number whenever an outgoing connection is + **established** from the primary socket to a remote address. */ + char local_ip[MAX_IPADR_LEN] = ""; + int local_port = -1; + + if(!conn->bits.reuse && + (conn->transport != TRNSPRT_TCP || !conn->bits.tcp_fastopen)) + Curl_conninfo_remote(data, conn, sockfd); + Curl_conninfo_local(data, sockfd, local_ip, &local_port); + + /* persist connection info in session handle */ + Curl_persistconninfo(data, conn, local_ip, local_port); +} - /* this works for an easy handle: - * - that has been used for curl_easy_perform() - * - that is associated with a multi handle, and whose connection - * was detached with CURLOPT_CONNECT_ONLY - */ - if((data->state.lastconnect_id != -1) && (data->multi_easy || data->multi)) { - struct connectdata *c; - struct connfind find; - find.id_tofind = data->state.lastconnect_id; - find.found = NULL; +/* After a TCP connection to the proxy has been verified, this function does + the next magic steps. If 'done' isn't set TRUE, it is not done yet and + must be called again. - Curl_conncache_foreach(data, - data->share && (data->share->specifier - & (1<< CURL_LOCK_DATA_CONNECT))? - &data->share->conn_cache: - data->multi_easy? - &data->multi_easy->conn_cache: - &data->multi->conn_cache, &find, conn_is_conn); + Note: this function's sub-functions call failf() - if(!find.found) { - data->state.lastconnect_id = -1; - return CURL_SOCKET_BAD; - } +*/ +static CURLcode connect_SOCKS(struct Curl_easy *data, int sockindex, + bool *done) +{ + CURLcode result = CURLE_OK; +#ifndef CURL_DISABLE_PROXY + CURLproxycode pxresult = CURLPX_OK; + struct connectdata *conn = data->conn; + if(conn->bits.socksproxy) { + /* for the secondary socket (FTP), use the "connect to host" + * but ignore the "connect to port" (use the secondary port) + */ + const char * const host = + conn->bits.httpproxy ? + conn->http_proxy.host.name : + conn->bits.conn_to_host ? + conn->conn_to_host.name : + sockindex == SECONDARYSOCKET ? + conn->secondaryhostname : conn->host.name; + const int port = + conn->bits.httpproxy ? (int)conn->http_proxy.port : + sockindex == SECONDARYSOCKET ? conn->secondary_port : + conn->bits.conn_to_port ? conn->conn_to_port : + conn->remote_port; + switch(conn->socks_proxy.proxytype) { + case CURLPROXY_SOCKS5: + case CURLPROXY_SOCKS5_HOSTNAME: + pxresult = Curl_SOCKS5(conn->socks_proxy.user, conn->socks_proxy.passwd, + host, port, sockindex, data, done); + break; - c = find.found; - if(connp) - /* only store this if the caller cares for it */ - *connp = c; - return c->sock[FIRSTSOCKET]; + case CURLPROXY_SOCKS4: + case CURLPROXY_SOCKS4A: + pxresult = Curl_SOCKS4(conn->socks_proxy.user, host, port, sockindex, + data, done); + break; + + default: + failf(data, "unknown proxytype option given"); + result = CURLE_COULDNT_CONNECT; + } /* switch proxytype */ + if(pxresult) { + result = CURLE_PROXY; + data->info.pxcode = pxresult; + } } - return CURL_SOCKET_BAD; + else +#else + (void)data; + (void)sockindex; +#endif /* CURL_DISABLE_PROXY */ + *done = TRUE; /* no SOCKS proxy, so consider us connected */ + + return result; } /* - * Curl_conncontrol() marks streams or connection for closure. + * post_SOCKS() is called after a successful connect to the peer, which + * *could* be a SOCKS proxy */ -void Curl_conncontrol(struct connectdata *conn, - int ctrl /* see defines in header */ -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - , const char *reason -#endif - ) +static void post_SOCKS(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + bool *connected) { - /* close if a connection, or a stream that isn't multiplexed. */ - /* This function will be called both before and after this connection is - associated with a transfer. */ - bool closeit, is_multiplex; - DEBUGASSERT(conn); -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - (void)reason; /* useful for debugging */ -#endif - is_multiplex = Curl_conn_is_multiplex(conn, FIRSTSOCKET); - closeit = (ctrl == CONNCTRL_CONNECTION) || - ((ctrl == CONNCTRL_STREAM) && !is_multiplex); - if((ctrl == CONNCTRL_STREAM) && is_multiplex) - ; /* stream signal on multiplex conn never affects close state */ - else if((bit)closeit != conn->bits.close) { - conn->bits.close = closeit; /* the only place in the source code that - should assign this bit */ - } + conn->bits.tcpconnect[sockindex] = TRUE; + + *connected = TRUE; + if(sockindex == FIRSTSOCKET) + Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */ + Curl_updateconninfo(data, conn, conn->sock[sockindex]); + Curl_verboseconnect(data, conn); + data->info.numconnects++; /* to track the number of connections made */ } -/** - * job walking the matching addr infos, creating a sub-cfilter with the - * provided method `cf_create` and running setup/connect on it. +/* + * Curl_is_connected() checks if the socket has connected. */ -struct eyeballer { - const char *name; - const struct Curl_addrinfo *addr; /* List of addresses to try, not owned */ - int ai_family; /* matching address family only */ - cf_ip_connect_create *cf_create; /* for creating cf */ - struct Curl_cfilter *cf; /* current sub-cfilter connecting */ - struct eyeballer *primary; /* eyeballer this one is backup for */ - timediff_t delay_ms; /* delay until start */ - struct curltime started; /* start of current attempt */ - timediff_t timeoutms; /* timeout for current attempt */ - expire_id timeout_id; /* ID for Curl_expire() */ - CURLcode result; - int error; - BIT(has_started); /* attempts have started */ - BIT(is_done); /* out of addresses/time */ - BIT(connected); /* cf has connected */ -}; +CURLcode Curl_is_connected(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + bool *connected) +{ + CURLcode result = CURLE_OK; + timediff_t allow; + int error = 0; + struct curltime now; + int rc = 0; + unsigned int i; -typedef enum { - SCFST_INIT, - SCFST_WAITING, - SCFST_DONE -} cf_connect_state; - -struct cf_he_ctx { - int transport; - cf_ip_connect_create *cf_create; - const struct Curl_dns_entry *remotehost; - cf_connect_state state; - struct eyeballer *baller[2]; - struct eyeballer *winner; - struct curltime started; -}; + DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET); -static CURLcode eyeballer_new(struct eyeballer **pballer, - cf_ip_connect_create *cf_create, - const struct Curl_addrinfo *addr, - int ai_family, - struct eyeballer *primary, - timediff_t delay_ms, - timediff_t timeout_ms, - expire_id timeout_id) -{ - struct eyeballer *baller; + *connected = FALSE; /* a very negative world view is best */ + + if(conn->bits.tcpconnect[sockindex]) { + /* we are connected already! */ + *connected = TRUE; + return CURLE_OK; + } + + now = Curl_now(); - *pballer = NULL; - baller = calloc(1, sizeof(*baller) + 1000); - if(!baller) - return CURLE_OUT_OF_MEMORY; + if(SOCKS_STATE(conn->cnnct.state)) { + /* still doing SOCKS */ + result = connect_SOCKS(data, sockindex, connected); + if(!result && *connected) + post_SOCKS(data, conn, sockindex, connected); + return result; + } - baller->name = ((ai_family == AF_INET)? "ipv4" : ( + for(i = 0; i<2; i++) { + const int other = i ^ 1; + if(conn->tempsock[i] == CURL_SOCKET_BAD) + continue; + error = 0; +#ifdef ENABLE_QUIC + if(conn->transport == TRNSPRT_QUIC) { + result = Curl_quic_is_connected(data, conn, i, connected); + if(!result && *connected) { + /* use this socket from now on */ + conn->sock[sockindex] = conn->tempsock[i]; + conn->ip_addr = conn->tempaddr[i]; + conn->tempsock[i] = CURL_SOCKET_BAD; + post_SOCKS(data, conn, sockindex, connected); + connkeep(conn, "HTTP/3 default"); + if(conn->tempsock[other] != CURL_SOCKET_BAD) + Curl_quic_disconnect(data, conn, other); + return CURLE_OK; + } + /* When a QUIC connect attempt fails, the better error explanation is in + 'result' and not in errno */ + if(result) { + conn->tempsock[i] = CURL_SOCKET_BAD; + error = SOCKERRNO; + } + } + else +#endif + { +#ifdef mpeix + /* Call this function once now, and ignore the results. We do this to + "clear" the error state on the socket so that we can later read it + reliably. This is reported necessary on the MPE/iX operating + system. */ + (void)verifyconnect(conn->tempsock[i], NULL); +#endif + + /* check socket for connect */ + rc = SOCKET_WRITABLE(conn->tempsock[i], 0); + } + + if(rc == 0) { /* no connection yet */ + if(Curl_timediff(now, conn->connecttime) >= + conn->timeoutms_per_addr[i]) { + infof(data, "After %" CURL_FORMAT_TIMEDIFF_T + "ms connect time, move on!", conn->timeoutms_per_addr[i]); + error = ETIMEDOUT; + } + + /* should we try another protocol family? */ + if(i == 0 && !conn->bits.parallel_connect && + (Curl_timediff(now, conn->connecttime) >= + data->set.happy_eyeballs_timeout)) { + conn->bits.parallel_connect = TRUE; /* starting now */ + trynextip(data, conn, sockindex, 1); + } + } + else if(rc == CURL_CSELECT_OUT || conn->bits.tcp_fastopen) { + if(verifyconnect(conn->tempsock[i], &error)) { + /* we are connected with TCP, awesome! */ + + /* use this socket from now on */ + conn->sock[sockindex] = conn->tempsock[i]; + conn->ip_addr = conn->tempaddr[i]; + conn->tempsock[i] = CURL_SOCKET_BAD; #ifdef ENABLE_IPV6 - (ai_family == AF_INET6)? "ipv6" : -#endif - "ip")); - baller->cf_create = cf_create; - baller->addr = addr; - baller->ai_family = ai_family; - baller->primary = primary; - baller->delay_ms = delay_ms; - baller->timeoutms = addr_next_match(baller->addr, baller->ai_family)? - timeout_ms / 2 : timeout_ms; - baller->timeout_id = timeout_id; - baller->result = CURLE_COULDNT_CONNECT; - - *pballer = baller; - return CURLE_OK; + conn->bits.ipv6 = (conn->ip_addr->ai_family == AF_INET6)?TRUE:FALSE; +#endif + + /* close the other socket, if open */ + if(conn->tempsock[other] != CURL_SOCKET_BAD) { + Curl_closesocket(data, conn, conn->tempsock[other]); + conn->tempsock[other] = CURL_SOCKET_BAD; + } + + /* see if we need to kick off any SOCKS proxy magic once we + connected */ + result = connect_SOCKS(data, sockindex, connected); + if(result || !*connected) + return result; + + post_SOCKS(data, conn, sockindex, connected); + + return CURLE_OK; + } + } + else if(rc & CURL_CSELECT_ERR) { + (void)verifyconnect(conn->tempsock[i], &error); + } + + /* + * The connection failed here, we should attempt to connect to the "next + * address" for the given host. But first remember the latest error. + */ + if(error) { + data->state.os_errno = error; + SET_SOCKERRNO(error); + if(conn->tempaddr[i]) { + CURLcode status; +#ifndef CURL_DISABLE_VERBOSE_STRINGS + char ipaddress[MAX_IPADR_LEN]; + char buffer[STRERROR_LEN]; + Curl_printable_address(conn->tempaddr[i], ipaddress, + sizeof(ipaddress)); +#ifdef ENABLE_QUIC + if(conn->transport == TRNSPRT_QUIC) { + infof(data, "connect to %s port %u failed: %s", + ipaddress, conn->port, curl_easy_strerror(result)); + } + else +#endif + infof(data, "connect to %s port %u failed: %s", + ipaddress, conn->port, + Curl_strerror(error, buffer, sizeof(buffer))); +#endif + + allow = Curl_timeleft(data, &now, TRUE); + conn->timeoutms_per_addr[i] = conn->tempaddr[i]->ai_next == NULL ? + allow : allow / 2; + ainext(conn, i, TRUE); + status = trynextip(data, conn, sockindex, i); + if((status != CURLE_COULDNT_CONNECT) || + conn->tempsock[other] == CURL_SOCKET_BAD) { + /* the last attempt failed and no other sockets remain open */ + if(!result) + result = status; + } + } + } + } + + /* + * Now that we've checked whether we are connected, check whether we've + * already timed out. + * + * First figure out how long time we have left to connect */ + + allow = Curl_timeleft(data, &now, TRUE); + + if(allow < 0) { + /* time-out, bail out, go home */ + failf(data, "Connection timeout after %ld ms", + Curl_timediff(now, data->progress.t_startsingle)); + return CURLE_OPERATION_TIMEDOUT; + } + + if(result && + (conn->tempsock[0] == CURL_SOCKET_BAD) && + (conn->tempsock[1] == CURL_SOCKET_BAD)) { + /* no more addresses to try */ + const char *hostname; + CURLcode failreason = result; + + /* if the first address family runs out of addresses to try before the + happy eyeball timeout, go ahead and try the next family now */ + result = trynextip(data, conn, sockindex, 1); + if(!result) + return result; + + result = failreason; + +#ifndef CURL_DISABLE_PROXY + if(conn->bits.socksproxy) + hostname = conn->socks_proxy.host.name; + else if(conn->bits.httpproxy) + hostname = conn->http_proxy.host.name; + else +#endif + if(conn->bits.conn_to_host) + hostname = conn->conn_to_host.name; + else + hostname = conn->host.name; + + failf(data, "Failed to connect to %s port %u after " + "%" CURL_FORMAT_TIMEDIFF_T " ms: %s", + hostname, conn->port, + Curl_timediff(now, data->progress.t_startsingle), + curl_easy_strerror(result)); + + Curl_quic_disconnect(data, conn, 0); + Curl_quic_disconnect(data, conn, 1); + +#ifdef WSAETIMEDOUT + if(WSAETIMEDOUT == data->state.os_errno) + result = CURLE_OPERATION_TIMEDOUT; +#elif defined(ETIMEDOUT) + if(ETIMEDOUT == data->state.os_errno) + result = CURLE_OPERATION_TIMEDOUT; +#endif + } + else + result = CURLE_OK; /* still trying */ + + return result; } -static void baller_close(struct eyeballer *baller, - struct Curl_easy *data) +static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd) { - if(baller && baller->cf) { - Curl_conn_cf_discard_chain(&baller->cf, data); - } +#if defined(TCP_NODELAY) + curl_socklen_t onoff = (curl_socklen_t) 1; + int level = IPPROTO_TCP; +#if !defined(CURL_DISABLE_VERBOSE_STRINGS) + char buffer[STRERROR_LEN]; +#else + (void) data; +#endif + + if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff, + sizeof(onoff)) < 0) + infof(data, "Could not set TCP_NODELAY: %s", + Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); +#else + (void)data; + (void)sockfd; +#endif } -static void baller_free(struct eyeballer *baller, - struct Curl_easy *data) +#ifdef SO_NOSIGPIPE +/* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when + sending data to a dead peer (instead of relying on the 4th argument to send + being MSG_NOSIGNAL). Possibly also existing and in use on other BSD + systems? */ +static void nosigpipe(struct Curl_easy *data, + curl_socket_t sockfd) { - if(baller) { - baller_close(baller, data); - free(baller); + int onoff = 1; + if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff, + sizeof(onoff)) < 0) { +#if !defined(CURL_DISABLE_VERBOSE_STRINGS) + char buffer[STRERROR_LEN]; + infof(data, "Could not set SO_NOSIGPIPE: %s", + Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); +#endif } } +#else +#define nosigpipe(x,y) Curl_nop_stmt +#endif -static void baller_next_addr(struct eyeballer *baller) +#ifdef USE_WINSOCK +/* When you run a program that uses the Windows Sockets API, you may + experience slow performance when you copy data to a TCP server. + + https://support.microsoft.com/kb/823764 + + Work-around: Make the Socket Send Buffer Size Larger Than the Program Send + Buffer Size + + The problem described in this knowledge-base is applied only to pre-Vista + Windows. Following function trying to detect OS version and skips + SO_SNDBUF adjustment for Windows Vista and above. +*/ +#define DETECT_OS_NONE 0 +#define DETECT_OS_PREVISTA 1 +#define DETECT_OS_VISTA_OR_LATER 2 + +void Curl_sndbufset(curl_socket_t sockfd) { - baller->addr = addr_next_match(baller->addr, baller->ai_family); + int val = CURL_MAX_WRITE_SIZE + 32; + int curval = 0; + int curlen = sizeof(curval); + + static int detectOsState = DETECT_OS_NONE; + + if(detectOsState == DETECT_OS_NONE) { + if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL)) + detectOsState = DETECT_OS_VISTA_OR_LATER; + else + detectOsState = DETECT_OS_PREVISTA; + } + + if(detectOsState == DETECT_OS_VISTA_OR_LATER) + return; + + if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0) + if(curval > val) + return; + + setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val)); } +#endif /* - * Initiate a connect attempt walk. + * singleipconnect() * * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to * CURL_SOCKET_BAD. Other errors will however return proper errors. + * + * singleipconnect() connects to the given IP only, and it may return without + * having connected. */ -static void baller_initiate(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct eyeballer *baller) +static CURLcode singleipconnect(struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai, + int tempindex) { - struct cf_he_ctx *ctx = cf->ctx; - struct Curl_cfilter *cf_prev = baller->cf; - struct Curl_cfilter *wcf; + struct Curl_sockaddr_ex addr; + int rc = -1; + int error = 0; + bool isconnected = FALSE; + curl_socket_t sockfd; CURLcode result; + char ipaddress[MAX_IPADR_LEN]; + int port; + bool is_tcp; +#ifdef TCP_FASTOPEN_CONNECT + int optval = 1; +#endif + const char *ipmsg; + char buffer[STRERROR_LEN]; + curl_socket_t *sockp = &conn->tempsock[tempindex]; + *sockp = CURL_SOCKET_BAD; - - /* Don't close a previous cfilter yet to ensure that the next IP's - socket gets a different file descriptor, which can prevent bugs when - the curl_multi_socket_action interface is used with certain select() - replacements such as kqueue. */ - result = baller->cf_create(&baller->cf, data, cf->conn, baller->addr, - ctx->transport); + result = Curl_socket(data, ai, &addr, &sockfd); if(result) - goto out; + return result; - /* the new filter might have sub-filters */ - for(wcf = baller->cf; wcf; wcf = wcf->next) { - wcf->conn = cf->conn; - wcf->sockindex = cf->sockindex; + /* store remote address and port used in this connection attempt */ + if(!Curl_addr2string((struct sockaddr*)&addr.sa_addr, addr.addrlen, + ipaddress, &port)) { + /* malformed address or bug in inet_ntop, try next address */ + failf(data, "sa_addr inet_ntop() failed with errno %d: %s", + errno, Curl_strerror(errno, buffer, sizeof(buffer))); + Curl_closesocket(data, conn, sockfd); + return CURLE_OK; } +#ifdef ENABLE_IPV6 + if(addr.family == AF_INET6) + ipmsg = " Trying [%s]:%d..."; + else +#endif + ipmsg = " Trying %s:%d..."; + infof(data, ipmsg, ipaddress, port); - if(addr_next_match(baller->addr, baller->ai_family)) { - Curl_expire(data, baller->timeoutms, baller->timeout_id); - } +#ifdef ENABLE_IPV6 + is_tcp = (addr.family == AF_INET || addr.family == AF_INET6) && + addr.socktype == SOCK_STREAM; +#else + is_tcp = (addr.family == AF_INET) && addr.socktype == SOCK_STREAM; +#endif + if(is_tcp && data->set.tcp_nodelay) + tcpnodelay(data, sockfd); -out: - if(result) { - DEBUGF(LOG_CF(data, cf, "%s failed", baller->name)); - baller_close(baller, data); - } - if(cf_prev) - Curl_conn_cf_discard_chain(&cf_prev, data); - baller->result = result; -} + nosigpipe(data, sockfd); -/** - * Start a connection attempt on the current baller address. - * Will return CURLE_OK on the first address where a socket - * could be created and the non-blocking connect started. - * Returns error when all remaining addresses have been tried. - */ -static CURLcode baller_start(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct eyeballer *baller, - timediff_t timeoutms) -{ - baller->error = 0; - baller->connected = FALSE; - baller->has_started = TRUE; - - while(baller->addr) { - baller->started = Curl_now(); - baller->timeoutms = addr_next_match(baller->addr, baller->ai_family) ? - timeoutms / 2 : timeoutms; - baller_initiate(cf, data, baller); - if(!baller->result) - break; - baller_next_addr(baller); - } - if(!baller->addr) { - baller->is_done = TRUE; - } - return baller->result; -} + Curl_sndbufset(sockfd); + if(is_tcp && data->set.tcp_keepalive) + tcpkeepalive(data, sockfd); -/* Used within the multi interface. Try next IP address, returns error if no - more address exists or error */ -static CURLcode baller_start_next(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct eyeballer *baller, - timediff_t timeoutms) -{ - if(cf->sockindex == FIRSTSOCKET) { - baller_next_addr(baller); - baller_start(cf, data, baller, timeoutms); - } - else { - baller->error = 0; - baller->connected = FALSE; - baller->has_started = TRUE; - baller->is_done = TRUE; - baller->result = CURLE_COULDNT_CONNECT; + if(data->set.fsockopt) { + /* activate callback for setting socket options */ + Curl_set_in_callback(data, true); + error = data->set.fsockopt(data->set.sockopt_client, + sockfd, + CURLSOCKTYPE_IPCXN); + Curl_set_in_callback(data, false); + + if(error == CURL_SOCKOPT_ALREADY_CONNECTED) + isconnected = TRUE; + else if(error) { + Curl_closesocket(data, conn, sockfd); /* close the socket and bail out */ + return CURLE_ABORTED_BY_CALLBACK; + } } - return baller->result; -} -static CURLcode baller_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct eyeballer *baller, - struct curltime *now, - bool *connected) -{ - (void)cf; - *connected = baller->connected; - if(!baller->result && !*connected) { - /* evaluate again */ - baller->result = Curl_conn_cf_connect(baller->cf, data, 0, connected); - - if(!baller->result) { - if(*connected) { - baller->connected = TRUE; - baller->is_done = TRUE; - } - else if(Curl_timediff(*now, baller->started) >= baller->timeoutms) { - infof(data, "%s connect timeout after %" CURL_FORMAT_TIMEDIFF_T - "ms, move on!", baller->name, baller->timeoutms); -#if defined(ETIMEDOUT) - baller->error = ETIMEDOUT; + /* possibly bind the local end to an IP, interface or port */ + if(addr.family == AF_INET +#ifdef ENABLE_IPV6 + || addr.family == AF_INET6 #endif - baller->result = CURLE_OPERATION_TIMEDOUT; + ) { + result = bindlocal(data, sockfd, addr.family, + Curl_ipv6_scope((struct sockaddr*)&addr.sa_addr)); + if(result) { + Curl_closesocket(data, conn, sockfd); /* close socket and bail out */ + if(result == CURLE_UNSUPPORTED_PROTOCOL) { + /* The address family is not supported on this interface. + We can continue trying addresses */ + return CURLE_COULDNT_CONNECT; } + return result; } } - return baller->result; -} -/* - * is_connected() checks if the socket has connected. - */ -static CURLcode is_connected(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *connected) -{ - struct cf_he_ctx *ctx = cf->ctx; - struct connectdata *conn = cf->conn; - CURLcode result; - struct curltime now; - size_t i; - int ongoing, not_started; - const char *hostname; - - /* Check if any of the conn->tempsock we use for establishing connections - * succeeded and, if so, close any ongoing other ones. - * Transfer the successful conn->tempsock to conn->sock[sockindex] - * and set conn->tempsock to CURL_SOCKET_BAD. - * If transport is QUIC, we need to shutdown the ongoing 'other' - * cot ballers in a QUIC appropriate way. */ -evaluate: - *connected = FALSE; /* a very negative world view is best */ - now = Curl_now(); - ongoing = not_started = 0; - for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { - struct eyeballer *baller = ctx->baller[i]; + /* set socket non-blocking */ + (void)curlx_nonblock(sockfd, TRUE); - if(!baller || baller->is_done) - continue; + conn->connecttime = Curl_now(); + if(conn->num_addr > 1) { + Curl_expire(data, conn->timeoutms_per_addr[0], EXPIRE_DNS_PER_NAME); + Curl_expire(data, conn->timeoutms_per_addr[1], EXPIRE_DNS_PER_NAME2); + } - if(!baller->has_started) { - ++not_started; - continue; - } - baller->result = baller_connect(cf, data, baller, &now, connected); - DEBUGF(LOG_CF(data, cf, "%s connect -> %d, connected=%d", - baller->name, baller->result, *connected)); - - if(!baller->result) { - if(*connected) { - /* connected, declare the winner */ - ctx->winner = baller; - ctx->baller[i] = NULL; - break; - } - else { /* still waiting */ - ++ongoing; - } - } - else if(!baller->is_done) { - /* The baller failed to connect, start its next attempt */ - if(baller->error) { - data->state.os_errno = baller->error; - SET_SOCKERRNO(baller->error); - } - baller_start_next(cf, data, baller, Curl_timeleft(data, &now, TRUE)); - if(baller->is_done) { - DEBUGF(LOG_CF(data, cf, "%s done", baller->name)); + /* Connect TCP and QUIC sockets */ + if(!isconnected && (conn->transport != TRNSPRT_UDP)) { + if(conn->bits.tcp_fastopen) { +#if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */ +# if defined(HAVE_BUILTIN_AVAILABLE) + /* while connectx function is available since macOS 10.11 / iOS 9, + it did not have the interface declared correctly until + Xcode 9 / macOS SDK 10.13 */ + if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) { + sa_endpoints_t endpoints; + endpoints.sae_srcif = 0; + endpoints.sae_srcaddr = NULL; + endpoints.sae_srcaddrlen = 0; + endpoints.sae_dstaddr = &addr.sa_addr; + endpoints.sae_dstaddrlen = addr.addrlen; + + rc = connectx(sockfd, &endpoints, SAE_ASSOCID_ANY, + CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT, + NULL, 0, NULL, NULL); } else { - /* next attempt was started */ - DEBUGF(LOG_CF(data, cf, "%s trying next", baller->name)); - ++ongoing; + rc = connect(sockfd, &addr.sa_addr, addr.addrlen); } +# else + rc = connect(sockfd, &addr.sa_addr, addr.addrlen); +# endif /* HAVE_BUILTIN_AVAILABLE */ +#elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */ + if(setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, + (void *)&optval, sizeof(optval)) < 0) + infof(data, "Failed to enable TCP Fast Open on fd %d", sockfd); + + rc = connect(sockfd, &addr.sa_addr, addr.addrlen); +#elif defined(MSG_FASTOPEN) /* old Linux */ + if(conn->given->flags & PROTOPT_SSL) + rc = connect(sockfd, &addr.sa_addr, addr.addrlen); + else + rc = 0; /* Do nothing */ +#endif + } + else { + rc = connect(sockfd, &addr.sa_addr, addr.addrlen); } - } - - if(ctx->winner) { - *connected = TRUE; - return CURLE_OK; - } - - /* Nothing connected, check the time before we might - * start new ballers or return ok. */ - if((ongoing || not_started) && Curl_timeleft(data, &now, TRUE) < 0) { - failf(data, "Connection timeout after %ld ms", - Curl_timediff(now, data->progress.t_startsingle)); - return CURLE_OPERATION_TIMEDOUT; - } - - /* Check if we have any waiting ballers to start now. */ - if(not_started > 0) { - int added = 0; - - for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { - struct eyeballer *baller = ctx->baller[i]; - if(!baller || baller->has_started) - continue; - /* We start its primary baller has failed to connect or if - * its start delay_ms have expired */ - if((baller->primary && baller->primary->is_done) || - Curl_timediff(now, ctx->started) >= baller->delay_ms) { - baller_start(cf, data, baller, Curl_timeleft(data, &now, TRUE)); - if(baller->is_done) { - DEBUGF(LOG_CF(data, cf, "%s done", baller->name)); - } - else { - DEBUGF(LOG_CF(data, cf, "%s starting (timeout=%" - CURL_FORMAT_TIMEDIFF_T "ms)", - baller->name, baller->timeoutms)); - ++ongoing; - ++added; - } - } + if(-1 == rc) + error = SOCKERRNO; +#ifdef ENABLE_QUIC + else if(conn->transport == TRNSPRT_QUIC) { + /* pass in 'sockfd' separately since it hasn't been put into the + tempsock array at this point */ + result = Curl_quic_connect(data, conn, sockfd, tempindex, + &addr.sa_addr, addr.addrlen); + if(result) + error = SOCKERRNO; } - if(added > 0) - goto evaluate; +#endif } - - if(ongoing > 0) { - /* We are still trying, return for more waiting */ - *connected = FALSE; + else { + *sockp = sockfd; return CURLE_OK; } - /* all ballers have failed to connect. */ - DEBUGF(LOG_CF(data, cf, "all eyeballers failed")); - result = CURLE_COULDNT_CONNECT; - for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { - struct eyeballer *baller = ctx->baller[i]; - DEBUGF(LOG_CF(data, cf, "%s assess started=%d, result=%d", - baller?baller->name:NULL, - baller?baller->has_started:0, - baller?baller->result:0)); - if(baller && baller->has_started && baller->result) { - result = baller->result; + if(-1 == rc) { + switch(error) { + case EINPROGRESS: + case EWOULDBLOCK: +#if defined(EAGAIN) +#if (EAGAIN) != (EWOULDBLOCK) + /* On some platforms EAGAIN and EWOULDBLOCK are the + * same value, and on others they are different, hence + * the odd #if + */ + case EAGAIN: +#endif +#endif + result = CURLE_OK; break; + + default: + /* unknown error, fallthrough and try another address! */ + infof(data, "Immediate connect fail for %s: %s", + ipaddress, Curl_strerror(error, buffer, sizeof(buffer))); + data->state.os_errno = error; + + /* connect failed */ + Curl_closesocket(data, conn, sockfd); + result = CURLE_COULDNT_CONNECT; } } -#ifndef CURL_DISABLE_PROXY - if(conn->bits.socksproxy) - hostname = conn->socks_proxy.host.name; - else if(conn->bits.httpproxy) - hostname = conn->http_proxy.host.name; - else -#endif - if(conn->bits.conn_to_host) - hostname = conn->conn_to_host.name; - else - hostname = conn->host.name; - - failf(data, "Failed to connect to %s port %u after " - "%" CURL_FORMAT_TIMEDIFF_T " ms: %s", - hostname, conn->port, - Curl_timediff(now, data->progress.t_startsingle), - curl_easy_strerror(result)); - -#ifdef WSAETIMEDOUT - if(WSAETIMEDOUT == data->state.os_errno) - result = CURLE_OPERATION_TIMEDOUT; -#elif defined(ETIMEDOUT) - if(ETIMEDOUT == data->state.os_errno) - result = CURLE_OPERATION_TIMEDOUT; -#endif + if(!result) + *sockp = sockfd; return result; } /* - * Connect to the given host with timeout, proxy or remote doesn't matter. - * There might be more than one IP address to try out. + * TCP connect to the given host with timeout, proxy or remote doesn't matter. + * There might be more than one IP address to try out. Fill in the passed + * pointer with the connected socket. */ -static CURLcode start_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - const struct Curl_dns_entry *remotehost) + +CURLcode Curl_connecthost(struct Curl_easy *data, + struct connectdata *conn, /* context */ + const struct Curl_dns_entry *remotehost) { - struct cf_he_ctx *ctx = cf->ctx; - struct connectdata *conn = cf->conn; CURLcode result = CURLE_COULDNT_CONNECT; - int ai_family0, ai_family1; + int i; timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); - const struct Curl_addrinfo *addr0, *addr1; if(timeout_ms < 0) { /* a precaution, no need to continue if time already is up */ @@ -747,78 +1398,58 @@ static CURLcode start_connect(struct Curl_cfilter *cf, return CURLE_OPERATION_TIMEDOUT; } - ctx->started = Curl_now(); + conn->num_addr = Curl_num_addresses(remotehost->addr); + conn->tempaddr[0] = conn->tempaddr[1] = remotehost->addr; + conn->tempsock[0] = conn->tempsock[1] = CURL_SOCKET_BAD; + + /* Max time for the next connection attempt */ + conn->timeoutms_per_addr[0] = + conn->tempaddr[0]->ai_next == NULL ? timeout_ms : timeout_ms / 2; + conn->timeoutms_per_addr[1] = + conn->tempaddr[1]->ai_next == NULL ? timeout_ms : timeout_ms / 2; - /* remotehost->addr is the list of addresses from the resolver, each - * with an address family. The list has at least one entry, possibly - * many more. - * We try at most 2 at a time, until we either get a connection or - * run out of addresses to try. Since likelihood of success is tied - * to the address family (e.g. IPV6 might not work at all ), we want - * the 2 connect attempt ballers to try different families, if possible. - * - */ if(conn->ip_version == CURL_IPRESOLVE_WHATEVER) { /* any IP version is allowed */ - ai_family0 = remotehost->addr? - remotehost->addr->ai_family : 0; + conn->tempfamily[0] = conn->tempaddr[0]? + conn->tempaddr[0]->ai_family:0; #ifdef ENABLE_IPV6 - ai_family1 = ai_family0 == AF_INET6 ? + conn->tempfamily[1] = conn->tempfamily[0] == AF_INET6 ? AF_INET : AF_INET6; #else - ai_family1 = AF_UNSPEC; + conn->tempfamily[1] = AF_UNSPEC; #endif } else { /* only one IP version is allowed */ - ai_family0 = (conn->ip_version == CURL_IPRESOLVE_V4) ? + conn->tempfamily[0] = (conn->ip_version == CURL_IPRESOLVE_V4) ? AF_INET : #ifdef ENABLE_IPV6 AF_INET6; #else AF_UNSPEC; #endif - ai_family1 = AF_UNSPEC; - } + conn->tempfamily[1] = AF_UNSPEC; - /* Get the first address in the list that matches the family, - * this might give NULL, if we do not have any matches. */ - addr0 = addr_first_match(remotehost->addr, ai_family0); - addr1 = addr_first_match(remotehost->addr, ai_family1); - if(!addr0 && addr1) { - /* switch around, so a single baller always uses addr0 */ - addr0 = addr1; - ai_family0 = ai_family1; - addr1 = NULL; + ainext(conn, 0, FALSE); /* find first address of the right type */ } - /* We found no address that matches our criteria, we cannot connect */ - if(!addr0) { - return CURLE_COULDNT_CONNECT; - } + ainext(conn, 1, FALSE); /* assigns conn->tempaddr[1] accordingly */ + + DEBUGF(infof(data, "family0 == %s, family1 == %s", + conn->tempfamily[0] == AF_INET ? "v4" : "v6", + conn->tempfamily[1] == AF_INET ? "v4" : "v6")); - memset(ctx->baller, 0, sizeof(ctx->baller)); - result = eyeballer_new(&ctx->baller[0], ctx->cf_create, addr0, ai_family0, - NULL, 0, /* no primary/delay, start now */ - timeout_ms, EXPIRE_DNS_PER_NAME); + /* get through the list in family order in case of quick failures */ + for(i = 0; (i < 2) && result; i++) { + while(conn->tempaddr[i]) { + result = singleipconnect(data, conn, conn->tempaddr[i], i); + if(!result) + break; + ainext(conn, i, TRUE); + } + } if(result) return result; - DEBUGF(LOG_CF(data, cf, "created %s (timeout %" - CURL_FORMAT_TIMEDIFF_T "ms)", - ctx->baller[0]->name, ctx->baller[0]->timeoutms)); - if(addr1) { - /* second one gets a delayed start */ - result = eyeballer_new(&ctx->baller[1], ctx->cf_create, addr1, ai_family1, - ctx->baller[0], /* wait on that to fail */ - /* or start this delayed */ - data->set.happy_eyeballs_timeout, - timeout_ms, EXPIRE_DNS_PER_NAME2); - if(result) - return result; - DEBUGF(LOG_CF(data, cf, "created %s (timeout %" - CURL_FORMAT_TIMEDIFF_T "ms)", - ctx->baller[1]->name, ctx->baller[1]->timeoutms)); - } Curl_expire(data, data->set.happy_eyeballs_timeout, EXPIRE_HAPPY_EYEBALLS); @@ -826,619 +1457,273 @@ static CURLcode start_connect(struct Curl_cfilter *cf, return CURLE_OK; } -static void cf_he_ctx_clear(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_he_ctx *ctx = cf->ctx; - size_t i; - - DEBUGASSERT(ctx); - DEBUGASSERT(data); - for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { - baller_free(ctx->baller[i], data); - ctx->baller[i] = NULL; - } - baller_free(ctx->winner, data); - ctx->winner = NULL; -} +struct connfind { + long id_tofind; + struct connectdata *found; +}; -static int cf_he_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) +static int conn_is_conn(struct Curl_easy *data, + struct connectdata *conn, void *param) { - struct cf_he_ctx *ctx = cf->ctx; - size_t i, s; - int wrc, rc = GETSOCK_BLANK; - curl_socket_t wsocks[MAX_SOCKSPEREASYHANDLE]; - - if(cf->connected) - return cf->next->cft->get_select_socks(cf->next, data, socks); - - for(i = s = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { - struct eyeballer *baller = ctx->baller[i]; - if(!baller || !baller->cf) - continue; - - wrc = Curl_conn_cf_get_select_socks(baller->cf, data, wsocks); - if(wrc) { - /* TODO: we assume we get at most one socket back */ - socks[s] = wsocks[0]; - if(wrc & GETSOCK_WRITESOCK(0)) - rc |= GETSOCK_WRITESOCK(s); - if(wrc & GETSOCK_READSOCK(0)) - rc |= GETSOCK_READSOCK(s); - s++; - } + struct connfind *f = (struct connfind *)param; + (void)data; + if(conn->connection_id == f->id_tofind) { + f->found = conn; + return 1; } - return rc; + return 0; } -static CURLcode cf_he_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking, bool *done) +/* + * Used to extract socket and connectdata struct for the most recent + * transfer on the given Curl_easy. + * + * The returned socket will be CURL_SOCKET_BAD in case of failure! + */ +curl_socket_t Curl_getconnectinfo(struct Curl_easy *data, + struct connectdata **connp) { - struct cf_he_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } - - (void)blocking; /* TODO: do we want to support this? */ - DEBUGASSERT(ctx); - *done = FALSE; + DEBUGASSERT(data); - switch(ctx->state) { - case SCFST_INIT: - DEBUGASSERT(CURL_SOCKET_BAD == Curl_conn_cf_get_socket(cf, data)); - DEBUGASSERT(!cf->connected); - result = start_connect(cf, data, ctx->remotehost); - if(result) - return result; - ctx->state = SCFST_WAITING; - /* FALLTHROUGH */ - case SCFST_WAITING: - result = is_connected(cf, data, done); - if(!result && *done) { - DEBUGASSERT(ctx->winner); - DEBUGASSERT(ctx->winner->cf); - DEBUGASSERT(ctx->winner->cf->connected); - /* we have a winner. Install and activate it. - * close/free all others. */ - ctx->state = SCFST_DONE; - cf->connected = TRUE; - cf->next = ctx->winner->cf; - ctx->winner->cf = NULL; - cf_he_ctx_clear(cf, data); - Curl_conn_cf_cntrl(cf->next, data, TRUE, - CF_CTRL_CONN_INFO_UPDATE, 0, NULL); - - if(cf->conn->handler->protocol & PROTO_FAMILY_SSH) - Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */ - Curl_verboseconnect(data, cf->conn); - data->info.numconnects++; /* to track the # of connections made */ - } - break; - case SCFST_DONE: - *done = TRUE; - break; - } - return result; -} + /* this works for an easy handle: + * - that has been used for curl_easy_perform() + * - that is associated with a multi handle, and whose connection + * was detached with CURLOPT_CONNECT_ONLY + */ + if((data->state.lastconnect_id != -1) && (data->multi_easy || data->multi)) { + struct connectdata *c; + struct connfind find; + find.id_tofind = data->state.lastconnect_id; + find.found = NULL; -static void cf_he_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_he_ctx *ctx = cf->ctx; + Curl_conncache_foreach(data, + data->share && (data->share->specifier + & (1<< CURL_LOCK_DATA_CONNECT))? + &data->share->conn_cache: + data->multi_easy? + &data->multi_easy->conn_cache: + &data->multi->conn_cache, &find, conn_is_conn); - DEBUGF(LOG_CF(data, cf, "close")); - cf_he_ctx_clear(cf, data); - cf->connected = FALSE; - ctx->state = SCFST_INIT; + if(!find.found) { + data->state.lastconnect_id = -1; + return CURL_SOCKET_BAD; + } - if(cf->next) { - cf->next->cft->do_close(cf->next, data); - Curl_conn_cf_discard_chain(&cf->next, data); + c = find.found; + if(connp) + /* only store this if the caller cares for it */ + *connp = c; + return c->sock[FIRSTSOCKET]; } + return CURL_SOCKET_BAD; } -static bool cf_he_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data) +/* + * Check if a connection seems to be alive. + */ +bool Curl_connalive(struct connectdata *conn) { - struct cf_he_ctx *ctx = cf->ctx; - size_t i; - - if(cf->connected) - return cf->next->cft->has_data_pending(cf->next, data); - - for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { - struct eyeballer *baller = ctx->baller[i]; - if(!baller || !baller->cf) - continue; - if(baller->cf->cft->has_data_pending(baller->cf, data)) - return TRUE; + /* First determine if ssl */ + if(conn->ssl[FIRSTSOCKET].use) { + /* use the SSL context */ + if(!Curl_ssl_check_cxn(conn)) + return false; /* FIN received */ } - return FALSE; -} - -static struct curltime get_max_baller_time(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query) -{ - struct cf_he_ctx *ctx = cf->ctx; - struct curltime t, tmax; - size_t i; - - memset(&tmax, 0, sizeof(tmax)); - for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { - struct eyeballer *baller = ctx->baller[i]; - - memset(&t, 0, sizeof(t)); - if(baller && baller->cf && - !baller->cf->cft->query(baller->cf, data, query, NULL, &t)) { - if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0) - tmax = t; +/* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */ +#ifdef MSG_PEEK + else if(conn->sock[FIRSTSOCKET] == CURL_SOCKET_BAD) + return false; + else { + /* use the socket */ + char buf; + if(recv((RECV_TYPE_ARG1)conn->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf, + (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) { + return false; /* FIN received */ } } - return tmax; +#endif + return true; } -static CURLcode cf_he_query(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query, int *pres1, void *pres2) +/* + * Close a socket. + * + * 'conn' can be NULL, beware! + */ +int Curl_closesocket(struct Curl_easy *data, struct connectdata *conn, + curl_socket_t sock) { - struct cf_he_ctx *ctx = cf->ctx; - - if(!cf->connected) { - switch(query) { - case CF_QUERY_CONNECT_REPLY_MS: { - int reply_ms = -1; - size_t i; - - for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { - struct eyeballer *baller = ctx->baller[i]; - int breply_ms; - - if(baller && baller->cf && - !baller->cf->cft->query(baller->cf, data, query, - &breply_ms, NULL)) { - if(breply_ms >= 0 && (reply_ms < 0 || breply_ms < reply_ms)) - reply_ms = breply_ms; - } - } - *pres1 = reply_ms; - DEBUGF(LOG_CF(data, cf, "query connect reply: %dms", *pres1)); - return CURLE_OK; - } - case CF_QUERY_TIMER_CONNECT: { - struct curltime *when = pres2; - *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT); - return CURLE_OK; - } - case CF_QUERY_TIMER_APPCONNECT: { - struct curltime *when = pres2; - *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT); - return CURLE_OK; - } - default: - break; + if(conn && conn->fclosesocket) { + if((sock == conn->sock[SECONDARYSOCKET]) && conn->bits.sock_accepted) + /* if this socket matches the second socket, and that was created with + accept, then we MUST NOT call the callback but clear the accepted + status */ + conn->bits.sock_accepted = FALSE; + else { + int rc; + Curl_multi_closed(data, sock); + Curl_set_in_callback(data, true); + rc = conn->fclosesocket(conn->closesocket_client, sock); + Curl_set_in_callback(data, false); + return rc; } } - return cf->next? - cf->next->cft->query(cf->next, data, query, pres1, pres2) : - CURLE_UNKNOWN_OPTION; -} + if(conn) + /* tell the multi-socket code about this */ + Curl_multi_closed(data, sock); -static void cf_he_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_he_ctx *ctx = cf->ctx; + sclose(sock); - DEBUGF(LOG_CF(data, cf, "destroy")); - if(ctx) { - cf_he_ctx_clear(cf, data); - } - /* release any resources held in state */ - Curl_safefree(ctx); + return 0; } -struct Curl_cftype Curl_cft_happy_eyeballs = { - "HAPPY-EYEBALLS", - 0, - CURL_LOG_DEFAULT, - cf_he_destroy, - cf_he_connect, - cf_he_close, - Curl_cf_def_get_host, - cf_he_get_select_socks, - cf_he_data_pending, - Curl_cf_def_send, - Curl_cf_def_recv, - Curl_cf_def_cntrl, - Curl_cf_def_conn_is_alive, - Curl_cf_def_conn_keep_alive, - cf_he_query, -}; - -/** - * Create a happy eyeball connection filter that uses the, once resolved, - * address information to connect on ip families based on connection - * configuration. - * @param pcf output, the created cfilter - * @param data easy handle used in creation - * @param conn connection the filter is created for - * @param cf_create method to create the sub-filters performing the - * actual connects. +/* + * Create a socket based on info from 'conn' and 'ai'. + * + * 'addr' should be a pointer to the correct struct to get data back, or NULL. + * 'sockfd' must be a pointer to a socket descriptor. + * + * If the open socket callback is set, used that! + * */ -static CURLcode -cf_happy_eyeballs_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - cf_ip_connect_create *cf_create, - const struct Curl_dns_entry *remotehost, - int transport) -{ - struct cf_he_ctx *ctx = NULL; - CURLcode result; - - (void)data; - (void)conn; - *pcf = NULL; - ctx = calloc(sizeof(*ctx), 1); - if(!ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - ctx->transport = transport; - ctx->cf_create = cf_create; - ctx->remotehost = remotehost; - - result = Curl_cf_create(pcf, &Curl_cft_happy_eyeballs, ctx); - -out: - if(result) { - Curl_safefree(*pcf); - Curl_safefree(ctx); - } - return result; -} - -struct transport_provider { - int transport; - cf_ip_connect_create *cf_create; -}; - -static -#ifndef DEBUGBUILD -const -#endif -struct transport_provider transport_providers[] = { - { TRNSPRT_TCP, Curl_cf_tcp_create }, -#ifdef ENABLE_QUIC - { TRNSPRT_QUIC, Curl_cf_quic_create }, -#endif - { TRNSPRT_UDP, Curl_cf_udp_create }, - { TRNSPRT_UNIX, Curl_cf_unix_create }, -}; - -#ifndef ARRAYSIZE -#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) -#endif - -static cf_ip_connect_create *get_cf_create(int transport) -{ - size_t i; - for(i = 0; i < ARRAYSIZE(transport_providers); ++i) { - if(transport == transport_providers[i].transport) - return transport_providers[i].cf_create; - } - return NULL; -} - -static CURLcode cf_he_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data, - const struct Curl_dns_entry *remotehost, - int transport) -{ - cf_ip_connect_create *cf_create; - struct Curl_cfilter *cf; - CURLcode result; - - /* Need to be first */ - DEBUGASSERT(cf_at); - cf_create = get_cf_create(transport); - if(!cf_create) { - DEBUGF(LOG_CF(data, cf_at, "unsupported transport type %d", transport)); - return CURLE_UNSUPPORTED_PROTOCOL; - } - result = cf_happy_eyeballs_create(&cf, data, cf_at->conn, - cf_create, remotehost, - transport); - if(result) - return result; - - Curl_conn_cf_insert_after(cf_at, cf); - return CURLE_OK; -} - -typedef enum { - CF_SETUP_INIT, - CF_SETUP_CNNCT_EYEBALLS, - CF_SETUP_CNNCT_SOCKS, - CF_SETUP_CNNCT_HTTP_PROXY, - CF_SETUP_CNNCT_HAPROXY, - CF_SETUP_CNNCT_SSL, - CF_SETUP_DONE -} cf_setup_state; - -struct cf_setup_ctx { - cf_setup_state state; - const struct Curl_dns_entry *remotehost; - int ssl_mode; - int transport; -}; - -static CURLcode cf_setup_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking, bool *done) +CURLcode Curl_socket(struct Curl_easy *data, + const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, + curl_socket_t *sockfd) { - struct cf_setup_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } + struct connectdata *conn = data->conn; + struct Curl_sockaddr_ex dummy; + + if(!addr) + /* if the caller doesn't want info back, use a local temp copy */ + addr = &dummy; + + /* + * The Curl_sockaddr_ex structure is basically libcurl's external API + * curl_sockaddr structure with enough space available to directly hold + * any protocol-specific address structures. The variable declared here + * will be used to pass / receive data to/from the fopensocket callback + * if this has been set, before that, it is initialized from parameters. + */ - /* connect current sub-chain */ -connect_sub_chain: - if(cf->next && !cf->next->connected) { - result = Curl_conn_cf_connect(cf->next, data, blocking, done); - if(result || !*done) - return result; + addr->family = ai->ai_family; + switch(conn->transport) { + case TRNSPRT_TCP: + addr->socktype = SOCK_STREAM; + addr->protocol = IPPROTO_TCP; + break; + case TRNSPRT_UNIX: + addr->socktype = SOCK_STREAM; + addr->protocol = IPPROTO_IP; + break; + default: /* UDP and QUIC */ + addr->socktype = SOCK_DGRAM; + addr->protocol = IPPROTO_UDP; + break; } - - if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) { - result = cf_he_insert_after(cf, data, ctx->remotehost, ctx->transport); - if(result) - return result; - ctx->state = CF_SETUP_CNNCT_EYEBALLS; - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; + addr->addrlen = ai->ai_addrlen; + + if(addr->addrlen > sizeof(struct Curl_sockaddr_storage)) + addr->addrlen = sizeof(struct Curl_sockaddr_storage); + memcpy(&addr->sa_addr, ai->ai_addr, addr->addrlen); + + if(data->set.fopensocket) { + /* + * If the opensocket callback is set, all the destination address + * information is passed to the callback. Depending on this information the + * callback may opt to abort the connection, this is indicated returning + * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When + * the callback returns a valid socket the destination address information + * might have been changed and this 'new' address will actually be used + * here to connect. + */ + Curl_set_in_callback(data, true); + *sockfd = data->set.fopensocket(data->set.opensocket_client, + CURLSOCKTYPE_IPCXN, + (struct curl_sockaddr *)addr); + Curl_set_in_callback(data, false); } + else + /* opensocket callback not set, so simply create the socket now */ + *sockfd = socket(addr->family, addr->socktype, addr->protocol); - /* sub-chain connected, do we need to add more? */ -#ifndef CURL_DISABLE_PROXY - if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) { - result = Curl_cf_socks_proxy_insert_after(cf, data); - if(result) - return result; - ctx->state = CF_SETUP_CNNCT_SOCKS; - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; - } + if(*sockfd == CURL_SOCKET_BAD) + /* no socket, no connection */ + return CURLE_COULDNT_CONNECT; - if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) { -#ifdef USE_SSL - if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype) - && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { - result = Curl_cf_ssl_proxy_insert_after(cf, data); - if(result) - return result; + if(conn->transport == TRNSPRT_QUIC) { + /* QUIC sockets need to be nonblocking */ + (void)curlx_nonblock(*sockfd, TRUE); + switch(addr->family) { +#if defined(__linux__) && defined(IP_MTU_DISCOVER) + case AF_INET: { + int val = IP_PMTUDISC_DO; + (void)setsockopt(*sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &val, + sizeof(val)); + break; } -#endif /* USE_SSL */ - -#if !defined(CURL_DISABLE_HTTP) - if(cf->conn->bits.tunnel_proxy) { - result = Curl_cf_http_proxy_insert_after(cf, data); - if(result) - return result; +#endif +#if defined(__linux__) && defined(IPV6_MTU_DISCOVER) + case AF_INET6: { + int val = IPV6_PMTUDISC_DO; + (void)setsockopt(*sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, + sizeof(val)); + break; } -#endif /* !CURL_DISABLE_HTTP */ - ctx->state = CF_SETUP_CNNCT_HTTP_PROXY; - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; - } -#endif /* !CURL_DISABLE_PROXY */ - - if(ctx->state < CF_SETUP_CNNCT_HAPROXY) { -#if !defined(CURL_DISABLE_PROXY) - if(data->set.haproxyprotocol) { - if(Curl_conn_is_ssl(cf->conn, cf->sockindex)) { - failf(data, "haproxy protocol not support with SSL " - "encryption in place (QUIC?)"); - return CURLE_UNSUPPORTED_PROTOCOL; - } - result = Curl_cf_haproxy_insert_after(cf, data); - if(result) - return result; +#endif } -#endif /* !CURL_DISABLE_PROXY */ - ctx->state = CF_SETUP_CNNCT_HAPROXY; - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; } - if(ctx->state < CF_SETUP_CNNCT_SSL) { -#ifdef USE_SSL - if((ctx->ssl_mode == CURL_CF_SSL_ENABLE - || (ctx->ssl_mode != CURL_CF_SSL_DISABLE - && cf->conn->handler->flags & PROTOPT_SSL)) /* we want SSL */ - && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */ - result = Curl_cf_ssl_insert_after(cf, data); - if(result) - return result; - } -#endif /* USE_SSL */ - ctx->state = CF_SETUP_CNNCT_SSL; - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; +#if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) + if(conn->scope_id && (addr->family == AF_INET6)) { + struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr; + sa6->sin6_scope_id = conn->scope_id; } +#endif - ctx->state = CF_SETUP_DONE; - cf->connected = TRUE; - *done = TRUE; return CURLE_OK; } -static void cf_setup_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_setup_ctx *ctx = cf->ctx; - - DEBUGF(LOG_CF(data, cf, "close")); - cf->connected = FALSE; - ctx->state = CF_SETUP_INIT; - - if(cf->next) { - cf->next->cft->do_close(cf->next, data); - Curl_conn_cf_discard_chain(&cf->next, data); - } -} - -static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_setup_ctx *ctx = cf->ctx; - - (void)data; - DEBUGF(LOG_CF(data, cf, "destroy")); - Curl_safefree(ctx); -} - - -struct Curl_cftype Curl_cft_setup = { - "SETUP", - 0, - CURL_LOG_DEFAULT, - cf_setup_destroy, - cf_setup_connect, - cf_setup_close, - Curl_cf_def_get_host, - Curl_cf_def_get_select_socks, - Curl_cf_def_data_pending, - Curl_cf_def_send, - Curl_cf_def_recv, - Curl_cf_def_cntrl, - Curl_cf_def_conn_is_alive, - Curl_cf_def_conn_keep_alive, - Curl_cf_def_query, -}; - -static CURLcode cf_setup_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - const struct Curl_dns_entry *remotehost, - int transport, - int ssl_mode) -{ - struct Curl_cfilter *cf = NULL; - struct cf_setup_ctx *ctx; - CURLcode result = CURLE_OK; - - (void)data; - ctx = calloc(sizeof(*ctx), 1); - if(!ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - ctx->state = CF_SETUP_INIT; - ctx->remotehost = remotehost; - ctx->ssl_mode = ssl_mode; - ctx->transport = transport; - - result = Curl_cf_create(&cf, &Curl_cft_setup, ctx); - if(result) - goto out; - ctx = NULL; - -out: - *pcf = result? NULL : cf; - free(ctx); - return result; -} - -static CURLcode cf_setup_add(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - const struct Curl_dns_entry *remotehost, - int transport, - int ssl_mode) -{ - struct Curl_cfilter *cf; - CURLcode result = CURLE_OK; - - DEBUGASSERT(data); - result = cf_setup_create(&cf, data, remotehost, transport, ssl_mode); - if(result) - goto out; - Curl_conn_cf_add(data, conn, sockindex, cf); -out: - return result; -} - -#ifdef DEBUGBUILD -/* used by unit2600.c */ -void Curl_debug_set_transport_provider(int transport, - cf_ip_connect_create *cf_create) +/* + * Curl_conncontrol() marks streams or connection for closure. + */ +void Curl_conncontrol(struct connectdata *conn, + int ctrl /* see defines in header */ +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + , const char *reason +#endif + ) { - size_t i; - for(i = 0; i < ARRAYSIZE(transport_providers); ++i) { - if(transport == transport_providers[i].transport) { - transport_providers[i].cf_create = cf_create; - return; - } + /* close if a connection, or a stream that isn't multiplexed. */ + /* This function will be called both before and after this connection is + associated with a transfer. */ + bool closeit; + DEBUGASSERT(conn); +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + (void)reason; /* useful for debugging */ +#endif + closeit = (ctrl == CONNCTRL_CONNECTION) || + ((ctrl == CONNCTRL_STREAM) && !(conn->handler->flags & PROTOPT_STREAM)); + if((ctrl == CONNCTRL_STREAM) && + (conn->handler->flags & PROTOPT_STREAM)) + ; + else if((bit)closeit != conn->bits.close) { + conn->bits.close = closeit; /* the only place in the source code that + should assign this bit */ } } -#endif /* DEBUGBUILD */ - -CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data, - const struct Curl_dns_entry *remotehost, - int transport, - int ssl_mode) -{ - struct Curl_cfilter *cf; - CURLcode result; - - DEBUGASSERT(data); - result = cf_setup_create(&cf, data, remotehost, transport, ssl_mode); - if(result) - goto out; - Curl_conn_cf_insert_after(cf_at, cf); -out: - return result; -} -CURLcode Curl_conn_setup(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - const struct Curl_dns_entry *remotehost, - int ssl_mode) +/* Data received can be cached at various levels, so check them all here. */ +bool Curl_conn_data_pending(struct connectdata *conn, int sockindex) { - CURLcode result = CURLE_OK; + int readable; + DEBUGASSERT(conn); - DEBUGASSERT(data); - DEBUGASSERT(conn->handler); - -#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) - if(!conn->cfilter[sockindex] && - conn->handler->protocol == CURLPROTO_HTTPS) { - DEBUGASSERT(ssl_mode != CURL_CF_SSL_DISABLE); - result = Curl_cf_https_setup(data, conn, sockindex, remotehost); - if(result) - goto out; - } -#endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */ - - /* Still no cfilter set, apply default. */ - if(!conn->cfilter[sockindex]) { - result = cf_setup_add(data, conn, sockindex, remotehost, - conn->transport, ssl_mode); - if(result) - goto out; - } + if(Curl_ssl_data_pending(conn, sockindex) || + Curl_recv_has_postponed_data(conn, sockindex)) + return true; - DEBUGASSERT(conn->cfilter[sockindex]); -out: - return result; + readable = SOCKET_READABLE(conn->sock[sockindex], 0); + return (readable > 0 && (readable & CURL_CSELECT_IN)); } - diff --git a/contrib/libs/curl/lib/connect.h b/contrib/libs/curl/lib/connect.h index 58264bdba4..582ff0813d 100644 --- a/contrib/libs/curl/lib/connect.h +++ b/contrib/libs/curl/lib/connect.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -29,7 +29,14 @@ #include "sockaddr.h" #include "timeval.h" -struct Curl_dns_entry; +CURLcode Curl_is_connected(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + bool *connected); + +CURLcode Curl_connecthost(struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_dns_entry *host); /* generic function that returns how much time there's left to run, according to the timeouts set */ @@ -51,8 +58,67 @@ curl_socket_t Curl_getconnectinfo(struct Curl_easy *data, bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, char *addr, int *port); +/* + * Check if a connection seems to be alive. + */ +bool Curl_connalive(struct connectdata *conn); + +#ifdef USE_WINSOCK +/* When you run a program that uses the Windows Sockets API, you may + experience slow performance when you copy data to a TCP server. + + https://support.microsoft.com/kb/823764 + + Work-around: Make the Socket Send Buffer Size Larger Than the Program Send + Buffer Size + +*/ +void Curl_sndbufset(curl_socket_t sockfd); +#else +#define Curl_sndbufset(y) Curl_nop_stmt +#endif + +void Curl_updateconninfo(struct Curl_easy *data, struct connectdata *conn, + curl_socket_t sockfd); +void Curl_conninfo_remote(struct Curl_easy *data, struct connectdata *conn, + curl_socket_t sockfd); +void Curl_conninfo_local(struct Curl_easy *data, curl_socket_t sockfd, + char *local_ip, int *local_port); void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn, char *local_ip, int local_port); +int Curl_closesocket(struct Curl_easy *data, struct connectdata *conn, + curl_socket_t sock); + +/* + * The Curl_sockaddr_ex structure is basically libcurl's external API + * curl_sockaddr structure with enough space available to directly hold any + * protocol-specific address structures. The variable declared here will be + * used to pass / receive data to/from the fopensocket callback if this has + * been set, before that, it is initialized from parameters. + */ +struct Curl_sockaddr_ex { + int family; + int socktype; + int protocol; + unsigned int addrlen; + union { + struct sockaddr addr; + struct Curl_sockaddr_storage buff; + } _sa_ex_u; +}; +#define sa_addr _sa_ex_u.addr + +/* + * Create a socket based on info from 'conn' and 'ai'. + * + * Fill in 'addr' and 'sockfd' accordingly if OK is returned. If the open + * socket callback is set, used that! + * + */ +CURLcode Curl_socket(struct Curl_easy *data, + const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, + curl_socket_t *sockfd); /* * Curl_conncontrol() marks the end of a connection/stream. The 'closeit' @@ -87,46 +153,6 @@ void Curl_conncontrol(struct connectdata *conn, #define connkeep(x,y) Curl_conncontrol(x, CONNCTRL_KEEP) #endif -/** - * Create a cfilter for making an "ip" connection to the - * given address, using parameters from `conn`. The "ip" connection - * can be a TCP socket, a UDP socket or even a QUIC connection. - * - * It MUST use only the supplied `ai` for its connection attempt. - * - * Such a filter may be used in "happy eyeball" scenarios, and its - * `connect` implementation needs to support non-blocking. Once connected, - * it MAY be installed in the connection filter chain to serve transfers. - */ -typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai, - int transport); - -CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data, - const struct Curl_dns_entry *remotehost, - int transport, - int ssl_mode); - -/** - * Setup the cfilters at `sockindex` in connection `conn`. - * If no filter chain is installed yet, inspects the configuration - * in `data` and `conn? to install a suitable filter chain. - */ -CURLcode Curl_conn_setup(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - const struct Curl_dns_entry *remotehost, - int ssl_mode); - -extern struct Curl_cftype Curl_cft_happy_eyeballs; -extern struct Curl_cftype Curl_cft_setup; - -#ifdef DEBUGBUILD -void Curl_debug_set_transport_provider(int transport, - cf_ip_connect_create *cf_create); -#endif +bool Curl_conn_data_pending(struct connectdata *conn, int sockindex); #endif /* HEADER_CURL_CONNECT_H */ diff --git a/contrib/libs/curl/lib/content_encoding.c b/contrib/libs/curl/lib/content_encoding.c index a1f1975c66..39bb9b5b13 100644 --- a/contrib/libs/curl/lib/content_encoding.c +++ b/contrib/libs/curl/lib/content_encoding.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,15 +33,7 @@ #endif #ifdef HAVE_BROTLI -#if defined(__GNUC__) -/* Ignore -Wvla warnings in brotli headers */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wvla" -#endif #error #include <brotli/decode.h> -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif #endif #ifdef HAVE_ZSTD @@ -53,9 +45,6 @@ #include "content_encoding.h" #include "strdup.h" #include "strcase.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" @@ -988,8 +977,7 @@ static const struct content_encoding error_encoding = { static struct contenc_writer * new_unencoding_writer(struct Curl_easy *data, const struct content_encoding *handler, - struct contenc_writer *downstream, - int order) + struct contenc_writer *downstream) { struct contenc_writer *writer; @@ -999,7 +987,6 @@ new_unencoding_writer(struct Curl_easy *data, if(writer) { writer->handler = handler; writer->downstream = downstream; - writer->order = order; if(handler->init_writer(data, writer)) { free(writer); writer = NULL; @@ -1055,10 +1042,10 @@ static const struct content_encoding *find_encoding(const char *name, /* Set-up the unencoding stack from the Content-Encoding header value. * See RFC 7231 section 3.1.2.2. */ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, - const char *enclist, int is_transfer) + const char *enclist, int maybechunked) { struct SingleRequest *k = &data->req; - unsigned int order = is_transfer? 2: 1; + int counter = 0; do { const char *name; @@ -1075,21 +1062,16 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, namelen = enclist - name + 1; /* Special case: chunked encoding is handled at the reader level. */ - if(is_transfer && namelen == 7 && strncasecompare(name, "chunked", 7)) { + if(maybechunked && namelen == 7 && strncasecompare(name, "chunked", 7)) { k->chunk = TRUE; /* chunks coming our way. */ Curl_httpchunk_init(data); /* init our chunky engine. */ } else if(namelen) { - const struct content_encoding *encoding; + const struct content_encoding *encoding = find_encoding(name, namelen); struct contenc_writer *writer; - if(is_transfer && !data->set.http_transfer_encoding) - /* not requested, ignore */ - return CURLE_OK; - encoding = find_encoding(name, namelen); if(!k->writer_stack) { - k->writer_stack = new_unencoding_writer(data, &client_encoding, - NULL, 0); + k->writer_stack = new_unencoding_writer(data, &client_encoding, NULL); if(!k->writer_stack) return CURLE_OUT_OF_MEMORY; @@ -1098,29 +1080,16 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, if(!encoding) encoding = &error_encoding; /* Defer error at stack use. */ - if(k->writer_stack_depth++ >= MAX_ENCODE_STACK) { - failf(data, "Reject response due to more than %u content encodings", - MAX_ENCODE_STACK); + if(++counter >= MAX_ENCODE_STACK) { + failf(data, "Reject response due to %u content encodings", + counter); return CURLE_BAD_CONTENT_ENCODING; } /* Stack the unencoding stage. */ - if(order >= k->writer_stack->order) { - writer = new_unencoding_writer(data, encoding, - k->writer_stack, order); - if(!writer) - return CURLE_OUT_OF_MEMORY; - k->writer_stack = writer; - } - else { - struct contenc_writer *w = k->writer_stack; - while(w->downstream && order < w->downstream->order) - w = w->downstream; - writer = new_unencoding_writer(data, encoding, - w->downstream, order); - if(!writer) - return CURLE_OUT_OF_MEMORY; - w->downstream = writer; - } + writer = new_unencoding_writer(data, encoding, k->writer_stack); + if(!writer) + return CURLE_OUT_OF_MEMORY; + k->writer_stack = writer; } } while(*enclist); @@ -1130,11 +1099,11 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, #else /* Stubs for builds without HTTP. */ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, - const char *enclist, int is_transfer) + const char *enclist, int maybechunked) { (void) data; (void) enclist; - (void) is_transfer; + (void) maybechunked; return CURLE_NOT_BUILT_IN; } diff --git a/contrib/libs/curl/lib/content_encoding.h b/contrib/libs/curl/lib/content_encoding.h index 56e7f97f70..3c278cf727 100644 --- a/contrib/libs/curl/lib/content_encoding.h +++ b/contrib/libs/curl/lib/content_encoding.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -28,7 +28,6 @@ struct contenc_writer { const struct content_encoding *handler; /* Encoding handler. */ struct contenc_writer *downstream; /* Downstream writer. */ - unsigned int order; /* Ordering within writer stack. */ }; /* Content encoding writer. */ @@ -47,7 +46,7 @@ struct content_encoding { CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, - const char *enclist, int is_transfer); + const char *enclist, int maybechunked); CURLcode Curl_unencode_write(struct Curl_easy *data, struct contenc_writer *writer, const char *buf, size_t nbytes); diff --git a/contrib/libs/curl/lib/cookie.c b/contrib/libs/curl/lib/cookie.c index 4345a84c6f..8eaedeeb7f 100644 --- a/contrib/libs/curl/lib/cookie.c +++ b/contrib/libs/curl/lib/cookie.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -101,14 +101,13 @@ Example set of cookies: #include "parsedate.h" #include "rename.h" #include "fopen.h" -#include "strdup.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" -static void strstore(char **str, const char *newstr, size_t len); +static void strstore(char **str, const char *newstr); static void freecookie(struct Cookie *co) { @@ -123,18 +122,15 @@ static void freecookie(struct Cookie *co) free(co); } -static bool cookie_tailmatch(const char *cookie_domain, - size_t cookie_domain_len, - const char *hostname) +static bool tailmatch(const char *cooke_domain, const char *hostname) { + size_t cookie_domain_len = strlen(cooke_domain); size_t hostname_len = strlen(hostname); if(hostname_len < cookie_domain_len) return FALSE; - if(!strncasecompare(cookie_domain, - hostname + hostname_len-cookie_domain_len, - cookie_domain_len)) + if(!strcasecompare(cooke_domain, hostname + hostname_len-cookie_domain_len)) return FALSE; /* @@ -180,7 +176,7 @@ static bool pathmatch(const char *cookie_path, const char *request_uri) /* #-fragments are already cut off! */ if(0 == strlen(uri_path) || uri_path[0] != '/') { - strstore(&uri_path, "/", 1); + strstore(&uri_path, "/"); if(!uri_path) return FALSE; } @@ -304,17 +300,18 @@ static char *sanitize_cookie_path(const char *cookie_path) /* some stupid site sends path attribute with '"'. */ len = strlen(new_path); if(new_path[0] == '\"') { - memmove(new_path, new_path + 1, len); + memmove((void *)new_path, (const void *)(new_path + 1), len); len--; } if(len && (new_path[len - 1] == '\"')) { - new_path[--len] = 0x0; + new_path[len - 1] = 0x0; + len--; } /* RFC6265 5.2.4 The Path Attribute */ if(new_path[0] != '/') { /* Let cookie-path be the default-path. */ - strstore(&new_path, "/", 1); + strstore(&new_path, "/"); return new_path; } @@ -333,13 +330,14 @@ static char *sanitize_cookie_path(const char *cookie_path) */ void Curl_cookie_loadfiles(struct Curl_easy *data) { - struct curl_slist *list = data->set.cookielist; + struct curl_slist *list = data->state.cookielist; if(list) { Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); while(list) { - struct CookieInfo *newcookies = - Curl_cookie_init(data, list->data, data->cookies, - data->set.cookiesession); + struct CookieInfo *newcookies = Curl_cookie_init(data, + list->data, + data->cookies, + data->set.cookiesession); if(!newcookies) /* * Failure may be due to OOM or a bad cookie; both are ignored @@ -350,6 +348,8 @@ void Curl_cookie_loadfiles(struct Curl_easy *data) data->cookies = newcookies; list = list->next; } + curl_slist_free_all(data->state.cookielist); /* clean up list */ + data->state.cookielist = NULL; /* don't do this again! */ Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); } } @@ -363,14 +363,10 @@ void Curl_cookie_loadfiles(struct Curl_easy *data) * parsing in a last-wins scenario. The caller is responsible for checking * for OOM errors. */ -static void strstore(char **str, const char *newstr, size_t len) +static void strstore(char **str, const char *newstr) { - DEBUGASSERT(newstr); - DEBUGASSERT(str); free(*str); - *str = Curl_memdup(newstr, len + 1); - if(*str) - (*str)[len] = 0; + *str = strdup(newstr); } /* @@ -432,19 +428,15 @@ static void remove_expired(struct CookieInfo *cookies) } /* Make sure domain contains a dot or is localhost. */ -static bool bad_domain(const char *domain, size_t len) +static bool bad_domain(const char *domain) { - if((len == 9) && strncasecompare(domain, "localhost", 9)) + if(strcasecompare(domain, "localhost")) return FALSE; else { /* there must be a dot present, but that dot must not be a trailing dot */ - char *dot = memchr(domain, '.', len); - if(dot) { - size_t i = dot - domain; - if((len - i) > 1) - /* the dot is not the last byte */ - return FALSE; - } + char *dot = strchr(domain, '.'); + if(dot) + return dot[1] ? FALSE : TRUE; } return TRUE; } @@ -484,6 +476,11 @@ static int invalid_octets(const char *p) */ struct Cookie * Curl_cookie_add(struct Curl_easy *data, + /* + * The 'data' pointer here may be NULL at times, and thus + * must only be used very carefully for things that can deal + * with data being NULL. Such as infof() and similar + */ struct CookieInfo *c, bool httpheader, /* TRUE if HTTP header-style line */ bool noexpire, /* if TRUE, skip remove_expired() */ @@ -504,7 +501,10 @@ Curl_cookie_add(struct Curl_easy *data, bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */ size_t myhash; - DEBUGASSERT(data); +#ifdef CURL_DISABLE_VERBOSE_STRINGS + (void)data; +#endif + DEBUGASSERT(MAX_SET_COOKIE_AMOUNT <= 255); /* counter is an unsigned char */ if(data->req.setcookies >= MAX_SET_COOKIE_AMOUNT) return NULL; @@ -515,8 +515,11 @@ Curl_cookie_add(struct Curl_easy *data, return NULL; /* bail out if we're this low on memory */ if(httpheader) { - /* This line was read off an HTTP-header */ + /* This line was read off a HTTP-header */ + char name[MAX_NAME]; + char what[MAX_NAME]; const char *ptr; + const char *semiptr; size_t linelength = strlen(lineptr); if(linelength > MAX_COOKIE_LINE) { @@ -525,66 +528,73 @@ Curl_cookie_add(struct Curl_easy *data, return NULL; } - ptr = lineptr; - do { - size_t vlen; - size_t nlen; + semiptr = strchr(lineptr, ';'); /* first, find a semicolon */ - while(*ptr && ISBLANK(*ptr)) - ptr++; + while(*lineptr && ISBLANK(*lineptr)) + lineptr++; - /* we have a <name>=<value> pair or a stand-alone word here */ - nlen = strcspn(ptr, ";\t\r\n="); - if(nlen) { + ptr = lineptr; + do { + /* we have a <what>=<this> pair or a stand-alone word here */ + name[0] = what[0] = 0; /* init the buffers */ + if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\t\r\n=] =%" + MAX_NAME_TXT "[^;\r\n]", + name, what)) { + /* + * Use strstore() below to properly deal with received cookie + * headers that have the same string property set more than once, + * and then we use the last one. + */ + const char *whatptr; bool done = FALSE; - bool sep = FALSE; - const char *namep = ptr; - const char *valuep; - - ptr += nlen; - - /* trim trailing spaces and tabs after name */ - while(nlen && ISBLANK(namep[nlen - 1])) - nlen--; - - if(*ptr == '=') { - vlen = strcspn(++ptr, ";\r\n"); - valuep = ptr; - sep = TRUE; - ptr = &valuep[vlen]; - - /* Strip off trailing whitespace from the value */ - while(vlen && ISBLANK(valuep[vlen-1])) - vlen--; - - /* Skip leading whitespace from the value */ - while(vlen && ISBLANK(*valuep)) { - valuep++; - vlen--; - } - - /* Reject cookies with a TAB inside the value */ - if(memchr(valuep, '\t', vlen)) { - freecookie(co); - infof(data, "cookie contains TAB, dropping"); - return NULL; - } - } - else { - valuep = NULL; - vlen = 0; - } + bool sep; + size_t len = strlen(what); + size_t nlen = strlen(name); + const char *endofn = &ptr[ nlen ]; /* * Check for too long individual name or contents, or too long * combination of name + contents. Chrome and Firefox support 4095 or * 4096 bytes combo */ - if(nlen >= (MAX_NAME-1) || vlen >= (MAX_NAME-1) || - ((nlen + vlen) > MAX_NAME)) { + if(nlen >= (MAX_NAME-1) || len >= (MAX_NAME-1) || + ((nlen + len) > MAX_NAME)) { freecookie(co); infof(data, "oversized cookie dropped, name/val %zu + %zu bytes", - nlen, vlen); + nlen, len); + return NULL; + } + + /* name ends with a '=' ? */ + sep = (*endofn == '=')?TRUE:FALSE; + + if(nlen) { + endofn--; /* move to the last character */ + if(ISBLANK(*endofn)) { + /* skip trailing spaces in name */ + while(*endofn && ISBLANK(*endofn) && nlen) { + endofn--; + nlen--; + } + name[nlen] = 0; /* new end of name */ + } + } + + /* Strip off trailing whitespace from the 'what' */ + while(len && ISBLANK(what[len-1])) { + what[len-1] = 0; + len--; + } + + /* Skip leading whitespace from the 'what' */ + whatptr = what; + while(*whatptr && ISBLANK(*whatptr)) + whatptr++; + + /* Reject cookies with a TAB inside the content */ + if(strchr(whatptr, '\t')) { + freecookie(co); + infof(data, "cookie contains TAB, dropping"); return NULL; } @@ -594,19 +604,13 @@ Curl_cookie_add(struct Curl_easy *data, * "the rest". Prefixes must start with '__' and end with a '-', so * only test for names where that can possibly be true. */ - if(nlen >= 7 && namep[0] == '_' && namep[1] == '_') { - if(strncasecompare("__Secure-", namep, 9)) + if(nlen > 3 && name[0] == '_' && name[1] == '_') { + if(!strncmp("__Secure-", name, 9)) co->prefix |= COOKIE_PREFIX__SECURE; - else if(strncasecompare("__Host-", namep, 7)) + else if(!strncmp("__Host-", name, 7)) co->prefix |= COOKIE_PREFIX__HOST; } - /* - * Use strstore() below to properly deal with received cookie - * headers that have the same string property set more than once, - * and then we use the last one. - */ - if(!co->name) { /* The very first name/value pair is the actual cookie name */ if(!sep) { @@ -614,20 +618,20 @@ Curl_cookie_add(struct Curl_easy *data, badcookie = TRUE; break; } - strstore(&co->name, namep, nlen); - strstore(&co->value, valuep, vlen); + co->name = strdup(name); + co->value = strdup(whatptr); done = TRUE; if(!co->name || !co->value) { badcookie = TRUE; break; } - if(invalid_octets(co->value) || invalid_octets(co->name)) { + if(invalid_octets(whatptr) || invalid_octets(name)) { infof(data, "invalid octets in name/value, cookie dropped"); badcookie = TRUE; break; } } - else if(!vlen) { + else if(!len) { /* * this was a "<name>=" with no content, and we must allow * 'secure' and 'httponly' specified this weirdly @@ -638,7 +642,7 @@ Curl_cookie_add(struct Curl_easy *data, * using a secure protocol, or when the cookie is being set by * reading from file */ - if((nlen == 6) && strncasecompare("secure", namep, 6)) { + if(strcasecompare("secure", name)) { if(secure || !c->running) { co->secure = TRUE; } @@ -647,7 +651,7 @@ Curl_cookie_add(struct Curl_easy *data, break; } } - else if((nlen == 8) && strncasecompare("httponly", namep, 8)) + else if(strcasecompare("httponly", name)) co->httponly = TRUE; else if(sep) /* there was a '=' so we're not done parsing this field */ @@ -655,8 +659,8 @@ Curl_cookie_add(struct Curl_easy *data, } if(done) ; - else if((nlen == 4) && strncasecompare("path", namep, 4)) { - strstore(&co->path, valuep, vlen); + else if(strcasecompare("path", name)) { + strstore(&co->path, whatptr); if(!co->path) { badcookie = TRUE; /* out of memory bad */ break; @@ -668,8 +672,7 @@ Curl_cookie_add(struct Curl_easy *data, break; } } - else if((nlen == 6) && - strncasecompare("domain", namep, 6) && vlen) { + else if(strcasecompare("domain", name) && whatptr[0]) { bool is_ip; /* @@ -677,10 +680,8 @@ Curl_cookie_add(struct Curl_easy *data, * the given domain is not valid and thus cannot be set. */ - if('.' == valuep[0]) { - valuep++; /* ignore preceding dot */ - vlen--; - } + if('.' == whatptr[0]) + whatptr++; /* ignore preceding dot */ #ifndef USE_LIBPSL /* @@ -688,17 +689,16 @@ Curl_cookie_add(struct Curl_easy *data, * TLD or otherwise "protected" suffix. To reduce risk, we require a * dot OR the exact host name being "localhost". */ - if(bad_domain(valuep, vlen)) + if(bad_domain(whatptr)) domain = ":"; #endif - is_ip = Curl_host_is_ipnum(domain ? domain : valuep); + is_ip = Curl_host_is_ipnum(domain ? domain : whatptr); if(!domain - || (is_ip && !strncmp(valuep, domain, vlen) && - (vlen == strlen(domain))) - || (!is_ip && cookie_tailmatch(valuep, vlen, domain))) { - strstore(&co->domain, valuep, vlen); + || (is_ip && !strcmp(whatptr, domain)) + || (!is_ip && tailmatch(whatptr, domain))) { + strstore(&co->domain, whatptr); if(!co->domain) { badcookie = TRUE; break; @@ -714,17 +714,17 @@ Curl_cookie_add(struct Curl_easy *data, */ badcookie = TRUE; infof(data, "skipped cookie with bad tailmatch domain: %s", - valuep); + whatptr); } } - else if((nlen == 7) && strncasecompare("version", namep, 7)) { - strstore(&co->version, valuep, vlen); + else if(strcasecompare("version", name)) { + strstore(&co->version, whatptr); if(!co->version) { badcookie = TRUE; break; } } - else if((nlen == 7) && strncasecompare("max-age", namep, 7)) { + else if(strcasecompare("max-age", name)) { /* * Defined in RFC2109: * @@ -734,14 +734,14 @@ Curl_cookie_add(struct Curl_easy *data, * client should discard the cookie. A value of zero means the * cookie should be discarded immediately. */ - strstore(&co->maxage, valuep, vlen); + strstore(&co->maxage, whatptr); if(!co->maxage) { badcookie = TRUE; break; } } - else if((nlen == 7) && strncasecompare("expires", namep, 7)) { - strstore(&co->expirestr, valuep, vlen); + else if(strcasecompare("expires", name)) { + strstore(&co->expirestr, whatptr); if(!co->expirestr) { badcookie = TRUE; break; @@ -756,29 +756,34 @@ Curl_cookie_add(struct Curl_easy *data, /* this is an "illegal" <what>=<this> pair */ } + if(!semiptr || !*semiptr) { + /* we already know there are no more cookies */ + semiptr = NULL; + continue; + } + + ptr = semiptr + 1; while(*ptr && ISBLANK(*ptr)) ptr++; - if(*ptr == ';') - ptr++; - else - break; - } while(1); + semiptr = strchr(ptr, ';'); /* now, find the next semicolon */ + + if(!semiptr && *ptr) + /* + * There are no more semicolons, but there's a final name=value pair + * coming up + */ + semiptr = strchr(ptr, '\0'); + } while(semiptr); if(co->maxage) { CURLofft offt; offt = curlx_strtoofft((*co->maxage == '\"')? &co->maxage[1]:&co->maxage[0], NULL, 10, &co->expires); - switch(offt) { - case CURL_OFFT_FLOW: + if(offt == CURL_OFFT_FLOW) /* overflow, used max value */ co->expires = CURL_OFF_T_MAX; - break; - case CURL_OFFT_INVAL: - /* negative or otherwise bad, expire */ - co->expires = 1; - break; - case CURL_OFFT_OK: + else if(!offt) { if(!co->expires) /* already expired */ co->expires = 1; @@ -787,7 +792,6 @@ Curl_cookie_add(struct Curl_easy *data, co->expires = CURL_OFF_T_MAX; else co->expires += now; - break; } } else if(co->expirestr) { @@ -860,7 +864,7 @@ Curl_cookie_add(struct Curl_easy *data, } else { /* - * This line is NOT an HTTP header style line, we do offer support for + * This line is NOT a HTTP header style line, we do offer support for * reading the odd netscape cookies-file format here */ char *ptr; @@ -1049,7 +1053,7 @@ Curl_cookie_add(struct Curl_easy *data, Curl_psl_release(data); } else - acceptable = !bad_domain(domain, strlen(domain)); + acceptable = !bad_domain(domain); if(!acceptable) { infof(data, "cookie '%s' dropped, domain '%s' must not " @@ -1120,11 +1124,17 @@ Curl_cookie_add(struct Curl_easy *data, if(replace_old) { /* the domains were identical */ - if(clist->spath && co->spath && - !strcasecompare(clist->spath, co->spath)) - replace_old = FALSE; - else if(!clist->spath != !co->spath) + if(clist->spath && co->spath) { + if(strcasecompare(clist->spath, co->spath)) + replace_old = TRUE; + else + replace_old = FALSE; + } + else if(!clist->spath && !co->spath) + replace_old = TRUE; + else replace_old = FALSE; + } if(replace_old && !co->livecookie && clist->livecookie) { @@ -1205,8 +1215,7 @@ Curl_cookie_add(struct Curl_easy *data, * * If 'newsession' is TRUE, discard all "session cookies" on read from file. * - * Note that 'data' might be called as NULL pointer. If data is NULL, 'file' - * will be ignored. + * Note that 'data' might be called as NULL pointer. * * Returns NULL on out of memory. Invalid cookies are ignored. */ @@ -1216,8 +1225,9 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, bool newsession) { struct CookieInfo *c; + FILE *fp = NULL; + bool fromfile = TRUE; char *line = NULL; - FILE *handle = NULL; if(!inc) { /* we didn't get a struct, create one */ @@ -1237,59 +1247,61 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, /* we got an already existing one, use that */ c = inc; } + c->running = FALSE; /* this is not running, this is init */ + + if(file && !strcmp(file, "-")) { + fp = stdin; + fromfile = FALSE; + } + else if(!file || !*file) { + /* points to an empty string or NULL */ + fp = NULL; + } + else { + fp = fopen(file, FOPEN_READTEXT); + if(!fp) + infof(data, "WARNING: failed to open cookie file \"%s\"", file); + } + c->newsession = newsession; /* new session? */ - if(data) { - FILE *fp = NULL; - if(file) { - if(!strcmp(file, "-")) - fp = stdin; + if(fp) { + char *lineptr; + bool headerline; + + line = malloc(MAX_COOKIE_LINE); + if(!line) + goto fail; + while(Curl_get_line(line, MAX_COOKIE_LINE, fp)) { + if(checkprefix("Set-Cookie:", line)) { + /* This is a cookie line, get it! */ + lineptr = &line[11]; + headerline = TRUE; + } else { - fp = fopen(file, "rb"); - if(!fp) - infof(data, "WARNING: failed to open cookie file \"%s\"", file); - else - handle = fp; + lineptr = line; + headerline = FALSE; } - } + while(*lineptr && ISBLANK(*lineptr)) + lineptr++; - c->running = FALSE; /* this is not running, this is init */ - if(fp) { - char *lineptr; - bool headerline; - - line = malloc(MAX_COOKIE_LINE); - if(!line) - goto fail; - while(Curl_get_line(line, MAX_COOKIE_LINE, fp)) { - if(checkprefix("Set-Cookie:", line)) { - /* This is a cookie line, get it! */ - lineptr = &line[11]; - headerline = TRUE; - } - else { - lineptr = line; - headerline = FALSE; - } - while(*lineptr && ISBLANK(*lineptr)) - lineptr++; + Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL, TRUE); + } + free(line); /* free the line buffer */ - Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL, TRUE); - } - free(line); /* free the line buffer */ + /* + * Remove expired cookies from the hash. We must make sure to run this + * after reading the file, and not on every cookie. + */ + remove_expired(c); - /* - * Remove expired cookies from the hash. We must make sure to run this - * after reading the file, and not on every cookie. - */ - remove_expired(c); + if(fromfile && fp) + fclose(fp); + } - if(handle) - fclose(handle); - } + c->running = TRUE; /* now, we're running */ + if(data) data->state.cookie_engine = TRUE; - c->running = TRUE; /* now, we're running */ - } return c; @@ -1301,8 +1313,8 @@ fail: */ if(!inc) Curl_cookie_cleanup(c); - if(handle) - fclose(handle); + if(fromfile && fp) + fclose(fp); return NULL; /* out of memory */ } @@ -1388,7 +1400,7 @@ static struct Cookie *dup_cookie(struct Cookie *src) } return d; -fail: + fail: freecookie(d); return NULL; } @@ -1431,8 +1443,7 @@ struct Cookie *Curl_cookie_getlist(struct Curl_easy *data, /* now check if the domain is correct */ if(!co->domain || - (co->tailmatch && !is_ip && - cookie_tailmatch(co->domain, strlen(co->domain), host)) || + (co->tailmatch && !is_ip && tailmatch(co->domain, host)) || ((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) { /* * the right part of the host matches the domain stuff in the @@ -1722,7 +1733,7 @@ static CURLcode cookie_output(struct Curl_easy *data, } /* - * If we reach here we have successfully written a cookie file so there is + * If we reach here we have successfully written a cookie file so theree is * no need to inspect the error, any error case should have jumped into the * error block below. */ @@ -1783,6 +1794,13 @@ void Curl_flush_cookies(struct Curl_easy *data, bool cleanup) CURLcode res; if(data->set.str[STRING_COOKIEJAR]) { + if(data->state.cookielist) { + /* If there is a list of cookie files to read, do it first so that + we have all the told files read before we write the new jar. + Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */ + Curl_cookie_loadfiles(data); + } + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); /* if we have a destination file for all the cookies to get dumped to */ @@ -1792,6 +1810,12 @@ void Curl_flush_cookies(struct Curl_easy *data, bool cleanup) data->set.str[STRING_COOKIEJAR], curl_easy_strerror(res)); } else { + if(cleanup && data->state.cookielist) { + /* since nothing is written, we can just free the list of cookie file + names */ + curl_slist_free_all(data->state.cookielist); /* clean up list */ + data->state.cookielist = NULL; + } Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); } diff --git a/contrib/libs/curl/lib/cookie.h b/contrib/libs/curl/lib/cookie.h index b3c0063b2c..abc0a2e8a0 100644 --- a/contrib/libs/curl/lib/cookie.h +++ b/contrib/libs/curl/lib/cookie.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -61,6 +61,7 @@ struct Cookie { struct CookieInfo { /* linked list of cookies we know of */ struct Cookie *cookies[COOKIE_HASH_SIZE]; + char *filename; /* file we read from/write to */ long numcookies; /* number of cookies in the "jar" */ bool running; /* state info, for cookie adding information */ @@ -69,34 +70,23 @@ struct CookieInfo { curl_off_t next_expiration; /* the next time at which expiration happens */ }; -/* The maximum sizes we accept for cookies. RFC 6265 section 6.1 says - "general-use user agents SHOULD provide each of the following minimum - capabilities": - - - At least 4096 bytes per cookie (as measured by the sum of the length of - the cookie's name, value, and attributes). +/* This is the maximum line length we accept for a cookie line. RFC 2109 + section 6.3 says: - In the 6265bis draft document section 5.4 it is phrased even stronger: "If - the sum of the lengths of the name string and the value string is more than - 4096 octets, abort these steps and ignore the set-cookie-string entirely." -*/ + "at least 4096 bytes per cookie (as measured by the size of the characters + that comprise the cookie non-terminal in the syntax description of the + Set-Cookie header)" -/** Limits for INCOMING cookies **/ + We allow max 5000 bytes cookie header. Max 4095 bytes length per cookie + name and value. Name + value may not exceed 4096 bytes. -/* The longest we allow a line to be when reading a cookie from a HTTP header - or from a cookie jar */ +*/ #define MAX_COOKIE_LINE 5000 /* Maximum length of an incoming cookie name or content we deal with. Longer cookies are ignored. */ #define MAX_NAME 4096 - -/* Maximum number of Set-Cookie: lines accepted in a single response. If more - such header lines are received, they are ignored. This value must be less - than 256 since an unsigned char is used to count. */ -#define MAX_SET_COOKIE_AMOUNT 50 - -/** Limits for OUTGOING cookies **/ +#define MAX_NAME_TXT "4095" /* Maximum size for an outgoing cookie line libcurl will use in an http request. This is the default maximum length used in some versions of Apache @@ -108,6 +98,11 @@ struct CookieInfo { keep the maximum HTTP request within the maximum allowed size. */ #define MAX_COOKIE_SEND_AMOUNT 150 +/* Maximum number of Set-Cookie: lines accepted in a single response. If more + such header lines are received, they are ignored. This value must be less + than 256 since an unsigned char is used to count. */ +#define MAX_SET_COOKIE_AMOUNT 50 + struct Curl_easy; /* * Add a cookie to the internal list of cookies. The domain and path arguments diff --git a/contrib/libs/curl/lib/curl_addrinfo.c b/contrib/libs/curl/lib/curl_addrinfo.c index 8d1b20c13b..a844101b92 100644 --- a/contrib/libs/curl/lib/curl_addrinfo.c +++ b/contrib/libs/curl/lib/curl_addrinfo.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -47,6 +47,11 @@ # include <inet.h> #endif +#if defined(NETWARE) && defined(__NOVELL_LIBC__) +# undef in_addr_t +# define in_addr_t unsigned long +#endif + #include <stddef.h> #include "curl_addrinfo.h" @@ -274,7 +279,7 @@ Curl_he2ai(const struct hostent *he, int port) for(i = 0; (curr = he->h_addr_list[i]) != NULL; i++) { size_t ss_size; - size_t namelen = strlen(he->h_name) + 1; /* include null-terminator */ + size_t namelen = strlen(he->h_name) + 1; /* include null-terminatior */ #ifdef ENABLE_IPV6 if(he->h_addrtype == AF_INET6) ss_size = sizeof(struct sockaddr_in6); diff --git a/contrib/libs/curl/lib/curl_addrinfo.h b/contrib/libs/curl/lib/curl_addrinfo.h index c757c49c5c..b778121a7c 100644 --- a/contrib/libs/curl/lib/curl_addrinfo.h +++ b/contrib/libs/curl/lib/curl_addrinfo.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/curl_base64.h b/contrib/libs/curl/lib/curl_base64.h index 806d4431cf..85368a163c 100644 --- a/contrib/libs/curl/lib/curl_base64.h +++ b/contrib/libs/curl/lib/curl_base64.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/curl_config-linux.h b/contrib/libs/curl/lib/curl_config-linux.h index f197878085..765dd67d61 100644 --- a/contrib/libs/curl/lib/curl_config-linux.h +++ b/contrib/libs/curl/lib/curl_config-linux.h @@ -142,21 +142,18 @@ /* Define to 1 if you have the alarm function. */ #define HAVE_ALARM 1 -/* Define to 1 if you have the `arc4random' function. */ -/* #undef HAVE_ARC4RANDOM */ - /* Define to 1 if you have the <arpa/inet.h> header file. */ #define HAVE_ARPA_INET_H 1 /* Define to 1 if you have the <arpa/tftp.h> header file. */ #define HAVE_ARPA_TFTP_H 1 +/* Define to 1 if you have the <assert.h> header file. */ +#define HAVE_ASSERT_H 1 + /* Define to 1 if you have _Atomic support. */ #define HAVE_ATOMIC 1 -/* Define to 1 if using AWS-LC. */ -/* #undef HAVE_AWSLC */ - /* Define to 1 if you have the basename function. */ #define HAVE_BASENAME 1 @@ -178,10 +175,6 @@ /* Define to 1 if you have the clock_gettime function and monotonic timer. */ #define HAVE_CLOCK_GETTIME_MONOTONIC 1 -/* Define to 1 if you have the clock_gettime function and raw monotonic timer. - */ -#define HAVE_CLOCK_GETTIME_MONOTONIC_RAW 1 - /* Define to 1 if you have the closesocket function. */ /* #undef HAVE_CLOSESOCKET */ @@ -204,6 +197,9 @@ /* Define to 1 if you have the <dlfcn.h> header file. */ #define HAVE_DLFCN_H 1 +/* Define to 1 if you have the <errno.h> header file. */ +#define HAVE_ERRNO_H 1 + /* Define to 1 if you have the <err.h> header file. */ /* #undef HAVE_ERR_H */ @@ -222,9 +218,6 @@ /* Define to 1 if you have the `fnmatch' function. */ #define HAVE_FNMATCH 1 -/* Define to 1 if you have the `fork' function. */ -#define HAVE_FORK 1 - /* Define to 1 if you have the freeaddrinfo function. */ #define HAVE_FREEADDRINFO 1 @@ -367,9 +360,6 @@ /* Define to 1 if you have the ldap.h header file. */ /* #undef HAVE_LDAP_H */ -/* Define to 1 if you have the `ldap_init_fd' function. */ -/* #undef HAVE_LDAP_INIT_FD */ - /* Use LDAPS implementation */ /* #undef HAVE_LDAP_SSL */ @@ -468,7 +458,7 @@ /* #undef HAVE_OLD_GSSMIT */ /* Define to 1 if using OpenSSL 3 or later. */ -#define HAVE_OPENSSL3 1 +/* #undef HAVE_OPENSSL3 */ /* Define to 1 if you have the <openssl/crypto.h> header file. */ #define HAVE_OPENSSL_CRYPTO_H 1 @@ -594,9 +584,6 @@ /* Define to 1 if you have the <ssl.h> header file. */ /* #undef HAVE_SSL_H */ -/* Define to 1 if you have the `SSL_set0_wbio' function. */ -#define HAVE_SSL_SET0_WBIO 1 - /* Define to 1 if you have the <stdatomic.h> header file. */ #define HAVE_STDATOMIC_H 1 @@ -729,6 +716,9 @@ /* Define to 1 if you have the windows.h header file. */ /* #undef HAVE_WINDOWS_H */ +/* Define to 1 if you have the winldap.h header file. */ +/* #undef HAVE_WINLDAP_H */ + /* Define to 1 if you have the winsock2.h header file. */ /* #undef HAVE_WINSOCK2_H */ @@ -738,9 +728,6 @@ /* if you have wolfSSL_DES_ecb_encrypt */ /* #undef HAVE_WOLFSSL_DES_ECB_ENCRYPT */ -/* if you have wolfSSL_BIO_set_shutdown */ -/* #undef HAVE_WOLFSSL_FULL_BIO */ - /* Define to 1 if you have the `wolfSSL_get_peer_certificate' function. */ /* #undef HAVE_WOLFSSL_GET_PEER_CERTIFICATE */ @@ -810,9 +797,6 @@ /* Size of curl_off_t in number of bytes */ #define SIZEOF_CURL_OFF_T 8 -/* Size of curl_socket_t in number of bytes */ -#define SIZEOF_CURL_SOCKET_T 4 - /* Size of int in number of bytes */ #ifndef SIZEOF_INT #error undefined SIZEOF_INT @@ -894,8 +878,8 @@ /* if ngtcp2_crypto_gnutls is in use */ /* #undef USE_NGTCP2_CRYPTO_GNUTLS */ -/* if ngtcp2_crypto_quictls is in use */ -/* #undef USE_NGTCP2_CRYPTO_QUICTLS */ +/* if ngtcp2_crypto_openssl is in use */ +/* #undef USE_NGTCP2_CRYPTO_OPENSSL */ /* if ngtcp2_crypto_wolfssl is in use */ /* #undef USE_NGTCP2_CRYPTO_WOLFSSL */ @@ -966,6 +950,9 @@ /* Version number of package */ #define VERSION "-" +/* Define to 1 to provide own prototypes. */ +/* #undef WANT_IDN_PROTOTYPES */ + /* Define to 1 if OS is AIX. */ #ifndef _ALL_SOURCE /* # undef _ALL_SOURCE */ diff --git a/contrib/libs/curl/lib/curl_ctype.h b/contrib/libs/curl/lib/curl_ctype.h index 1d1d60c28d..dc6b8cab86 100644 --- a/contrib/libs/curl/lib/curl_ctype.h +++ b/contrib/libs/curl/lib/curl_ctype.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -27,7 +27,7 @@ #define ISLOWHEXALHA(x) (((x) >= 'a') && ((x) <= 'f')) #define ISUPHEXALHA(x) (((x) >= 'A') && ((x) <= 'F')) -#define ISLOWCNTRL(x) ((unsigned char)(x) <= 0x1f) +#define ISLOWCNTRL(x) ((x) >= 0 && ((x) <= 0x1f)) #define IS7F(x) ((x) == 0x7f) #define ISLOWPRINT(x) (((x) >= 9) && ((x) <= 0x0d)) diff --git a/contrib/libs/curl/lib/curl_des.c b/contrib/libs/curl/lib/curl_des.c index 5c623b35bc..a2bf648c29 100644 --- a/contrib/libs/curl/lib/curl_des.c +++ b/contrib/libs/curl/lib/curl_des.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) 2015 - 2022, Steve Holme, <steve_holme@hotmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/contrib/libs/curl/lib/curl_des.h b/contrib/libs/curl/lib/curl_des.h index 6ec450accc..c1c167471a 100644 --- a/contrib/libs/curl/lib/curl_des.h +++ b/contrib/libs/curl/lib/curl_des.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) 2015 - 2022, Steve Holme, <steve_holme@hotmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/contrib/libs/curl/lib/curl_endian.c b/contrib/libs/curl/lib/curl_endian.c index 11c662a4c7..3cc7734f31 100644 --- a/contrib/libs/curl/lib/curl_endian.c +++ b/contrib/libs/curl/lib/curl_endian.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/curl_endian.h b/contrib/libs/curl/lib/curl_endian.h index fa283214b1..758d55f2c5 100644 --- a/contrib/libs/curl/lib/curl_endian.h +++ b/contrib/libs/curl/lib/curl_endian.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,4 +33,13 @@ unsigned int Curl_read32_le(const unsigned char *buf); /* Converts a 16-bit integer from big endian */ unsigned short Curl_read16_be(const unsigned char *buf); +#if (SIZEOF_CURL_OFF_T > 4) +/* Converts a 64-bit integer to little endian */ +#if defined(HAVE_LONGLONG) +void Curl_write64_le(const long long value, unsigned char *buffer); +#else +void Curl_write64_le(const __int64 value, unsigned char *buffer); +#endif +#endif + #endif /* HEADER_CURL_ENDIAN_H */ diff --git a/contrib/libs/curl/lib/curl_fnmatch.c b/contrib/libs/curl/lib/curl_fnmatch.c index 5f9ca4f1be..0dd1eb5ef5 100644 --- a/contrib/libs/curl/lib/curl_fnmatch.c +++ b/contrib/libs/curl/lib/curl_fnmatch.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -76,9 +76,9 @@ static int parsekeyword(unsigned char **pattern, unsigned char *charset) parsekey_state state = CURLFNM_PKW_INIT; #define KEYLEN 10 char keyword[KEYLEN] = { 0 }; + int found = FALSE; int i; unsigned char *p = *pattern; - bool found = FALSE; for(i = 0; !found; i++) { char c = *p++; if(i >= KEYLEN) @@ -368,13 +368,14 @@ int Curl_fnmatch(void *ptr, const char *pattern, const char *string) */ int Curl_fnmatch(void *ptr, const char *pattern, const char *string) { + int rc; (void)ptr; /* the argument is specified by the curl_fnmatch_callback prototype, but not used by Curl_fnmatch() */ if(!pattern || !string) { return CURL_FNMATCH_FAIL; } - - switch(fnmatch(pattern, string, 0)) { + rc = fnmatch(pattern, string, 0); + switch(rc) { case 0: return CURL_FNMATCH_MATCH; case FNM_NOMATCH: diff --git a/contrib/libs/curl/lib/curl_fnmatch.h b/contrib/libs/curl/lib/curl_fnmatch.h index 595646ff0d..8324be533d 100644 --- a/contrib/libs/curl/lib/curl_fnmatch.h +++ b/contrib/libs/curl/lib/curl_fnmatch.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/curl_get_line.c b/contrib/libs/curl/lib/curl_get_line.c index 686abe7511..22e3705f4c 100644 --- a/contrib/libs/curl/lib/curl_get_line.c +++ b/contrib/libs/curl/lib/curl_get_line.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,41 +41,17 @@ char *Curl_get_line(char *buf, int len, FILE *input) bool partial = FALSE; while(1) { char *b = fgets(buf, len, input); - if(b) { size_t rlen = strlen(b); - - if(!rlen) - break; - - if(b[rlen-1] == '\n') { - /* b is \n terminated */ + if(rlen && (b[rlen-1] == '\n')) { if(partial) { partial = FALSE; continue; } return b; } - else if(feof(input)) { - if(partial) - /* Line is already too large to return, ignore rest */ - break; - - if(rlen + 1 < (size_t) len) { - /* b is EOF terminated, insert missing \n */ - b[rlen] = '\n'; - b[rlen + 1] = '\0'; - return b; - } - else - /* Maximum buffersize reached + EOF - * This line is impossible to add a \n to so we'll ignore it - */ - break; - } - else - /* Maximum buffersize reached */ - partial = TRUE; + /* read a partial, discard the next piece that ends with newline */ + partial = TRUE; } else break; diff --git a/contrib/libs/curl/lib/curl_get_line.h b/contrib/libs/curl/lib/curl_get_line.h index 0ff32c5c2c..b2a534d001 100644 --- a/contrib/libs/curl/lib/curl_get_line.h +++ b/contrib/libs/curl/lib/curl_get_line.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/curl_gethostname.c b/contrib/libs/curl/lib/curl_gethostname.c index 706b2e6892..4747e938db 100644 --- a/contrib/libs/curl/lib/curl_gethostname.c +++ b/contrib/libs/curl/lib/curl_gethostname.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/curl_gethostname.h b/contrib/libs/curl/lib/curl_gethostname.h index 9281d9c242..b7360969ac 100644 --- a/contrib/libs/curl/lib/curl_gethostname.h +++ b/contrib/libs/curl/lib/curl_gethostname.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/curl_gssapi.c b/contrib/libs/curl/lib/curl_gssapi.c index 614f66e124..3e8936bf86 100644 --- a/contrib/libs/curl/lib/curl_gssapi.c +++ b/contrib/libs/curl/lib/curl_gssapi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2011 - 2022, 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 @@ -34,16 +34,10 @@ #include "curl_memory.h" #include "memdebug.h" -#if defined(__GNUC__) -#define CURL_ALIGN8 __attribute__ ((aligned(8))) -#else -#define CURL_ALIGN8 -#endif - -gss_OID_desc Curl_spnego_mech_oid CURL_ALIGN8 = { +gss_OID_desc Curl_spnego_mech_oid = { 6, (char *)"\x2b\x06\x01\x05\x05\x02" }; -gss_OID_desc Curl_krb5_mech_oid CURL_ALIGN8 = { +gss_OID_desc Curl_krb5_mech_oid = { 9, (char *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" }; diff --git a/contrib/libs/curl/lib/curl_hmac.h b/contrib/libs/curl/lib/curl_hmac.h index 11625c0cb4..36c0bd62e5 100644 --- a/contrib/libs/curl/lib/curl_hmac.h +++ b/contrib/libs/curl/lib/curl_hmac.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/curl_krb5.h b/contrib/libs/curl/lib/curl_krb5.h index ccf6b96a87..ccd6f10e3d 100644 --- a/contrib/libs/curl/lib/curl_krb5.h +++ b/contrib/libs/curl/lib/curl_krb5.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/curl_ldap.h b/contrib/libs/curl/lib/curl_ldap.h index 8a1d807ed9..ba3ede4260 100644 --- a/contrib/libs/curl/lib/curl_ldap.h +++ b/contrib/libs/curl/lib/curl_ldap.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/curl_log.c b/contrib/libs/curl/lib/curl_log.c deleted file mode 100644 index 782c35a632..0000000000 --- a/contrib/libs/curl/lib/curl_log.c +++ /dev/null @@ -1,228 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#include <curl/curl.h> - -#include "curl_log.h" -#include "urldata.h" -#include "easyif.h" -#include "cfilters.h" -#include "timeval.h" -#include "multiif.h" -#include "strcase.h" - -#include "cf-socket.h" -#include "connect.h" -#include "http2.h" -#include "http_proxy.h" -#include "cf-h1-proxy.h" -#include "cf-h2-proxy.h" -#include "cf-haproxy.h" -#include "cf-https-connect.h" -#include "socks.h" -#include "strtok.h" -#include "vtls/vtls.h" -#include "vquic/vquic.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - - -void Curl_debug(struct Curl_easy *data, curl_infotype type, - char *ptr, size_t size) -{ - if(data->set.verbose) { - static const char s_infotype[CURLINFO_END][3] = { - "* ", "< ", "> ", "{ ", "} ", "{ ", "} " }; - if(data->set.fdebug) { - bool inCallback = Curl_is_in_callback(data); - Curl_set_in_callback(data, true); - (void)(*data->set.fdebug)(data, type, ptr, size, data->set.debugdata); - Curl_set_in_callback(data, inCallback); - } - else { - switch(type) { - case CURLINFO_TEXT: - case CURLINFO_HEADER_OUT: - case CURLINFO_HEADER_IN: - fwrite(s_infotype[type], 2, 1, data->set.err); - fwrite(ptr, size, 1, data->set.err); - break; - default: /* nada */ - break; - } - } - } -} - - -/* Curl_failf() is for messages stating why we failed. - * The message SHALL NOT include any LF or CR. - */ -void Curl_failf(struct Curl_easy *data, const char *fmt, ...) -{ - DEBUGASSERT(!strchr(fmt, '\n')); - if(data->set.verbose || data->set.errorbuffer) { - va_list ap; - int len; - char error[CURL_ERROR_SIZE + 2]; - va_start(ap, fmt); - len = mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap); - - if(data->set.errorbuffer && !data->state.errorbuf) { - strcpy(data->set.errorbuffer, error); - data->state.errorbuf = TRUE; /* wrote error string */ - } - error[len++] = '\n'; - error[len] = '\0'; - Curl_debug(data, CURLINFO_TEXT, error, len); - va_end(ap); - } -} - -/* Curl_infof() is for info message along the way */ -#define MAXINFO 2048 - -void Curl_infof(struct Curl_easy *data, const char *fmt, ...) -{ - DEBUGASSERT(!strchr(fmt, '\n')); - if(data && data->set.verbose) { - va_list ap; - int len; - char buffer[MAXINFO + 2]; - va_start(ap, fmt); - len = mvsnprintf(buffer, MAXINFO, fmt, ap); - va_end(ap); - buffer[len++] = '\n'; - buffer[len] = '\0'; - Curl_debug(data, CURLINFO_TEXT, buffer, len); - } -} - -#ifdef DEBUGBUILD - -void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf, - const char *fmt, ...) -{ - DEBUGASSERT(cf); - if(data && Curl_log_cf_is_debug(cf, data)) { - va_list ap; - int len; - char buffer[MAXINFO + 2]; - len = msnprintf(buffer, MAXINFO, "[%s] ", cf->cft->name); - va_start(ap, fmt); - len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap); - va_end(ap); - buffer[len++] = '\n'; - buffer[len] = '\0'; - Curl_debug(data, CURLINFO_TEXT, buffer, len); - } -} - - -static struct Curl_cftype *cf_types[] = { - &Curl_cft_tcp, - &Curl_cft_udp, - &Curl_cft_unix, - &Curl_cft_tcp_accept, - &Curl_cft_happy_eyeballs, - &Curl_cft_setup, -#ifdef USE_NGHTTP2 - &Curl_cft_nghttp2, -#endif -#ifdef USE_SSL - &Curl_cft_ssl, - &Curl_cft_ssl_proxy, -#endif -#if !defined(CURL_DISABLE_PROXY) -#if !defined(CURL_DISABLE_HTTP) - &Curl_cft_h1_proxy, -#ifdef USE_NGHTTP2 - &Curl_cft_h2_proxy, -#endif - &Curl_cft_http_proxy, -#endif /* !CURL_DISABLE_HTTP */ - &Curl_cft_haproxy, - &Curl_cft_socks_proxy, -#endif /* !CURL_DISABLE_PROXY */ -#ifdef ENABLE_QUIC - &Curl_cft_http3, -#endif -#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) - &Curl_cft_http_connect, -#endif - NULL, -}; - -#ifndef ARRAYSIZE -#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) -#endif - -CURLcode Curl_log_init(void) -{ - const char *setting = getenv("CURL_DEBUG"); - if(setting) { - char *token, *tok_buf, *tmp; - size_t i; - - tmp = strdup(setting); - if(!tmp) - return CURLE_OUT_OF_MEMORY; - - token = strtok_r(tmp, ", ", &tok_buf); - while(token) { - for(i = 0; cf_types[i]; ++i) { - if(strcasecompare(token, cf_types[i]->name)) { - cf_types[i]->log_level = CURL_LOG_DEBUG; - break; - } - } - token = strtok_r(NULL, ", ", &tok_buf); - } - free(tmp); - } - return CURLE_OK; -} -#else /* DEBUGBUILD */ - -CURLcode Curl_log_init(void) -{ - return CURLE_OK; -} - -#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) -void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf, - const char *fmt, ...) -{ - (void)data; - (void)cf; - (void)fmt; -} -#endif - -#endif /* !DEBUGBUILD */ diff --git a/contrib/libs/curl/lib/curl_log.h b/contrib/libs/curl/lib/curl_log.h deleted file mode 100644 index ebfa5a0003..0000000000 --- a/contrib/libs/curl/lib/curl_log.h +++ /dev/null @@ -1,121 +0,0 @@ -#ifndef HEADER_CURL_LOG_H -#define HEADER_CURL_LOG_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -struct Curl_easy; -struct Curl_cfilter; - -/** - * Init logging, return != 0 on failure. - */ -CURLcode Curl_log_init(void); - - -void Curl_infof(struct Curl_easy *, const char *fmt, ...); -void Curl_failf(struct Curl_easy *, const char *fmt, ...); - -#if defined(CURL_DISABLE_VERBOSE_STRINGS) - -#if defined(HAVE_VARIADIC_MACROS_C99) -#define infof(...) Curl_nop_stmt -#elif defined(HAVE_VARIADIC_MACROS_GCC) -#define infof(x...) Curl_nop_stmt -#else -#error "missing VARIADIC macro define, fix and rebuild!" -#endif - -#else /* CURL_DISABLE_VERBOSE_STRINGS */ - -#define infof Curl_infof - -#endif /* CURL_DISABLE_VERBOSE_STRINGS */ - -#define failf Curl_failf - - -#define CURL_LOG_DEFAULT 0 -#define CURL_LOG_DEBUG 1 -#define CURL_LOG_TRACE 2 - - -/* the function used to output verbose information */ -void Curl_debug(struct Curl_easy *data, curl_infotype type, - char *ptr, size_t size); - -#ifdef DEBUGBUILD - -/* explainer: we have some mix configuration and werror settings - * that define HAVE_VARIADIC_MACROS_C99 even though C89 is enforced - * on gnuc and some other compiler. Need to treat carefully. - */ -#if defined(HAVE_VARIADIC_MACROS_C99) && \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) - -#define LOG_CF(data, cf, ...) \ - do { if(Curl_log_cf_is_debug(cf, data)) \ - Curl_log_cf_debug(data, cf, __VA_ARGS__); } while(0) -#else -#define LOG_CF Curl_log_cf_debug -#endif - -void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf, -#if defined(__GNUC__) && !defined(printf) && \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ - !defined(__MINGW32__) - const char *fmt, ...) - __attribute__((format(printf, 3, 4))); -#else - const char *fmt, ...); -#endif - -#define Curl_log_cf_is_debug(cf, data) \ - ((data) && (data)->set.verbose && \ - (cf) && (cf)->cft->log_level >= CURL_LOG_DEBUG) - - -#else /* !DEBUGBUILD */ - -#if defined(HAVE_VARIADIC_MACROS_C99) && \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) -#define LOG_CF(...) Curl_nop_stmt -#define Curl_log_cf_debug(...) Curl_nop_stmt -#elif defined(HAVE_VARIADIC_MACROS_GCC) && \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) -#define LOG_CF(x...) Curl_nop_stmt -#define Curl_log_cf_debug(x...) Curl_nop_stmt -#else -#define LOG_CF Curl_log_cf_debug -/* without c99, we seem unable to completely define away this function. */ -void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf, - const char *fmt, ...); -#endif - -#define Curl_log_cf_is_debug(x,y) ((void)(x), (void)(y), FALSE) - -#endif /* !DEBUGBUILD */ - -#define LOG_CF_IS_DEBUG(cf, data) Curl_log_cf_is_debug(cf, data) - -#endif /* HEADER_CURL_LOG_H */ diff --git a/contrib/libs/curl/lib/curl_md4.h b/contrib/libs/curl/lib/curl_md4.h index 03567b9916..8049355cff 100644 --- a/contrib/libs/curl/lib/curl_md4.h +++ b/contrib/libs/curl/lib/curl_md4.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/curl_md5.h b/contrib/libs/curl/lib/curl_md5.h index ec2512f002..789329654b 100644 --- a/contrib/libs/curl/lib/curl_md5.h +++ b/contrib/libs/curl/lib/curl_md5.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/curl_memory.h b/contrib/libs/curl/lib/curl_memory.h index b8c46d7939..092fc9f758 100644 --- a/contrib/libs/curl/lib/curl_memory.h +++ b/contrib/libs/curl/lib/curl_memory.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -52,67 +52,38 @@ * mentioned above will compile without any indication, but it will * trigger weird memory related issues at runtime. * + * OTOH some source files from 'lib' subdirectory may additionally be + * used directly as source code when using some curlx_ functions by + * third party programs that don't even use libcurl at all. When using + * these source files in this way it is necessary these are compiled + * with CURLX_NO_MEMORY_CALLBACKS defined, in order to ensure that no + * attempt of calling libcurl's memory callbacks is done from code + * which can not use this machinery. + * + * Notice that libcurl's 'memory tracking' system works chaining into + * the memory callback machinery. This implies that when compiling + * 'lib' source files with CURLX_NO_MEMORY_CALLBACKS defined this file + * disengages usage of libcurl's 'memory tracking' system, defining + * MEMDEBUG_NODEFINES and overriding CURLDEBUG purpose. + * + * CURLX_NO_MEMORY_CALLBACKS takes precedence over CURLDEBUG. This is + * done in order to allow building a 'memory tracking' enabled libcurl + * and at the same time allow building programs which do not use it. + * + * Programs and libraries in 'tests' subdirectories have specific + * purposes and needs, and as such each one will use whatever fits + * best, depending additionally whether it links with libcurl or not. + * + * Caveat emptor. Proper curlx_* separation is a work in progress + * the same as CURLX_NO_MEMORY_CALLBACKS usage, some adjustments may + * still be required. IOW don't use them yet, there are sharp edges. */ #ifdef HEADER_CURL_MEMDEBUG_H -/* cleanup after memdebug.h */ - -#ifdef MEMDEBUG_NODEFINES -#ifdef CURLDEBUG - -#undef strdup -#undef malloc -#undef calloc -#undef realloc -#undef free -#undef send -#undef recv - -#ifdef WIN32 -# ifdef UNICODE -# undef wcsdup -# undef _wcsdup -# undef _tcsdup -# else -# undef _tcsdup -# endif +#error "Header memdebug.h shall not be included before curl_memory.h" #endif -#undef socket -#undef accept -#ifdef HAVE_SOCKETPAIR -#undef socketpair -#endif - -#ifdef HAVE_GETADDRINFO -#if defined(getaddrinfo) && defined(__osf__) -#undef ogetaddrinfo -#else -#undef getaddrinfo -#endif -#endif /* HAVE_GETADDRINFO */ - -#ifdef HAVE_FREEADDRINFO -#undef freeaddrinfo -#endif /* HAVE_FREEADDRINFO */ - -/* sclose is probably already defined, redefine it! */ -#undef sclose -#undef fopen -#undef fdopen -#undef fclose - -#endif /* MEMDEBUG_NODEFINES */ -#endif /* CURLDEBUG */ - -#undef HEADER_CURL_MEMDEBUG_H -#endif /* HEADER_CURL_MEMDEBUG_H */ - -/* -** Following section applies even when CURLDEBUG is not defined. -*/ - -#undef fake_sclose +#ifndef CURLX_NO_MEMORY_CALLBACKS #ifndef CURL_DID_MEMORY_FUNC_TYPEDEFS /* only if not already done */ /* @@ -175,4 +146,13 @@ extern curl_wcsdup_callback Curl_cwcsdup; #endif #endif /* CURLDEBUG */ + +#else /* CURLX_NO_MEMORY_CALLBACKS */ + +#ifndef MEMDEBUG_NODEFINES +#define MEMDEBUG_NODEFINES +#endif + +#endif /* CURLX_NO_MEMORY_CALLBACKS */ + #endif /* HEADER_CURL_MEMORY_H */ diff --git a/contrib/libs/curl/lib/curl_memrchr.c b/contrib/libs/curl/lib/curl_memrchr.c index 3f3dc6de16..c329a6176a 100644 --- a/contrib/libs/curl/lib/curl_memrchr.c +++ b/contrib/libs/curl/lib/curl_memrchr.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/curl_memrchr.h b/contrib/libs/curl/lib/curl_memrchr.h index a1a4ba0927..e7654e1d9a 100644 --- a/contrib/libs/curl/lib/curl_memrchr.h +++ b/contrib/libs/curl/lib/curl_memrchr.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/curl_multibyte.c b/contrib/libs/curl/lib/curl_multibyte.c index 522ea34e82..309dccbfef 100644 --- a/contrib/libs/curl/lib/curl_multibyte.c +++ b/contrib/libs/curl/lib/curl_multibyte.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/curl_multibyte.h b/contrib/libs/curl/lib/curl_multibyte.h index ddac1f6382..929714873a 100644 --- a/contrib/libs/curl/lib/curl_multibyte.h +++ b/contrib/libs/curl/lib/curl_multibyte.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/curl_ntlm_core.c b/contrib/libs/curl/lib/curl_ntlm_core.c index f94f2900b9..14c9f34d21 100644 --- a/contrib/libs/curl/lib/curl_ntlm_core.c +++ b/contrib/libs/curl/lib/curl_ntlm_core.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -36,13 +36,12 @@ /* Please keep the SSL backend-specific #if branches in this order: 1. USE_OPENSSL - 2. USE_WOLFSSL - 3. USE_GNUTLS - 4. USE_NSS - 5. USE_MBEDTLS - 6. USE_SECTRANSP - 7. USE_OS400CRYPTO - 8. USE_WIN32_CRYPTO + 2. USE_GNUTLS + 3. USE_NSS + 4. USE_MBEDTLS + 5. USE_SECTRANSP + 6. USE_OS400CRYPTO + 7. USE_WIN32_CRYPTO This ensures that: - the same SSL branch gets activated throughout this source @@ -83,10 +82,6 @@ # define DES_ecb_encrypt des_ecb_encrypt # define DESKEY(x) x # define DESKEYARG(x) x -# elif defined(OPENSSL_IS_AWSLC) -# define DES_set_key_unchecked (void)DES_set_key -# define DESKEYARG(x) *x -# define DESKEY(x) &x # else # define DESKEYARG(x) *x # define DESKEY(x) &x @@ -191,9 +186,9 @@ static void setup_des_key(const unsigned char *key_56, #elif defined(USE_NSS) /* - * encrypt_des() expands a 56 bit key KEY_56 to 64 bit and encrypts 64 bit of - * data, using the expanded key. IN should point to 64 bits of source data, - * OUT to a 64 bit output buffer. + * Expands a 56 bit key KEY_56 to 64 bit and encrypts 64 bit of data, using + * the expanded key. The caller is responsible for giving 64 bit of valid + * data is IN and (at least) 64 bit large buffer as OUT. */ static bool encrypt_des(const unsigned char *in, unsigned char *out, const unsigned char *key_56) @@ -663,8 +658,7 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash, LONGQUARTET(tw.dwLowDateTime), LONGQUARTET(tw.dwHighDateTime)); memcpy(ptr + 32, challenge_client, 8); - if(ntlm->target_info_len) - memcpy(ptr + 44, ntlm->target_info, ntlm->target_info_len); + memcpy(ptr + 44, ntlm->target_info, ntlm->target_info_len); /* Concatenate the Type 2 challenge with the BLOB and do HMAC MD5 */ memcpy(ptr + 8, &ntlm->nonce[0], 8); diff --git a/contrib/libs/curl/lib/curl_ntlm_core.h b/contrib/libs/curl/lib/curl_ntlm_core.h index 52dd5cf4e2..8774838f66 100644 --- a/contrib/libs/curl/lib/curl_ntlm_core.h +++ b/contrib/libs/curl/lib/curl_ntlm_core.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,11 +37,11 @@ #define NTLM_NEEDS_NSS_INIT #endif -#if defined(USE_OPENSSL) -# include <openssl/ssl.h> -#elif defined(USE_WOLFSSL) +#ifdef USE_WOLFSSL # error #include <wolfssl/options.h> # error #include <wolfssl/openssl/ssl.h> +#elif defined(USE_OPENSSL) +# include <openssl/ssl.h> #endif /* Helpers to generate function byte arguments in little endian order */ diff --git a/contrib/libs/curl/lib/curl_ntlm_wb.c b/contrib/libs/curl/lib/curl_ntlm_wb.c index a10e2a1b09..33dcf0ce25 100644 --- a/contrib/libs/curl/lib/curl_ntlm_wb.c +++ b/contrib/libs/curl/lib/curl_ntlm_wb.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -385,7 +385,7 @@ CURLcode Curl_output_ntlm_wb(struct Curl_easy *data, struct connectdata *conn, bool proxy) { /* point to the address of the pointer that holds the string to send to the - server, which is for a plain host or for an HTTP proxy */ + server, which is for a plain host or for a HTTP proxy */ char **allocuserpwd; /* point to the name and password for this */ const char *userp; diff --git a/contrib/libs/curl/lib/curl_ntlm_wb.h b/contrib/libs/curl/lib/curl_ntlm_wb.h index 37704c0fe0..1f04db8c3d 100644 --- a/contrib/libs/curl/lib/curl_ntlm_wb.h +++ b/contrib/libs/curl/lib/curl_ntlm_wb.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/curl_path.c b/contrib/libs/curl/lib/curl_path.c index 2e5e3e7ba8..67065809ba 100644 --- a/contrib/libs/curl/lib/curl_path.c +++ b/contrib/libs/curl/lib/curl_path.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -32,68 +32,66 @@ #include "escape.h" #include "memdebug.h" -#define MAX_SSHPATH_LEN 100000 /* arbitrary */ - /* figure out the path to work with in this particular request */ CURLcode Curl_getworkingpath(struct Curl_easy *data, char *homedir, /* when SFTP is used */ char **path) /* returns the allocated real path to work with */ { + char *real_path = NULL; char *working_path; size_t working_path_len; - struct dynbuf npath; CURLcode result = Curl_urldecode(data->state.up.path, 0, &working_path, &working_path_len, REJECT_ZERO); if(result) return result; - /* new path to switch to in case we need to */ - Curl_dyn_init(&npath, MAX_SSHPATH_LEN); - /* Check for /~/, indicating relative to the user's home directory */ - if((data->conn->handler->protocol & CURLPROTO_SCP) && - (working_path_len > 3) && (!memcmp(working_path, "/~/", 3))) { - /* It is referenced to the home directory, so strip the leading '/~/' */ - if(Curl_dyn_addn(&npath, &working_path[3], working_path_len - 3)) { + if(data->conn->handler->protocol & CURLPROTO_SCP) { + real_path = malloc(working_path_len + 1); + if(!real_path) { free(working_path); return CURLE_OUT_OF_MEMORY; } + if((working_path_len > 3) && (!memcmp(working_path, "/~/", 3))) + /* It is referenced to the home directory, so strip the leading '/~/' */ + memcpy(real_path, working_path + 3, working_path_len - 2); + else + memcpy(real_path, working_path, 1 + working_path_len); } - else if((data->conn->handler->protocol & CURLPROTO_SFTP) && - (!strcmp("/~", working_path) || - ((working_path_len > 2) && !memcmp(working_path, "/~/", 3)))) { - if(Curl_dyn_add(&npath, homedir)) { - free(working_path); - return CURLE_OUT_OF_MEMORY; + else if(data->conn->handler->protocol & CURLPROTO_SFTP) { + if((working_path_len > 1) && (working_path[1] == '~')) { + size_t homelen = strlen(homedir); + real_path = malloc(homelen + working_path_len + 1); + if(!real_path) { + free(working_path); + return CURLE_OUT_OF_MEMORY; + } + /* It is referenced to the home directory, so strip the + leading '/' */ + memcpy(real_path, homedir, homelen); + real_path[homelen] = '/'; + real_path[homelen + 1] = '\0'; + if(working_path_len > 3) { + memcpy(real_path + homelen + 1, working_path + 3, + 1 + working_path_len -3); + } } - if(working_path_len > 2) { - size_t len; - const char *p; - int copyfrom = 3; - /* Copy a separating '/' if homedir does not end with one */ - len = Curl_dyn_len(&npath); - p = Curl_dyn_ptr(&npath); - if(len && (p[len-1] != '/')) - copyfrom = 2; - - if(Curl_dyn_addn(&npath, - &working_path[copyfrom], working_path_len - copyfrom)) { + else { + real_path = malloc(working_path_len + 1); + if(!real_path) { free(working_path); return CURLE_OUT_OF_MEMORY; } + memcpy(real_path, working_path, 1 + working_path_len); } } - if(Curl_dyn_len(&npath)) { - free(working_path); + free(working_path); - /* store the pointer for the caller to receive */ - *path = Curl_dyn_ptr(&npath); - } - else - *path = working_path; + /* store the pointer for the caller to receive */ + *path = real_path; return CURLE_OK; } @@ -150,12 +148,15 @@ CURLcode Curl_get_pathname(const char **cpp, char **path, char *homedir) break; } if(cp[i] == '\0') { /* End of string */ + /*error("Unterminated quote");*/ goto fail; } if(cp[i] == '\\') { /* Escaped characters */ i++; if(cp[i] != '\'' && cp[i] != '\"' && cp[i] != '\\') { + /*error("Bad escaped character '\\%c'", + cp[i]);*/ goto fail; } } @@ -163,6 +164,7 @@ CURLcode Curl_get_pathname(const char **cpp, char **path, char *homedir) } if(j == 0) { + /*error("Empty quotes");*/ goto fail; } *cpp = cp + i + strspn(cp + i, WHITESPACE); @@ -191,7 +193,7 @@ CURLcode Curl_get_pathname(const char **cpp, char **path, char *homedir) } return CURLE_OK; -fail: + fail: Curl_safefree(*path); return CURLE_QUOTE_ERROR; } diff --git a/contrib/libs/curl/lib/curl_printf.h b/contrib/libs/curl/lib/curl_printf.h index 46ef344f76..3823828cd9 100644 --- a/contrib/libs/curl/lib/curl_printf.h +++ b/contrib/libs/curl/lib/curl_printf.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,7 +37,6 @@ # undef vprintf # undef vfprintf # undef vsnprintf -# undef mvsnprintf # undef aprintf # undef vaprintf # define printf curl_mprintf diff --git a/contrib/libs/curl/lib/curl_range.c b/contrib/libs/curl/lib/curl_range.c index d499953c9e..dd92d05b39 100644 --- a/contrib/libs/curl/lib/curl_range.c +++ b/contrib/libs/curl/lib/curl_range.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -44,12 +44,12 @@ CURLcode Curl_range(struct Curl_easy *data) if(data->state.use_range && data->state.range) { CURLofft from_t; CURLofft to_t; - from_t = curlx_strtoofft(data->state.range, &ptr, 10, &from); + from_t = curlx_strtoofft(data->state.range, &ptr, 0, &from); if(from_t == CURL_OFFT_FLOW) return CURLE_RANGE_ERROR; while(*ptr && (ISBLANK(*ptr) || (*ptr == '-'))) ptr++; - to_t = curlx_strtoofft(ptr, &ptr2, 10, &to); + to_t = curlx_strtoofft(ptr, &ptr2, 0, &to); if(to_t == CURL_OFFT_FLOW) return CURLE_RANGE_ERROR; if((to_t == CURL_OFFT_INVAL) && !from_t) { diff --git a/contrib/libs/curl/lib/curl_range.h b/contrib/libs/curl/lib/curl_range.h index 77679e2b94..33570abc25 100644 --- a/contrib/libs/curl/lib/curl_range.h +++ b/contrib/libs/curl/lib/curl_range.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/curl_rtmp.c b/contrib/libs/curl/lib/curl_rtmp.c index 406fb42ac0..b0c371041e 100644 --- a/contrib/libs/curl/lib/curl_rtmp.c +++ b/contrib/libs/curl/lib/curl_rtmp.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) Howard Chu, <hyc@highlandsun.com> + * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012, Howard Chu, <hyc@highlandsun.com> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -85,7 +85,7 @@ const struct Curl_handler Curl_handler_rtmp = { PORT_RTMP, /* defport */ CURLPROTO_RTMP, /* protocol */ CURLPROTO_RTMP, /* family */ - PROTOPT_NONE /* flags */ + PROTOPT_NONE /* flags*/ }; const struct Curl_handler Curl_handler_rtmpt = { @@ -108,7 +108,7 @@ const struct Curl_handler Curl_handler_rtmpt = { PORT_RTMPT, /* defport */ CURLPROTO_RTMPT, /* protocol */ CURLPROTO_RTMPT, /* family */ - PROTOPT_NONE /* flags */ + PROTOPT_NONE /* flags*/ }; const struct Curl_handler Curl_handler_rtmpe = { @@ -131,7 +131,7 @@ const struct Curl_handler Curl_handler_rtmpe = { PORT_RTMP, /* defport */ CURLPROTO_RTMPE, /* protocol */ CURLPROTO_RTMPE, /* family */ - PROTOPT_NONE /* flags */ + PROTOPT_NONE /* flags*/ }; const struct Curl_handler Curl_handler_rtmpte = { @@ -154,7 +154,7 @@ const struct Curl_handler Curl_handler_rtmpte = { PORT_RTMPT, /* defport */ CURLPROTO_RTMPTE, /* protocol */ CURLPROTO_RTMPTE, /* family */ - PROTOPT_NONE /* flags */ + PROTOPT_NONE /* flags*/ }; const struct Curl_handler Curl_handler_rtmps = { @@ -177,7 +177,7 @@ const struct Curl_handler Curl_handler_rtmps = { PORT_RTMPS, /* defport */ CURLPROTO_RTMPS, /* protocol */ CURLPROTO_RTMP, /* family */ - PROTOPT_NONE /* flags */ + PROTOPT_NONE /* flags*/ }; const struct Curl_handler Curl_handler_rtmpts = { @@ -200,7 +200,7 @@ const struct Curl_handler Curl_handler_rtmpts = { PORT_RTMPS, /* defport */ CURLPROTO_RTMPTS, /* protocol */ CURLPROTO_RTMPT, /* family */ - PROTOPT_NONE /* flags */ + PROTOPT_NONE /* flags*/ }; static CURLcode rtmp_setup_connection(struct Curl_easy *data, @@ -231,7 +231,7 @@ static CURLcode rtmp_connect(struct Curl_easy *data, bool *done) /* We have to know if it's a write before we send the * connect request packet */ - if(data->state.upload) + if(data->set.upload) r->Link.protocol |= RTMP_FEATURE_WRITE; /* For plain streams, use the buffer toggle trick to keep data flowing */ @@ -263,7 +263,7 @@ static CURLcode rtmp_do(struct Curl_easy *data, bool *done) if(!RTMP_ConnectStream(r, 0)) return CURLE_FAILED_INIT; - if(data->state.upload) { + if(data->set.upload) { Curl_pgrsSetUploadSize(data, data->state.infilesize); Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); } diff --git a/contrib/libs/curl/lib/curl_rtmp.h b/contrib/libs/curl/lib/curl_rtmp.h index 9b93ee060b..f856085dc3 100644 --- a/contrib/libs/curl/lib/curl_rtmp.h +++ b/contrib/libs/curl/lib/curl_rtmp.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Howard Chu, <hyc@highlandsun.com> + * Copyright (C) 2010 - 2022, Howard Chu, <hyc@highlandsun.com> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/contrib/libs/curl/lib/curl_sasl.c b/contrib/libs/curl/lib/curl_sasl.c index 1cb0e546fe..9684ee476e 100644 --- a/contrib/libs/curl/lib/curl_sasl.c +++ b/contrib/libs/curl/lib/curl_sasl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2022, 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 @@ -36,8 +36,7 @@ #include "curl_setup.h" #if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \ - !defined(CURL_DISABLE_POP3) || \ - (!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)) + !defined(CURL_DISABLE_POP3) #include <curl/curl.h> #include "urldata.h" @@ -45,7 +44,6 @@ #include "curl_base64.h" #include "curl_md5.h" #include "vauth/vauth.h" -#include "cfilters.h" #include "vtls/vtls.h" #include "curl_hmac.h" #include "curl_sasl.h" @@ -221,12 +219,12 @@ void Curl_sasl_init(struct SASL *sasl, struct Curl_easy *data, } /* - * sasl_state() + * state() * * This is the ONLY way to change SASL state! */ -static void sasl_state(struct SASL *sasl, struct Curl_easy *data, - saslstate newstate) +static void state(struct SASL *sasl, struct Curl_easy *data, + saslstate newstate) { #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) /* for debug purposes */ @@ -342,8 +340,8 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, struct bufref resp; saslstate state1 = SASL_STOP; saslstate state2 = SASL_FINAL; - const char *hostname, *disp_hostname; - int port; + const char * const hostname = SSL_HOST_NAME(); + const long int port = SSL_HOST_PORT(); #if defined(USE_KERBEROS5) || defined(USE_NTLM) const char *service = data->set.str[STRING_SERVICE_NAME] ? data->set.str[STRING_SERVICE_NAME] : @@ -352,7 +350,6 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, const char *oauth_bearer = data->set.str[STRING_BEARER]; struct bufref nullmsg; - Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &disp_hostname, &port); Curl_bufref_init(&nullmsg); Curl_bufref_init(&resp); sasl->force_ir = force_ir; /* Latch for future use */ @@ -508,7 +505,7 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, if(!result) { *progress = SASL_INPROGRESS; - sasl_state(sasl, data, Curl_bufref_ptr(&resp) ? state2 : state1); + state(sasl, data, Curl_bufref_ptr(&resp) ? state2 : state1); } } @@ -528,8 +525,8 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, struct connectdata *conn = data->conn; saslstate newstate = SASL_FINAL; struct bufref resp; - const char *hostname, *disp_hostname; - int port; + const char * const hostname = SSL_HOST_NAME(); + const long int port = SSL_HOST_PORT(); #if !defined(CURL_DISABLE_CRYPTO_AUTH) || defined(USE_KERBEROS5) || \ defined(USE_NTLM) const char *service = data->set.str[STRING_SERVICE_NAME] ? @@ -539,7 +536,6 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, const char *oauth_bearer = data->set.str[STRING_BEARER]; struct bufref serverdata; - Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &disp_hostname, &port); Curl_bufref_init(&serverdata); Curl_bufref_init(&resp); *progress = SASL_INPROGRESS; @@ -548,14 +544,14 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, if(code != sasl->params->finalcode) result = CURLE_LOGIN_DENIED; *progress = SASL_DONE; - sasl_state(sasl, data, SASL_STOP); + state(sasl, data, SASL_STOP); return result; } if(sasl->state != SASL_CANCEL && sasl->state != SASL_OAUTH2_RESP && code != sasl->params->contcode) { *progress = SASL_DONE; - sasl_state(sasl, data, SASL_STOP); + state(sasl, data, SASL_STOP); return CURLE_LOGIN_DENIED; } @@ -698,7 +694,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, if(code == sasl->params->finalcode) { /* Final response was received so we are done */ *progress = SASL_DONE; - sasl_state(sasl, data, SASL_STOP); + state(sasl, data, SASL_STOP); return result; } else if(code == sasl->params->contcode) { @@ -708,7 +704,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, } else { *progress = SASL_DONE; - sasl_state(sasl, data, SASL_STOP); + state(sasl, data, SASL_STOP); return CURLE_LOGIN_DENIED; } @@ -745,7 +741,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, Curl_bufref_free(&resp); - sasl_state(sasl, data, newstate); + state(sasl, data, newstate); return result; } diff --git a/contrib/libs/curl/lib/curl_sasl.h b/contrib/libs/curl/lib/curl_sasl.h index e94e6431a2..c709d56a67 100644 --- a/contrib/libs/curl/lib/curl_sasl.h +++ b/contrib/libs/curl/lib/curl_sasl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2022, 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 @@ -125,9 +125,9 @@ struct SASL { unsigned short authmechs; /* Accepted authentication mechanisms */ unsigned short prefmech; /* Preferred authentication mechanism */ unsigned short authused; /* Auth mechanism used for the connection */ - BIT(resetprefs); /* For URL auth option parsing. */ - BIT(mutual_auth); /* Mutual authentication enabled (GSSAPI only) */ - BIT(force_ir); /* Protocol always supports initial response */ + bool resetprefs; /* For URL auth option parsing. */ + bool mutual_auth; /* Mutual authentication enabled (GSSAPI only) */ + bool force_ir; /* Protocol always supports initial response */ }; /* This is used to test whether the line starts with the given mechanism */ diff --git a/contrib/libs/curl/lib/curl_setup.h b/contrib/libs/curl/lib/curl_setup.h index ea46ef2eac..5ad6f3b02e 100644 --- a/contrib/libs/curl/lib/curl_setup.h +++ b/contrib/libs/curl/lib/curl_setup.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -92,7 +92,7 @@ # endif #endif -#ifdef macintosh +#if defined(macintosh) && defined(__MRC__) # error #include "config-mac.h" #endif @@ -112,10 +112,6 @@ # error #include "config-plan9.h" #endif -#ifdef MSDOS -# error #include "config-dos.h" -#endif - #endif /* HAVE_CONFIG_H */ /* ================================================================ */ @@ -258,7 +254,7 @@ #if defined(__APPLE__) && !defined(USE_ARES) #include <TargetConditionals.h> #define USE_RESOLVE_ON_IPS 1 -# if !defined(TARGET_OS_OSX) || TARGET_OS_OSX +# if defined(TARGET_OS_OSX) && TARGET_OS_OSX # define CURL_OSX_CALL_COPYPROXIES 1 # endif #endif @@ -298,7 +294,6 @@ # if defined(HAVE_PROTO_BSDSOCKET_H) && \ (!defined(__amigaos4__) || defined(USE_AMISSL)) /* use bsdsocket.library directly, instead of libc networking functions */ -# define _SYS_MBUF_H /* m_len define clashes with curl */ # error #include <proto/bsdsocket.h> # ifdef __amigaos4__ int Curl_amiga_select(int nfds, fd_set *readfds, fd_set *writefds, @@ -323,7 +318,9 @@ #endif #include <stdio.h> +#ifdef HAVE_ASSERT_H #include <assert.h> +#endif #ifdef __TANDEM /* for ns*-tandem-nsk systems */ # if ! defined __LP64 @@ -442,8 +439,8 @@ # endif #endif -#if (SIZEOF_CURL_OFF_T < 8) -#error "too small curl_off_t" +#if (SIZEOF_CURL_OFF_T == 4) +# define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFF) #else /* assume SIZEOF_CURL_OFF_T == 8 */ # define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFFFFFFFFFF) @@ -696,7 +693,7 @@ # define UNUSED_PARAM __attribute__((__unused__)) # define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) #else -# define UNUSED_PARAM /* NOTHING */ +# define UNUSED_PARAM /*NOTHING*/ # define WARN_UNUSED_RESULT #endif @@ -778,6 +775,21 @@ endings either CRLF or LF so 't' is appropriate. #define FOPEN_APPENDTEXT "a" #endif +/* WinSock destroys recv() buffer when send() failed. + * Enabled automatically for Windows and for Cygwin as Cygwin sockets are + * wrappers for WinSock sockets. https://github.com/curl/curl/issues/657 + * Define DONT_USE_RECV_BEFORE_SEND_WORKAROUND to force disable workaround. + */ +#if !defined(DONT_USE_RECV_BEFORE_SEND_WORKAROUND) +# if defined(WIN32) || defined(__CYGWIN__) +# define USE_RECV_BEFORE_SEND_WORKAROUND +# endif +#else /* DONT_USE_RECV_BEFORE_SEND_WORKAROUND */ +# ifdef USE_RECV_BEFORE_SEND_WORKAROUND +# undef USE_RECV_BEFORE_SEND_WORKAROUND +# endif +#endif /* DONT_USE_RECV_BEFORE_SEND_WORKAROUND */ + /* for systems that don't detect this in configure */ #ifndef CURL_SA_FAMILY_T # if defined(HAVE_SA_FAMILY_T) @@ -817,19 +829,11 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, #define USE_HTTP2 #endif -#if (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \ - defined(USE_QUICHE) || defined(USE_MSH3) +#if defined(USE_NGTCP2) || defined(USE_QUICHE) || defined(USE_MSH3) #define ENABLE_QUIC #define USE_HTTP3 #endif -/* Certain Windows implementations are not aligned with what curl expects, - so always use the local one on this platform. E.g. the mingw-w64 - implementation can return wrong results for non-ASCII inputs. */ -#if defined(HAVE_BASENAME) && defined(WIN32) -#undef HAVE_BASENAME -#endif - #if defined(USE_UNIX_SOCKETS) && defined(WIN32) # if defined(__MINGW32__) && !defined(LUP_SECURE) typedef u_short ADDRESS_FAMILY; /* Classic mingw, 11y+ old mingw-w64 */ @@ -847,10 +851,4 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, # endif #endif -/* OpenSSLv3 marks DES, MD5 and ENGINE functions deprecated but we have no - replacements (yet) so tell the compiler to not warn for them. */ -#ifdef USE_OPENSSL -#define OPENSSL_SUPPRESS_DEPRECATED -#endif - #endif /* HEADER_CURL_SETUP_H */ diff --git a/contrib/libs/curl/lib/curl_setup_once.h b/contrib/libs/curl/lib/curl_setup_once.h index c1ed059070..f09b00f9f2 100644 --- a/contrib/libs/curl/lib/curl_setup_once.h +++ b/contrib/libs/curl/lib/curl_setup_once.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -34,7 +34,10 @@ #include <string.h> #include <stdarg.h> #include <time.h> + +#ifdef HAVE_ERRNO_H #include <errno.h> +#endif #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> @@ -69,20 +72,6 @@ #include <unistd.h> #endif -#ifdef USE_WOLFSSL -# if defined(HAVE_STDINT_H) -# include <stdint.h> -# elif defined(HAVE_INTTYPES_H) -# include <inttypes.h> -# endif -#endif - -#ifdef USE_SCHANNEL -/* Must set this before <schannel.h> is included directly or indirectly by - another Windows header. */ -# define SCHANNEL_USE_BLACKLISTS 1 -#endif - #ifdef __hpux # if !defined(_XOPEN_SOURCE_EXTENDED) || defined(_KERNEL) # ifdef _APP32_64BIT_OFF_T @@ -298,7 +287,7 @@ typedef unsigned int bit; */ #undef DEBUGASSERT -#if defined(DEBUGBUILD) +#if defined(DEBUGBUILD) && defined(HAVE_ASSERT_H) #define DEBUGASSERT(x) assert(x) #else #define DEBUGASSERT(x) do { } while(0) diff --git a/contrib/libs/curl/lib/curl_sha256.h b/contrib/libs/curl/lib/curl_sha256.h index 481b849baa..7f296dc8f0 100644 --- a/contrib/libs/curl/lib/curl_sha256.h +++ b/contrib/libs/curl/lib/curl_sha256.h @@ -7,8 +7,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Florin Petriuc, <petriuc.florin@gmail.com> - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2017, Florin Petriuc, <petriuc.florin@gmail.com> + * Copyright (C) 2018 - 2022, 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,7 @@ extern const struct HMAC_params Curl_HMAC_SHA256[1]; #ifdef USE_WOLFSSL /* SHA256_DIGEST_LENGTH is an enum value in wolfSSL. Need to import it from - * sha.h */ + * sha.h*/ #error #include <wolfssl/options.h> #error #include <wolfssl/openssl/sha.h> #else diff --git a/contrib/libs/curl/lib/curl_sspi.c b/contrib/libs/curl/lib/curl_sspi.c index eb21e7e2b0..33108c48e9 100644 --- a/contrib/libs/curl/lib/curl_sspi.c +++ b/contrib/libs/curl/lib/curl_sspi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/curl_sspi.h b/contrib/libs/curl/lib/curl_sspi.h index 9816d59c84..ad111309c5 100644 --- a/contrib/libs/curl/lib/curl_sspi.h +++ b/contrib/libs/curl/lib/curl_sspi.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/curl_threads.c b/contrib/libs/curl/lib/curl_threads.c index e13e2947c2..eb8e136087 100644 --- a/contrib/libs/curl/lib/curl_threads.c +++ b/contrib/libs/curl/lib/curl_threads.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -31,7 +31,9 @@ # include <pthread.h> # endif #elif defined(USE_THREADS_WIN32) -# include <process.h> +# ifdef HAVE_PROCESS_H +# include <process.h> +# endif #endif #include "curl_threads.h" diff --git a/contrib/libs/curl/lib/curl_threads.h b/contrib/libs/curl/lib/curl_threads.h index d8165f7b8d..529181034a 100644 --- a/contrib/libs/curl/lib/curl_threads.h +++ b/contrib/libs/curl/lib/curl_threads.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/curlx.h b/contrib/libs/curl/lib/curlx.h index 7a753d6824..1796afa00a 100644 --- a/contrib/libs/curl/lib/curlx.h +++ b/contrib/libs/curl/lib/curlx.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/dict.c b/contrib/libs/curl/lib/dict.c index 3172b38290..6f7678f5ca 100644 --- a/contrib/libs/curl/lib/dict.c +++ b/contrib/libs/curl/lib/dict.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -98,27 +98,37 @@ const struct Curl_handler Curl_handler_dict = { PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */ }; -#define DYN_DICT_WORD 10000 -static char *unescape_word(const char *input) +static char *unescape_word(const char *inputbuff) { - struct dynbuf out; - const char *ptr; - CURLcode result = CURLE_OK; - Curl_dyn_init(&out, DYN_DICT_WORD); - - /* According to RFC2229 section 2.2, these letters need to be escaped with - \[letter] */ - for(ptr = input; *ptr; ptr++) { - char ch = *ptr; - if((ch <= 32) || (ch == 127) || - (ch == '\'') || (ch == '\"') || (ch == '\\')) - result = Curl_dyn_addn(&out, "\\", 1); - if(!result) - result = Curl_dyn_addn(&out, ptr, 1); - if(result) - return NULL; + char *newp = NULL; + char *dictp; + size_t len; + + CURLcode result = Curl_urldecode(inputbuff, 0, &newp, &len, + REJECT_NADA); + if(!newp || result) + return NULL; + + dictp = malloc(len*2 + 1); /* add one for terminating zero */ + if(dictp) { + char *ptr; + char ch; + int olen = 0; + /* According to RFC2229 section 2.2, these letters need to be escaped with + \[letter] */ + for(ptr = newp; + (ch = *ptr) != 0; + ptr++) { + if((ch <= 32) || (ch == 127) || + (ch == '\'') || (ch == '\"') || (ch == '\\')) { + dictp[olen++] = '\\'; + } + dictp[olen++] = ch; + } + dictp[olen] = 0; } - return Curl_dyn_ptr(&out); + free(newp); + return dictp; } /* sendf() sends formatted data to the server */ @@ -168,25 +178,20 @@ static CURLcode sendf(curl_socket_t sockfd, struct Curl_easy *data, static CURLcode dict_do(struct Curl_easy *data, bool *done) { char *word; - char *eword = NULL; + char *eword; char *ppath; char *database = NULL; char *strategy = NULL; char *nthdef = NULL; /* This is not part of the protocol, but required by RFC 2229 */ - CURLcode result; + CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; - char *path; + char *path = data->state.up.path; *done = TRUE; /* unconditionally */ - /* url-decode path before further evaluation */ - result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL); - if(result) - return result; - if(strncasecompare(path, DICT_MATCH, sizeof(DICT_MATCH)-1) || strncasecompare(path, DICT_MATCH2, sizeof(DICT_MATCH2)-1) || strncasecompare(path, DICT_MATCH3, sizeof(DICT_MATCH3)-1)) { @@ -220,10 +225,8 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) } eword = unescape_word(word); - if(!eword) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } + if(!eword) + return CURLE_OUT_OF_MEMORY; result = sendf(sockfd, data, "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n" @@ -236,9 +239,11 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) strategy, eword); + free(eword); + if(result) { failf(data, "Failed sending DICT request"); - goto error; + return result; } Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); /* no upload */ } @@ -268,10 +273,8 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) } eword = unescape_word(word); - if(!eword) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } + if(!eword) + return CURLE_OUT_OF_MEMORY; result = sendf(sockfd, data, "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n" @@ -282,9 +285,11 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) database, eword); + free(eword); + if(result) { failf(data, "Failed sending DICT request"); - goto error; + return result; } Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); } @@ -305,16 +310,13 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) "QUIT\r\n", ppath); if(result) { failf(data, "Failed sending DICT request"); - goto error; + return result; } Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); } } -error: - free(eword); - free(path); - return result; + return CURLE_OK; } -#endif /* CURL_DISABLE_DICT */ +#endif /*CURL_DISABLE_DICT*/ diff --git a/contrib/libs/curl/lib/dict.h b/contrib/libs/curl/lib/dict.h index ba9a92719b..b283a0dfc1 100644 --- a/contrib/libs/curl/lib/dict.h +++ b/contrib/libs/curl/lib/dict.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/doh.c b/contrib/libs/curl/lib/doh.c index 7a38eab01f..3b1d5d60ef 100644 --- a/contrib/libs/curl/lib/doh.c +++ b/contrib/libs/curl/lib/doh.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2018 - 2022, 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 @@ -347,7 +347,7 @@ static CURLcode dohprobe(struct Curl_easy *data, free(nurl); return CURLE_OK; -error: + error: free(nurl); Curl_close(&doh); return result; @@ -396,7 +396,6 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, goto error; dohp->pending++; -#ifdef ENABLE_IPV6 if((conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { /* create IPv6 DoH request */ result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V6], @@ -406,10 +405,9 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, goto error; dohp->pending++; } -#endif return NULL; -error: + error: curl_slist_free_all(dohp->headers); data->req.doh->headers = NULL; for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) { @@ -952,7 +950,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data, 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, dohp->host, 0, dohp->port); + dns = Curl_cache_addr(data, ai, dohp->host, dohp->port); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); diff --git a/contrib/libs/curl/lib/doh.h b/contrib/libs/curl/lib/doh.h index 7d7b694f33..678e807fe4 100644 --- a/contrib/libs/curl/lib/doh.h +++ b/contrib/libs/curl/lib/doh.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2018 - 2022, 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 diff --git a/contrib/libs/curl/lib/dynbuf.c b/contrib/libs/curl/lib/dynbuf.c index 0c9c491aeb..0b1cf9afd8 100644 --- a/contrib/libs/curl/lib/dynbuf.c +++ b/contrib/libs/curl/lib/dynbuf.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2020 - 2022, 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 @@ -76,7 +76,6 @@ static CURLcode dyn_nappend(struct dynbuf *s, DEBUGASSERT(s->toobig); DEBUGASSERT(indx < s->toobig); DEBUGASSERT(!s->leng || s->bufr); - DEBUGASSERT(a <= s->toobig); if(fit > s->toobig) { Curl_dyn_free(s); @@ -85,9 +84,7 @@ static CURLcode dyn_nappend(struct dynbuf *s, else if(!a) { DEBUGASSERT(!indx); /* first invoke */ - if(MIN_FIRST_ALLOC > s->toobig) - a = s->toobig; - else if(fit < MIN_FIRST_ALLOC) + if(fit < MIN_FIRST_ALLOC) a = MIN_FIRST_ALLOC; else a = fit; @@ -95,9 +92,6 @@ static CURLcode dyn_nappend(struct dynbuf *s, else { while(a < fit) a *= 2; - if(a > s->toobig) - /* no point in allocating a larger buffer than this is allowed to use */ - a = s->toobig; } if(a != s->allc) { @@ -105,7 +99,8 @@ static CURLcode dyn_nappend(struct dynbuf *s, include that as well when it uses this code */ void *p = realloc(s->bufr, a); if(!p) { - Curl_dyn_free(s); + Curl_safefree(s->bufr); + s->leng = s->allc = 0; return CURLE_OUT_OF_MEMORY; } s->bufr = p; diff --git a/contrib/libs/curl/lib/dynbuf.h b/contrib/libs/curl/lib/dynbuf.h index 6291eabd37..04a728c779 100644 --- a/contrib/libs/curl/lib/dynbuf.h +++ b/contrib/libs/curl/lib/dynbuf.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2020 - 2022, 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 @@ -81,6 +81,8 @@ int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save); #define DYN_PAUSE_BUFFER (64 * 1024 * 1024) #define DYN_HAXPROXY 2048 #define DYN_HTTP_REQUEST (1024*1024) +#define DYN_H2_HEADERS (128*1024) +#define DYN_H2_TRAILERS (128*1024) #define DYN_APRINTF 8000000 #define DYN_RTSP_REQ_HEADER (64*1024) #define DYN_TRAILERS (64*1024) diff --git a/contrib/libs/curl/lib/dynhds.c b/contrib/libs/curl/lib/dynhds.c deleted file mode 100644 index 007dfc588c..0000000000 --- a/contrib/libs/curl/lib/dynhds.c +++ /dev/null @@ -1,366 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" -#include "dynhds.h" -#include "strcase.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - - -static struct dynhds_entry * -entry_new(const char *name, size_t namelen, - const char *value, size_t valuelen, int opts) -{ - struct dynhds_entry *e; - char *p; - - DEBUGASSERT(name); - DEBUGASSERT(value); - e = calloc(1, sizeof(*e) + namelen + valuelen + 2); - if(!e) - return NULL; - e->name = p = ((char *)e) + sizeof(*e); - memcpy(p, name, namelen); - e->namelen = namelen; - e->value = p += namelen + 1; /* leave a \0 at the end of name */ - memcpy(p, value, valuelen); - e->valuelen = valuelen; - if(opts & DYNHDS_OPT_LOWERCASE) - Curl_strntolower(e->name, e->name, e->namelen); - return e; -} - -static struct dynhds_entry * -entry_append(struct dynhds_entry *e, - const char *value, size_t valuelen) -{ - struct dynhds_entry *e2; - size_t valuelen2 = e->valuelen + 1 + valuelen; - char *p; - - DEBUGASSERT(value); - e2 = calloc(1, sizeof(*e) + e->namelen + valuelen2 + 2); - if(!e2) - return NULL; - e2->name = p = ((char *)e2) + sizeof(*e2); - memcpy(p, e->name, e->namelen); - e2->namelen = e->namelen; - e2->value = p += e->namelen + 1; /* leave a \0 at the end of name */ - memcpy(p, e->value, e->valuelen); - p += e->valuelen; - p[0] = ' '; - memcpy(p + 1, value, valuelen); - e2->valuelen = valuelen2; - return e2; -} - -static void entry_free(struct dynhds_entry *e) -{ - free(e); -} - -void Curl_dynhds_init(struct dynhds *dynhds, size_t max_entries, - size_t max_strs_size) -{ - DEBUGASSERT(dynhds); - DEBUGASSERT(max_strs_size); - dynhds->hds = NULL; - dynhds->hds_len = dynhds->hds_allc = dynhds->strs_len = 0; - dynhds->max_entries = max_entries; - dynhds->max_strs_size = max_strs_size; - dynhds->opts = 0; -} - -void Curl_dynhds_free(struct dynhds *dynhds) -{ - DEBUGASSERT(dynhds); - if(dynhds->hds && dynhds->hds_len) { - size_t i; - DEBUGASSERT(dynhds->hds); - for(i = 0; i < dynhds->hds_len; ++i) { - entry_free(dynhds->hds[i]); - } - } - Curl_safefree(dynhds->hds); - dynhds->hds_len = dynhds->hds_allc = dynhds->strs_len = 0; -} - -void Curl_dynhds_reset(struct dynhds *dynhds) -{ - DEBUGASSERT(dynhds); - if(dynhds->hds_len) { - size_t i; - DEBUGASSERT(dynhds->hds); - for(i = 0; i < dynhds->hds_len; ++i) { - entry_free(dynhds->hds[i]); - dynhds->hds[i] = NULL; - } - } - dynhds->hds_len = dynhds->strs_len = 0; -} - -size_t Curl_dynhds_count(struct dynhds *dynhds) -{ - return dynhds->hds_len; -} - -void Curl_dynhds_set_opts(struct dynhds *dynhds, int opts) -{ - dynhds->opts = opts; -} - -struct dynhds_entry *Curl_dynhds_getn(struct dynhds *dynhds, size_t n) -{ - DEBUGASSERT(dynhds); - return (n < dynhds->hds_len)? dynhds->hds[n] : NULL; -} - -struct dynhds_entry *Curl_dynhds_get(struct dynhds *dynhds, const char *name, - size_t namelen) -{ - size_t i; - for(i = 0; i < dynhds->hds_len; ++i) { - if(dynhds->hds[i]->namelen == namelen && - strncasecompare(dynhds->hds[i]->name, name, namelen)) { - return dynhds->hds[i]; - } - } - return NULL; -} - -struct dynhds_entry *Curl_dynhds_cget(struct dynhds *dynhds, const char *name) -{ - return Curl_dynhds_get(dynhds, name, strlen(name)); -} - -CURLcode Curl_dynhds_add(struct dynhds *dynhds, - const char *name, size_t namelen, - const char *value, size_t valuelen) -{ - struct dynhds_entry *entry = NULL; - CURLcode result = CURLE_OUT_OF_MEMORY; - - DEBUGASSERT(dynhds); - if(dynhds->max_entries && dynhds->hds_len >= dynhds->max_entries) - return CURLE_OUT_OF_MEMORY; - if(dynhds->strs_len + namelen + valuelen > dynhds->max_strs_size) - return CURLE_OUT_OF_MEMORY; - -entry = entry_new(name, namelen, value, valuelen, dynhds->opts); - if(!entry) - goto out; - - if(dynhds->hds_len + 1 >= dynhds->hds_allc) { - size_t nallc = dynhds->hds_len + 16; - struct dynhds_entry **nhds; - - if(dynhds->max_entries && nallc > dynhds->max_entries) - nallc = dynhds->max_entries; - - nhds = calloc(nallc, sizeof(struct dynhds_entry *)); - if(!nhds) - goto out; - if(dynhds->hds) { - memcpy(nhds, dynhds->hds, - dynhds->hds_len * sizeof(struct dynhds_entry *)); - Curl_safefree(dynhds->hds); - } - dynhds->hds = nhds; - dynhds->hds_allc = nallc; - } - dynhds->hds[dynhds->hds_len++] = entry; - entry = NULL; - dynhds->strs_len += namelen + valuelen; - result = CURLE_OK; - -out: - if(entry) - entry_free(entry); - return result; -} - -CURLcode Curl_dynhds_cadd(struct dynhds *dynhds, - const char *name, const char *value) -{ - return Curl_dynhds_add(dynhds, name, strlen(name), value, strlen(value)); -} - -CURLcode Curl_dynhds_h1_add_line(struct dynhds *dynhds, - const char *line, size_t line_len) -{ - const char *p; - const char *name; - size_t namelen; - const char *value; - size_t valuelen, i; - - if(!line || !line_len) - return CURLE_OK; - - if((line[0] == ' ') || (line[0] == '\t')) { - struct dynhds_entry *e, *e2; - /* header continuation, yikes! */ - if(!dynhds->hds_len) - return CURLE_BAD_FUNCTION_ARGUMENT; - - while(line_len && ISBLANK(line[0])) { - ++line; - --line_len; - } - if(!line_len) - return CURLE_BAD_FUNCTION_ARGUMENT; - e = dynhds->hds[dynhds->hds_len-1]; - e2 = entry_append(e, line, line_len); - if(!e2) - return CURLE_OUT_OF_MEMORY; - dynhds->hds[dynhds->hds_len-1] = e2; - entry_free(e); - return CURLE_OK; - } - else { - p = memchr(line, ':', line_len); - if(!p) - return CURLE_BAD_FUNCTION_ARGUMENT; - name = line; - namelen = p - line; - p++; /* move past the colon */ - for(i = namelen + 1; i < line_len; ++i, ++p) { - if(!ISBLANK(*p)) - break; - } - value = p; - valuelen = line_len - i; - - p = memchr(value, '\r', valuelen); - if(!p) - p = memchr(value, '\n', valuelen); - if(p) - valuelen = (size_t)(p - value); - - return Curl_dynhds_add(dynhds, name, namelen, value, valuelen); - } -} - -CURLcode Curl_dynhds_h1_cadd_line(struct dynhds *dynhds, const char *line) -{ - return Curl_dynhds_h1_add_line(dynhds, line, line? strlen(line) : 0); -} - -#ifdef DEBUGBUILD -/* used by unit2602.c */ - -bool Curl_dynhds_contains(struct dynhds *dynhds, - const char *name, size_t namelen) -{ - return !!Curl_dynhds_get(dynhds, name, namelen); -} - -bool Curl_dynhds_ccontains(struct dynhds *dynhds, const char *name) -{ - return Curl_dynhds_contains(dynhds, name, strlen(name)); -} - -size_t Curl_dynhds_count_name(struct dynhds *dynhds, - const char *name, size_t namelen) -{ - size_t n = 0; - if(dynhds->hds_len) { - size_t i; - for(i = 0; i < dynhds->hds_len; ++i) { - if((namelen == dynhds->hds[i]->namelen) && - strncasecompare(name, dynhds->hds[i]->name, namelen)) - ++n; - } - } - return n; -} - -size_t Curl_dynhds_ccount_name(struct dynhds *dynhds, const char *name) -{ - return Curl_dynhds_count_name(dynhds, name, strlen(name)); -} - -CURLcode Curl_dynhds_set(struct dynhds *dynhds, - const char *name, size_t namelen, - const char *value, size_t valuelen) -{ - Curl_dynhds_remove(dynhds, name, namelen); - return Curl_dynhds_add(dynhds, name, namelen, value, valuelen); -} - -size_t Curl_dynhds_remove(struct dynhds *dynhds, - const char *name, size_t namelen) -{ - size_t n = 0; - if(dynhds->hds_len) { - size_t i, len; - for(i = 0; i < dynhds->hds_len; ++i) { - if((namelen == dynhds->hds[i]->namelen) && - strncasecompare(name, dynhds->hds[i]->name, namelen)) { - ++n; - --dynhds->hds_len; - dynhds->strs_len -= (dynhds->hds[i]->namelen + - dynhds->hds[i]->valuelen); - entry_free(dynhds->hds[i]); - len = dynhds->hds_len - i; /* remaining entries */ - if(len) { - memmove(&dynhds->hds[i], &dynhds->hds[i + 1], - len * sizeof(dynhds->hds[i])); - } - --i; /* do this index again */ - } - } - } - return n; -} - -size_t Curl_dynhds_cremove(struct dynhds *dynhds, const char *name) -{ - return Curl_dynhds_remove(dynhds, name, strlen(name)); -} - -CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf) -{ - CURLcode result = CURLE_OK; - size_t i; - - if(!dynhds->hds_len) - return result; - - for(i = 0; i < dynhds->hds_len; ++i) { - result = Curl_dyn_addf(dbuf, "%.*s: %.*s\r\n", - (int)dynhds->hds[i]->namelen, dynhds->hds[i]->name, - (int)dynhds->hds[i]->valuelen, dynhds->hds[i]->value); - if(result) - break; - } - - return result; -} - -#endif diff --git a/contrib/libs/curl/lib/dynhds.h b/contrib/libs/curl/lib/dynhds.h deleted file mode 100644 index 8a053480e9..0000000000 --- a/contrib/libs/curl/lib/dynhds.h +++ /dev/null @@ -1,174 +0,0 @@ -#ifndef HEADER_CURL_DYNHDS_H -#define HEADER_CURL_DYNHDS_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ -#include "curl_setup.h" - -#include <curl/curl.h> -#include "dynbuf.h" - -struct dynbuf; - -/** - * A single header entry. - * `name` and `value` are non-NULL and always NUL terminated. - */ -struct dynhds_entry { - char *name; - char *value; - size_t namelen; - size_t valuelen; -}; - -struct dynhds { - struct dynhds_entry **hds; - size_t hds_len; /* number of entries in hds */ - size_t hds_allc; /* size of hds allocation */ - size_t max_entries; /* size limit number of entries */ - size_t strs_len; /* length of all strings */ - size_t max_strs_size; /* max length of all strings */ - int opts; -}; - -#define DYNHDS_OPT_NONE (0) -#define DYNHDS_OPT_LOWERCASE (1 << 0) - -/** - * Init for use on first time or after a reset. - * Allow `max_entries` headers to be added, 0 for unlimited. - * Allow size of all name and values added to not exceed `max_strs_size`` - */ -void Curl_dynhds_init(struct dynhds *dynhds, size_t max_entries, - size_t max_strs_size); -/** - * Frees all data held in `dynhds`, but not the struct itself. - */ -void Curl_dynhds_free(struct dynhds *dynhds); - -/** - * Reset `dyndns` to the initial init state. May keep allocations - * around. - */ -void Curl_dynhds_reset(struct dynhds *dynhds); - -/** - * Return the number of header entries. - */ -size_t Curl_dynhds_count(struct dynhds *dynhds); - -/** - * Set the options to use, replacing any existing ones. - * This will not have an effect on already existing headers. - */ -void Curl_dynhds_set_opts(struct dynhds *dynhds, int opts); - -/** - * Return the n-th header entry or NULL if it does not exist. - */ -struct dynhds_entry *Curl_dynhds_getn(struct dynhds *dynhds, size_t n); - -/** - * Return the 1st header entry of the name or NULL if none exists. - */ -struct dynhds_entry *Curl_dynhds_get(struct dynhds *dynhds, - const char *name, size_t namelen); -struct dynhds_entry *Curl_dynhds_cget(struct dynhds *dynhds, const char *name); - -/** - * Return TRUE iff one or more headers with the given name exist. - */ -bool Curl_dynhds_contains(struct dynhds *dynhds, - const char *name, size_t namelen); -bool Curl_dynhds_ccontains(struct dynhds *dynhds, const char *name); - -/** - * Return how often the given name appears in `dynhds`. - * Names are case-insensitive. - */ -size_t Curl_dynhds_count_name(struct dynhds *dynhds, - const char *name, size_t namelen); - -/** - * Return how often the given 0-terminated name appears in `dynhds`. - * Names are case-insensitive. - */ -size_t Curl_dynhds_ccount_name(struct dynhds *dynhds, const char *name); - -/** - * Add a header, name + value, to `dynhds` at the end. Does *not* - * check for duplicate names. - */ -CURLcode Curl_dynhds_add(struct dynhds *dynhds, - const char *name, size_t namelen, - const char *value, size_t valuelen); - -/** - * Add a header, c-string name + value, to `dynhds` at the end. - */ -CURLcode Curl_dynhds_cadd(struct dynhds *dynhds, - const char *name, const char *value); - -/** - * Remove all entries with the given name. - * Returns number of entries removed. - */ -size_t Curl_dynhds_remove(struct dynhds *dynhds, - const char *name, size_t namelen); -size_t Curl_dynhds_cremove(struct dynhds *dynhds, const char *name); - - -/** - * Set the give header name and value, replacing any entries with - * the same name. The header is added at the end of all (remaining) - * entries. - */ -CURLcode Curl_dynhds_set(struct dynhds *dynhds, - const char *name, size_t namelen, - const char *value, size_t valuelen); - -CURLcode Curl_dynhds_cset(struct dynhds *dynhds, - const char *name, const char *value); - -/** - * Add a single header from a HTTP/1.1 formatted line at the end. Line - * may contain a delimiting \r\n or just \n. Any characters after - * that will be ignored. - */ -CURLcode Curl_dynhds_h1_cadd_line(struct dynhds *dynhds, const char *line); - -/** - * Add a single header from a HTTP/1.1 formatted line at the end. Line - * may contain a delimiting \r\n or just \n. Any characters after - * that will be ignored. - */ -CURLcode Curl_dynhds_h1_add_line(struct dynhds *dynhds, - const char *line, size_t line_len); - -/** - * Add the headers to the given `dynbuf` in HTTP/1.1 format with - * cr+lf line endings. Will NOT output a last empty line. - */ -CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf); - -#endif /* HEADER_CURL_DYNHDS_H */ diff --git a/contrib/libs/curl/lib/easy.c b/contrib/libs/curl/lib/easy.c index d034629b57..b8ac1ef8a8 100644 --- a/contrib/libs/curl/lib/easy.c +++ b/contrib/libs/curl/lib/easy.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,6 +24,14 @@ #include "curl_setup.h" +/* + * See comment in curl_memory.h for the explanation of this sanity check. + */ + +#ifdef CURLX_NO_MEMORY_CALLBACKS +#error "libcurl shall not ever be built with CURLX_NO_MEMORY_CALLBACKS defined" +#endif + #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif @@ -57,14 +65,13 @@ #include "easyif.h" #include "multiif.h" #include "select.h" -#include "cfilters.h" #include "sendf.h" /* for failf function prototype */ #include "connect.h" /* for Curl_getconnectinfo */ #include "slist.h" #include "mime.h" #include "amigaos.h" -#include "macos.h" #include "warnless.h" +#include "multiif.h" #include "sigpipe.h" #include "vssh/ssh.h" #include "setopt.h" @@ -84,7 +91,7 @@ /* true globals -- for curl_global_init() and curl_global_cleanup() */ static unsigned int initialized; -static long easy_init_flags; +static long init_flags; #ifdef GLOBAL_INIT_IS_THREADSAFE @@ -107,7 +114,7 @@ static curl_simple_lock s_lock = CURL_SIMPLE_LOCK_INIT; #if defined(_WIN32_WCE) #define system_strdup _strdup #elif !defined(HAVE_STRDUP) -#define system_strdup Curl_strdup +#define system_strdup curlx_strdup #else #define system_strdup strdup #endif @@ -158,11 +165,6 @@ static CURLcode global_init(long flags, bool memoryfuncs) #endif } - if(Curl_log_init()) { - DEBUGF(fprintf(stderr, "Error: Curl_log_init failed\n")); - goto fail; - } - if(!Curl_ssl_init()) { DEBUGF(fprintf(stderr, "Error: Curl_ssl_init failed\n")); goto fail; @@ -182,11 +184,6 @@ static CURLcode global_init(long flags, bool memoryfuncs) } #endif - if(Curl_macos_init()) { - DEBUGF(fprintf(stderr, "Error: Curl_macos_init failed\n")); - goto fail; - } - if(Curl_resolver_global_init()) { DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n")); goto fail; @@ -205,7 +202,7 @@ static CURLcode global_init(long flags, bool memoryfuncs) } #endif - easy_init_flags = flags; + init_flags = flags; #ifdef DEBUGBUILD if(getenv("CURL_GLOBAL_INIT")) @@ -215,7 +212,7 @@ static CURLcode global_init(long flags, bool memoryfuncs) return CURLE_OK; -fail: + fail: initialized--; /* undo the increase */ return CURLE_FAILED_INIT; } @@ -280,7 +277,7 @@ CURLcode curl_global_init_mem(long flags, curl_malloc_callback m, /** * curl_global_cleanup() globally cleanups curl, uses the value of - * "easy_init_flags" to determine what needs to be cleaned up and what doesn't. + * "init_flags" to determine what needs to be cleaned up and what doesn't. */ void curl_global_cleanup(void) { @@ -300,7 +297,7 @@ void curl_global_cleanup(void) Curl_resolver_global_cleanup(); #ifdef WIN32 - Curl_win32_cleanup(easy_init_flags); + Curl_win32_cleanup(init_flags); #endif Curl_amiga_cleanup(); @@ -314,7 +311,7 @@ void curl_global_cleanup(void) free(leakpointer); #endif - easy_init_flags = 0; + init_flags = 0; global_init_unlock(); } @@ -793,12 +790,14 @@ CURLcode curl_easy_perform_ev(struct Curl_easy *data) */ void curl_easy_cleanup(struct Curl_easy *data) { - if(GOOD_EASY_HANDLE(data)) { - SIGPIPE_VARIABLE(pipe_st); - sigpipe_ignore(data, &pipe_st); - Curl_close(&data); - sigpipe_restore(&pipe_st); - } + SIGPIPE_VARIABLE(pipe_st); + + if(!data) + return; + + sigpipe_ignore(data, &pipe_st); + Curl_close(&data); + sigpipe_restore(&pipe_st); } /* @@ -830,7 +829,7 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) /* Copy src->set into dst->set first, then deal with the strings afterwards */ dst->set = src->set; - Curl_mime_initpart(&dst->set.mimepost); + Curl_mime_initpart(&dst->set.mimepost, dst); /* clear all string pointers first */ memset(dst->set.str, 0, STRING_LAST * sizeof(char *)); @@ -864,7 +863,7 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) } /* Duplicate mime data. */ - result = Curl_mime_duppart(dst, &dst->set.mimepost, &src->set.mimepost); + result = Curl_mime_duppart(&dst->set.mimepost, &src->set.mimepost); if(src->set.resolve) dst->state.resolve = dst->set.resolve; @@ -899,8 +898,6 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) /* the connection cache is setup on demand */ outcurl->state.conn_cache = NULL; outcurl->state.lastconnect_id = -1; - outcurl->state.recent_conn_id = -1; - outcurl->id = -1; outcurl->progress.flags = data->progress.flags; outcurl->progress.callback = data->progress.callback; @@ -917,9 +914,11 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) goto fail; } - if(data->set.cookielist) { - outcurl->set.cookielist = Curl_slist_duplicate(data->set.cookielist); - if(!outcurl->set.cookielist) + /* duplicate all values in 'change' */ + if(data->state.cookielist) { + outcurl->state.cookielist = + Curl_slist_duplicate(data->state.cookielist); + if(!outcurl->state.cookielist) goto fail; } #endif @@ -1001,12 +1000,12 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) return outcurl; -fail: + fail: if(outcurl) { #ifndef CURL_DISABLE_COOKIES - curl_slist_free_all(outcurl->set.cookielist); - outcurl->set.cookielist = NULL; + curl_slist_free_all(outcurl->state.cookielist); + outcurl->state.cookielist = NULL; #endif Curl_safefree(outcurl->state.buffer); Curl_dyn_free(&outcurl->state.headerb); @@ -1103,7 +1102,7 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action) k->keepon = newstate; if(!(newstate & KEEP_RECV_PAUSE)) { - Curl_conn_ev_data_pause(data, FALSE); + Curl_http2_stream_pause(data, FALSE); if(data->state.tempcount) { /* there are buffers for sending that can be delivered as the receive @@ -1226,28 +1225,9 @@ CURLcode curl_easy_recv(struct Curl_easy *data, void *buffer, size_t buflen, return result; *n = (size_t)n1; - return CURLE_OK; -} - -#ifdef USE_WEBSOCKETS -CURLcode Curl_connect_only_attach(struct Curl_easy *data) -{ - curl_socket_t sfd; - CURLcode result; - struct connectdata *c = NULL; - - result = easy_connection(data, &sfd, &c); - if(result) - return result; - - if(!data->conn) - /* on first invoke, the transfer has been detached from the connection and - needs to be reattached */ - Curl_attach_connection(data, c); return CURLE_OK; } -#endif /* USE_WEBSOCKETS */ /* * Sends data over the connected socket. @@ -1315,34 +1295,29 @@ static int conn_upkeep(struct Curl_easy *data, struct connectdata *conn, void *param) { - struct curltime *now = param; - - if(Curl_timediff(*now, conn->keepalive) <= data->set.upkeep_interval_ms) - return 0; + /* Param is unused. */ + (void)param; - /* briefly attach for action */ - Curl_attach_connection(data, conn); if(conn->handler->connection_check) { + /* briefly attach the connection to this transfer for the purpose of + checking it */ + Curl_attach_connection(data, conn); + /* Do a protocol-specific keepalive check on the connection. */ conn->handler->connection_check(data, conn, CONNCHECK_KEEPALIVE); + /* detach the connection again */ + Curl_detach_connection(data); } - else { - /* Do the generic action on the FIRSTSOCKE filter chain */ - Curl_conn_keep_alive(data, conn, FIRSTSOCKET); - } - Curl_detach_connection(data); - conn->keepalive = *now; return 0; /* continue iteration */ } static CURLcode upkeep(struct conncache *conn_cache, void *data) { - struct curltime now = Curl_now(); /* Loop over every connection and make connection alive. */ Curl_conncache_foreach(data, conn_cache, - &now, + data, conn_upkeep); return CURLE_OK; } diff --git a/contrib/libs/curl/lib/easy_lock.h b/contrib/libs/curl/lib/easy_lock.h index 6399a39bde..d96e56b8d8 100644 --- a/contrib/libs/curl/lib/easy_lock.h +++ b/contrib/libs/curl/lib/easy_lock.h @@ -1,5 +1,3 @@ -#ifndef HEADER_CURL_EASY_LOCK_H -#define HEADER_CURL_EASY_LOCK_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -105,5 +103,3 @@ static inline void curl_simple_lock_unlock(curl_simple_lock *lock) #undef GLOBAL_INIT_IS_THREADSAFE #endif - -#endif /* HEADER_CURL_EASY_LOCK_H */ diff --git a/contrib/libs/curl/lib/easygetopt.c b/contrib/libs/curl/lib/easygetopt.c index 2b8a521cd2..a639bb3758 100644 --- a/contrib/libs/curl/lib/easygetopt.c +++ b/contrib/libs/curl/lib/easygetopt.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * ___|___/|_| ______| * - * Copyright (C) Daniel Stenberg, <daniel.se>, et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel.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 diff --git a/contrib/libs/curl/lib/easyif.h b/contrib/libs/curl/lib/easyif.h index 6448952966..205382cd07 100644 --- a/contrib/libs/curl/lib/easyif.h +++ b/contrib/libs/curl/lib/easyif.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -30,10 +30,6 @@ CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer, size_t buflen, ssize_t *n); -#ifdef USE_WEBSOCKETS -CURLcode Curl_connect_only_attach(struct Curl_easy *data); -#endif - #ifdef CURLDEBUG CURL_EXTERN CURLcode curl_easy_perform_ev(struct Curl_easy *easy); #endif diff --git a/contrib/libs/curl/lib/easyoptions.c b/contrib/libs/curl/lib/easyoptions.c index e69c658b0c..e59b63af7a 100644 --- a/contrib/libs/curl/lib/easyoptions.c +++ b/contrib/libs/curl/lib/easyoptions.c @@ -1,11 +1,11 @@ /*************************************************************************** * _ _ ____ _ - * Project ___| | | | _ \| | + * Project ___| | | | _ | | * / __| | | | |_) | | * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| + * ___|___/|_| ______| * - * Copyright (C) Daniel Stenberg, <daniel.se>, et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel.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 @@ -42,7 +42,6 @@ struct curl_easyoption Curl_easyopts[] = { {"CAINFO", CURLOPT_CAINFO, CURLOT_STRING, 0}, {"CAINFO_BLOB", CURLOPT_CAINFO_BLOB, CURLOT_BLOB, 0}, {"CAPATH", CURLOPT_CAPATH, CURLOT_STRING, 0}, - {"CA_CACHE_TIMEOUT", CURLOPT_CA_CACHE_TIMEOUT, CURLOT_LONG, 0}, {"CERTINFO", CURLOPT_CERTINFO, CURLOT_LONG, 0}, {"CHUNK_BGN_FUNCTION", CURLOPT_CHUNK_BGN_FUNCTION, CURLOT_FUNCTION, 0}, {"CHUNK_DATA", CURLOPT_CHUNK_DATA, CURLOT_CBPTR, 0}, @@ -120,7 +119,6 @@ struct curl_easyoption Curl_easyopts[] = { {"HAPPY_EYEBALLS_TIMEOUT_MS", CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS, CURLOT_LONG, 0}, {"HAPROXYPROTOCOL", CURLOPT_HAPROXYPROTOCOL, CURLOT_LONG, 0}, - {"HAPROXY_CLIENT_IP", CURLOPT_HAPROXY_CLIENT_IP, CURLOT_STRING, 0}, {"HEADER", CURLOPT_HEADER, CURLOT_LONG, 0}, {"HEADERDATA", CURLOPT_HEADERDATA, CURLOT_CBPTR, 0}, {"HEADERFUNCTION", CURLOPT_HEADERFUNCTION, CURLOT_FUNCTION, 0}, @@ -165,9 +163,7 @@ struct curl_easyoption Curl_easyopts[] = { {"MAIL_AUTH", CURLOPT_MAIL_AUTH, CURLOT_STRING, 0}, {"MAIL_FROM", CURLOPT_MAIL_FROM, CURLOT_STRING, 0}, {"MAIL_RCPT", CURLOPT_MAIL_RCPT, CURLOT_SLIST, 0}, - {"MAIL_RCPT_ALLLOWFAILS", CURLOPT_MAIL_RCPT_ALLOWFAILS, - CURLOT_LONG, CURLOT_FLAG_ALIAS}, - {"MAIL_RCPT_ALLOWFAILS", CURLOPT_MAIL_RCPT_ALLOWFAILS, CURLOT_LONG, 0}, + {"MAIL_RCPT_ALLLOWFAILS", CURLOPT_MAIL_RCPT_ALLLOWFAILS, CURLOT_LONG, 0}, {"MAXAGE_CONN", CURLOPT_MAXAGE_CONN, CURLOT_LONG, 0}, {"MAXCONNECTS", CURLOPT_MAXCONNECTS, CURLOT_LONG, 0}, {"MAXFILESIZE", CURLOPT_MAXFILESIZE, CURLOT_LONG, 0}, @@ -245,7 +241,6 @@ struct curl_easyoption Curl_easyopts[] = { CURLOT_STRING, 0}, {"PROXY_TRANSFER_MODE", CURLOPT_PROXY_TRANSFER_MODE, CURLOT_LONG, 0}, {"PUT", CURLOPT_PUT, CURLOT_LONG, 0}, - {"QUICK_EXIT", CURLOPT_QUICK_EXIT, CURLOT_LONG, 0}, {"QUOTE", CURLOPT_QUOTE, CURLOT_SLIST, 0}, {"RANDOM_FILE", CURLOPT_RANDOM_FILE, CURLOT_STRING, 0}, {"RANGE", CURLOPT_RANGE, CURLOT_STRING, 0}, @@ -373,6 +368,6 @@ struct curl_easyoption Curl_easyopts[] = { */ int Curl_easyopts_check(void) { - return ((CURLOPT_LASTENTRY%10000) != (323 + 1)); + return ((CURLOPT_LASTENTRY%10000) != (320 + 1)); } #endif diff --git a/contrib/libs/curl/lib/easyoptions.h b/contrib/libs/curl/lib/easyoptions.h index 24b4cd93ed..33f816d6b6 100644 --- a/contrib/libs/curl/lib/easyoptions.h +++ b/contrib/libs/curl/lib/easyoptions.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/escape.c b/contrib/libs/curl/lib/escape.c index 56aa2b3988..0fa5f8817e 100644 --- a/contrib/libs/curl/lib/escape.c +++ b/contrib/libs/curl/lib/escape.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -97,7 +97,7 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string, return strdup(""); while(length--) { - unsigned char in = *string++; /* treat the characters unsigned */ + unsigned char in = *string; /* we need to treat the characters unsigned */ if(Curl_isunreserved(in)) { /* append this */ @@ -106,28 +106,15 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string, } else { /* encode it */ - const char hex[] = "0123456789ABCDEF"; - char out[3]={'%'}; - out[1] = hex[in>>4]; - out[2] = hex[in & 0xf]; - if(Curl_dyn_addn(&d, out, 3)) + if(Curl_dyn_addf(&d, "%%%02X", in)) return NULL; } + string++; } return Curl_dyn_ptr(&d); } -static const unsigned char hextable[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */ - 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */ - 0, 10, 11, 12, 13, 14, 15 /* 0x60 - 0x66 */ -}; - -/* the input is a single hex digit */ -#define onehex2dec(x) hextable[x - '0'] - /* * Curl_urldecode() URL decodes the given string. * @@ -150,47 +137,54 @@ CURLcode Curl_urldecode(const char *string, size_t length, { size_t alloc; char *ns; + size_t strindex = 0; + unsigned long hex; DEBUGASSERT(string); DEBUGASSERT(ctrl >= REJECT_NADA); /* crash on TRUE/FALSE */ - alloc = (length?length:strlen(string)); - ns = malloc(alloc + 1); + alloc = (length?length:strlen(string)) + 1; + ns = malloc(alloc); if(!ns) return CURLE_OUT_OF_MEMORY; - /* store output string */ - *ostring = ns; - - while(alloc) { + while(--alloc > 0) { unsigned char in = *string; if(('%' == in) && (alloc > 2) && ISXDIGIT(string[1]) && ISXDIGIT(string[2])) { /* this is two hexadecimal digits following a '%' */ - in = (unsigned char)(onehex2dec(string[1]) << 4) | onehex2dec(string[2]); + char hexstr[3]; + char *ptr; + hexstr[0] = string[1]; + hexstr[1] = string[2]; + hexstr[2] = 0; - string += 3; - alloc -= 3; - } - else { - string++; - alloc--; + hex = strtoul(hexstr, &ptr, 16); + + in = curlx_ultouc(hex); /* this long is never bigger than 255 anyway */ + + string += 2; + alloc -= 2; } if(((ctrl == REJECT_CTRL) && (in < 0x20)) || ((ctrl == REJECT_ZERO) && (in == 0))) { - Curl_safefree(*ostring); + free(ns); return CURLE_URL_MALFORMAT; } - *ns++ = in; + ns[strindex++] = in; + string++; } - *ns = 0; /* terminate it */ + ns[strindex] = 0; /* terminate it */ if(olen) /* store output size */ - *olen = ns - *ostring; + *olen = strindex; + + /* store output string */ + *ostring = ns; return CURLE_OK; } @@ -208,7 +202,7 @@ char *curl_easy_unescape(struct Curl_easy *data, const char *string, char *str = NULL; (void)data; if(length >= 0) { - size_t inputlen = (size_t)length; + size_t inputlen = length; size_t outputlen; CURLcode res = Curl_urldecode(string, inputlen, &str, &outputlen, REJECT_NADA); @@ -233,3 +227,29 @@ void curl_free(void *p) { free(p); } + +/* + * Curl_hexencode() + * + * Converts binary input to lowercase hex-encoded ASCII output. + * Null-terminated. + */ +void Curl_hexencode(const unsigned char *src, size_t len, /* input length */ + unsigned char *out, size_t olen) /* output buffer size */ +{ + const char *hex = "0123456789abcdef"; + DEBUGASSERT(src && len && (olen >= 3)); + if(src && len && (olen >= 3)) { + while(len-- && (olen >= 3)) { + /* clang-tidy warns on this line without this comment: */ + /* NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult) */ + *out++ = hex[(*src & 0xF0)>>4]; + *out++ = hex[*src & 0x0F]; + ++src; + olen -= 2; + } + *out = 0; + } + else if(olen) + *out = 0; +} diff --git a/contrib/libs/curl/lib/escape.h b/contrib/libs/curl/lib/escape.h index cdbb712acc..170253704d 100644 --- a/contrib/libs/curl/lib/escape.h +++ b/contrib/libs/curl/lib/escape.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -38,4 +38,7 @@ CURLcode Curl_urldecode(const char *string, size_t length, char **ostring, size_t *olen, enum urlreject ctrl); +void Curl_hexencode(const unsigned char *src, size_t len, /* input length */ + unsigned char *out, size_t olen); /* output buffer size */ + #endif /* HEADER_CURL_ESCAPE_H */ diff --git a/contrib/libs/curl/lib/file.c b/contrib/libs/curl/lib/file.c index c751e8861a..d82d57b463 100644 --- a/contrib/libs/curl/lib/file.c +++ b/contrib/libs/curl/lib/file.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -150,19 +150,9 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done) char *actual_path; #endif size_t real_path_len; - CURLcode result; - - if(file->path) { - /* already connected. - * the handler->connect_it() is normally only called once, but - * FILE does a special check on setting up the connection which - * calls this explicitly. */ - *done = TRUE; - return CURLE_OK; - } - result = Curl_urldecode(data->state.up.path, 0, &real_path, - &real_path_len, REJECT_ZERO); + CURLcode result = Curl_urldecode(data->state.up.path, 0, &real_path, + &real_path_len, REJECT_ZERO); if(result) return result; @@ -236,11 +226,10 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done) file->path = real_path; #endif #endif - Curl_safefree(file->freepath); file->freepath = real_path; /* free this when done */ file->fd = fd; - if(!data->state.upload && (fd == -1)) { + if(!data->set.upload && (fd == -1)) { failf(data, "Couldn't open file %s", data->state.up.path); file_done(data, CURLE_FILE_COULDNT_READ_FILE, FALSE); return CURLE_FILE_COULDNT_READ_FILE; @@ -340,7 +329,7 @@ static CURLcode file_upload(struct Curl_easy *data) while(!result) { size_t nread; - ssize_t nwrite; + size_t nwrite; size_t readcount; result = Curl_fillreadbuffer(data, data->set.buffer_size, &readcount); if(result) @@ -351,7 +340,7 @@ static CURLcode file_upload(struct Curl_easy *data) nread = readcount; - /* skip bytes before resume point */ + /*skip bytes before resume point*/ if(data->state.resume_from) { if((curl_off_t)nread <= data->state.resume_from) { data->state.resume_from -= nread; @@ -369,7 +358,7 @@ static CURLcode file_upload(struct Curl_easy *data) /* write the data to the target */ nwrite = write(fd, buf2, nread); - if((size_t)nwrite != nread) { + if(nwrite != nread) { result = CURLE_SEND_ERROR; break; } @@ -422,7 +411,7 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) Curl_pgrsStartNow(data); - if(data->state.upload) + if(data->set.upload) return file_upload(data); file = data->req.p.file; @@ -482,13 +471,13 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) tm->tm_hour, tm->tm_min, tm->tm_sec, - data->req.no_body ? "": "\r\n"); + data->set.opt_no_body ? "": "\r\n"); result = Curl_client_write(data, CLIENTWRITE_HEADER, header, headerlen); if(result) return result; /* set the file size to make it available post transfer */ Curl_pgrsSetDownloadSize(data, expected_size); - if(data->req.no_body) + if(data->set.opt_no_body) return result; } diff --git a/contrib/libs/curl/lib/file.h b/contrib/libs/curl/lib/file.h index 4565525592..826d45380d 100644 --- a/contrib/libs/curl/lib/file.h +++ b/contrib/libs/curl/lib/file.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/fileinfo.c b/contrib/libs/curl/lib/fileinfo.c index 2be3b3239b..7bbf24bdeb 100644 --- a/contrib/libs/curl/lib/fileinfo.c +++ b/contrib/libs/curl/lib/fileinfo.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2010 - 2022, 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 @@ -40,7 +40,7 @@ void Curl_fileinfo_cleanup(struct fileinfo *finfo) if(!finfo) return; - Curl_dyn_free(&finfo->buf); + Curl_safefree(finfo->info.b_data); free(finfo); } #endif diff --git a/contrib/libs/curl/lib/fileinfo.h b/contrib/libs/curl/lib/fileinfo.h index ce009da06d..5bad718cc8 100644 --- a/contrib/libs/curl/lib/fileinfo.h +++ b/contrib/libs/curl/lib/fileinfo.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2010 - 2022, 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 @@ -26,12 +26,10 @@ #include <curl/curl.h> #include "llist.h" -#include "dynbuf.h" struct fileinfo { struct curl_fileinfo info; struct Curl_llist_element list; - struct dynbuf buf; }; struct fileinfo *Curl_fileinfo_alloc(void); diff --git a/contrib/libs/curl/lib/fopen.c b/contrib/libs/curl/lib/fopen.c index b6e3caddde..ad3691ba9d 100644 --- a/contrib/libs/curl/lib/fopen.c +++ b/contrib/libs/curl/lib/fopen.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -56,13 +56,13 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, int fd = -1; *tempname = NULL; - *fh = fopen(filename, FOPEN_WRITETEXT); - if(!*fh) + if(stat(filename, &sb) == -1 || !S_ISREG(sb.st_mode)) { + /* a non-regular file, fallback to direct fopen() */ + *fh = fopen(filename, FOPEN_WRITETEXT); + if(*fh) + return CURLE_OK; goto fail; - if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode)) - return CURLE_OK; - fclose(*fh); - *fh = NULL; + } result = Curl_rand_hex(data, randsuffix, sizeof(randsuffix)); if(result) @@ -85,7 +85,7 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, if((fstat(fd, &nsb) != -1) && (nsb.st_uid == sb.st_uid) && (nsb.st_gid == sb.st_gid)) { /* if the user and group are the same, clone the original mode */ - if(fchmod(fd, (mode_t)sb.st_mode) == -1) + if(fchmod(fd, sb.st_mode) == -1) goto fail; } } @@ -106,6 +106,7 @@ fail: free(tempstore); + *tempname = NULL; return result; } diff --git a/contrib/libs/curl/lib/fopen.h b/contrib/libs/curl/lib/fopen.h index e3a919d073..289e55f2af 100644 --- a/contrib/libs/curl/lib/fopen.h +++ b/contrib/libs/curl/lib/fopen.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/formdata.c b/contrib/libs/curl/lib/formdata.c index 2bdb9f26ec..46542b4329 100644 --- a/contrib/libs/curl/lib/formdata.c +++ b/contrib/libs/curl/lib/formdata.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -59,7 +59,7 @@ * * AddHttpPost() * - * Adds an HttpPost structure to the list, if parent_post is given becomes + * Adds a HttpPost structure to the list, if parent_post is given becomes * a subpost of parent_post instead of a direct list element. * * Returns newly allocated HttpPost on success and NULL if malloc failed. @@ -135,13 +135,15 @@ static struct FormInfo *AddFormInfo(char *value, { struct FormInfo *form_info; form_info = calloc(1, sizeof(struct FormInfo)); - if(!form_info) + if(form_info) { + if(value) + form_info->value = value; + if(contenttype) + form_info->contenttype = contenttype; + form_info->flags = HTTPPOST_FILENAME; + } + else return NULL; - if(value) - form_info->value = value; - if(contenttype) - form_info->contenttype = contenttype; - form_info->flags = HTTPPOST_FILENAME; if(parent_form_info) { /* now, point our 'more' to the original 'more' */ @@ -197,7 +199,7 @@ static struct FormInfo *AddFormInfo(char *value, * CURL_FORMADD_MEMORY if the allocation of a FormInfo struct failed * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used * CURL_FORMADD_INCOMPLETE if the some FormInfo is not complete (or error) - * CURL_FORMADD_MEMORY if an HttpPost struct cannot be allocated + * CURL_FORMADD_MEMORY if a HttpPost struct cannot be allocated * CURL_FORMADD_MEMORY if some allocation for string copying failed. * CURL_FORMADD_ILLEGAL_ARRAY if an illegal option is used in an array * @@ -715,10 +717,10 @@ int curl_formget(struct curl_httppost *form, void *arg, CURLcode result; curl_mimepart toppart; - Curl_mime_initpart(&toppart); /* default form is empty */ + Curl_mime_initpart(&toppart, NULL); /* default form is empty */ result = Curl_getformdata(NULL, &toppart, form, NULL); if(!result) - result = Curl_mime_prepare_headers(NULL, &toppart, "multipart/form-data", + result = Curl_mime_prepare_headers(&toppart, "multipart/form-data", NULL, MIMESTRATEGY_FORM); while(!result) { diff --git a/contrib/libs/curl/lib/formdata.h b/contrib/libs/curl/lib/formdata.h index caabb6324c..c6c6397cd2 100644 --- a/contrib/libs/curl/lib/formdata.h +++ b/contrib/libs/curl/lib/formdata.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/ftp.c b/contrib/libs/curl/lib/ftp.c index c04daa8c72..f1a25b23dc 100644 --- a/contrib/libs/curl/lib/ftp.c +++ b/contrib/libs/curl/lib/ftp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -43,6 +43,11 @@ #include <inet.h> #endif +#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) +#undef in_addr_t +#define in_addr_t unsigned long +#endif + #include <curl/curl.h> #include "urldata.h" #include "sendf.h" @@ -60,8 +65,6 @@ #include "strtoofft.h" #include "strcase.h" #include "vtls/vtls.h" -#include "cfilters.h" -#include "cf-socket.h" #include "connect.h" #include "strerror.h" #include "inet_ntop.h" @@ -71,6 +74,7 @@ #include "sockaddr.h" /* required for Curl_sockaddr_storage */ #include "multiif.h" #include "url.h" +#include "strcase.h" #include "speedcheck.h" #include "warnless.h" #include "http_proxy.h" @@ -93,14 +97,14 @@ /* Local API functions */ #ifndef DEBUGBUILD -static void _ftp_state(struct Curl_easy *data, - ftpstate newstate); -#define ftp_state(x,y) _ftp_state(x,y) +static void _state(struct Curl_easy *data, + ftpstate newstate); +#define state(x,y) _state(x,y) #else -static void _ftp_state(struct Curl_easy *data, - ftpstate newstate, - int lineno); -#define ftp_state(x,y) _ftp_state(x,y,__LINE__) +static void _state(struct Curl_easy *data, + ftpstate newstate, + int lineno); +#define state(x,y) _state(x,y,__LINE__) #endif static CURLcode ftp_sendquote(struct Curl_easy *data, @@ -215,8 +219,14 @@ const struct Curl_handler Curl_handler_ftps = { static void close_secondarysocket(struct Curl_easy *data, struct connectdata *conn) { - Curl_conn_close(data, SECONDARYSOCKET); - Curl_conn_cf_discard_all(data, conn, SECONDARYSOCKET); + if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) { + Curl_closesocket(data, conn, conn->sock[SECONDARYSOCKET]); + conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; + } + conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE; +#ifndef CURL_DISABLE_PROXY + conn->bits.proxy_ssl_connected[SECONDARYSOCKET] = FALSE; +#endif } /* @@ -268,13 +278,13 @@ static CURLcode AcceptServerConnect(struct Curl_easy *data) struct sockaddr_in add; #endif curl_socklen_t size = (curl_socklen_t) sizeof(add); - CURLcode result; if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) { size = sizeof(add); s = accept(sock, (struct sockaddr *) &add, &size); } + Curl_closesocket(data, conn, sock); /* close the first socket */ if(CURL_SOCKET_BAD == s) { failf(data, "Error accept()ing server connect"); @@ -285,11 +295,9 @@ static CURLcode AcceptServerConnect(struct Curl_easy *data) not needing DO_MORE anymore */ conn->bits.do_more = FALSE; + conn->sock[SECONDARYSOCKET] = s; (void)curlx_nonblock(s, TRUE); /* enable non-blocking */ - /* Replace any filter on SECONDARY with one listening on this socket */ - result = Curl_conn_tcp_accepted_set(data, conn, SECONDARYSOCKET, &s); - if(result) - return result; + conn->bits.sock_accepted = TRUE; if(data->set.fsockopt) { int error = 0; @@ -433,18 +441,15 @@ static CURLcode InitiateTransfer(struct Curl_easy *data) { CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; - bool connected; - DEBUGF(infof(data, "ftp InitiateTransfer()")); - if(conn->bits.ftp_use_data_ssl && data->set.ftp_use_port && - !Curl_conn_is_ssl(conn, SECONDARYSOCKET)) { - result = Curl_ssl_cfilter_add(data, conn, SECONDARYSOCKET); + if(conn->bits.ftp_use_data_ssl) { + /* since we only have a plaintext TCP connection here, we must now + * do the TLS stuff */ + infof(data, "Doing the SSL/TLS handshake on the data stream"); + result = Curl_ssl_connect(data, conn, SECONDARYSOCKET); if(result) return result; } - result = Curl_conn_connect(data, SECONDARYSOCKET, TRUE, &connected); - if(result || !connected) - return result; if(conn->proto.ftpc.state_saved == FTP_STOR) { /* When we know we're uploading a specified file, we can get the file @@ -463,7 +468,7 @@ static CURLcode InitiateTransfer(struct Curl_easy *data) } conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */ - ftp_state(data, FTP_STOP); + state(data, FTP_STOP); return CURLE_OK; } @@ -492,23 +497,22 @@ static CURLcode AllowServerConnect(struct Curl_easy *data, bool *connected) if(timeout_ms < 0) { /* if a timeout was already reached, bail out */ failf(data, "Accept timeout occurred while waiting server connect"); - result = CURLE_FTP_ACCEPT_TIMEOUT; - goto out; + return CURLE_FTP_ACCEPT_TIMEOUT; } /* see if the connection request is already here */ result = ReceivedServerConnect(data, connected); if(result) - goto out; + return result; if(*connected) { result = AcceptServerConnect(data); if(result) - goto out; + return result; result = InitiateTransfer(data); if(result) - goto out; + return result; } else { /* Add timeout to multi handle and break out of the loop */ @@ -517,8 +521,6 @@ static CURLcode AllowServerConnect(struct Curl_easy *data, bool *connected) EXPIRE_FTP_ACCEPT); } -out: - DEBUGF(infof(data, "ftp AllowServerConnect() -> %d", result)); return result; } @@ -591,7 +593,7 @@ static CURLcode ftp_readresp(struct Curl_easy *data, * generically is a good idea. */ infof(data, "We got a 421 - timeout"); - ftp_state(data, FTP_STOP); + state(data, FTP_STOP); return CURLE_OPERATION_TIMEDOUT; } @@ -670,7 +672,7 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data, * wait for more data anyway. */ } - else if(!Curl_conn_data_pending(data, FIRSTSOCKET)) { + else if(!Curl_conn_data_pending(conn, FIRSTSOCKET)) { switch(SOCKET_READABLE(sockfd, interval_ms)) { case -1: /* select() error, stop reading */ failf(data, "FTP response aborted due to select/poll error: %d", @@ -750,10 +752,10 @@ static const char * const ftp_state_names[]={ #endif /* This is the ONLY way to change FTP state! */ -static void _ftp_state(struct Curl_easy *data, - ftpstate newstate +static void _state(struct Curl_easy *data, + ftpstate newstate #ifdef DEBUGBUILD - , int lineno + , int lineno #endif ) { @@ -784,7 +786,7 @@ static CURLcode ftp_state_user(struct Curl_easy *data, if(!result) { struct ftp_conn *ftpc = &conn->proto.ftpc; ftpc->ftp_trying_alternative = FALSE; - ftp_state(data, FTP_USER); + state(data, FTP_USER); } return result; } @@ -794,7 +796,7 @@ static CURLcode ftp_state_pwd(struct Curl_easy *data, { CURLcode result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PWD"); if(!result) - ftp_state(data, FTP_PWD); + state(data, FTP_PWD); return result; } @@ -819,18 +821,31 @@ static int ftp_domore_getsock(struct Curl_easy *data, * handle ordinary commands. */ - DEBUGF(infof(data, "ftp_domore_getsock()")); - if(conn->cfilter[SECONDARYSOCKET] - && !Curl_conn_is_connected(conn, SECONDARYSOCKET)) - return Curl_conn_get_select_socks(data, SECONDARYSOCKET, socks); + if(SOCKS_STATE(conn->cnnct.state)) + return Curl_SOCKS_getsock(conn, socks, SECONDARYSOCKET); if(FTP_STOP == ftpc->state) { int bits = GETSOCK_READSOCK(0); + bool any = FALSE; /* if stopped and still in this state, then we're also waiting for a connect on the secondary connection */ socks[0] = conn->sock[FIRSTSOCKET]; - if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) { + + if(!data->set.ftp_use_port) { + int s; + int i; + /* PORT is used to tell the server to connect to us, and during that we + don't do happy eyeballs, but we do if we connect to the server */ + for(s = 1, i = 0; i<2; i++) { + if(conn->tempsock[i] != CURL_SOCKET_BAD) { + socks[s] = conn->tempsock[i]; + bits |= GETSOCK_WRITESOCK(s++); + any = TRUE; + } + } + } + if(!any) { socks[1] = conn->sock[SECONDARYSOCKET]; bits |= GETSOCK_WRITESOCK(1) | GETSOCK_READSOCK(1); } @@ -872,7 +887,7 @@ static CURLcode ftp_state_cwd(struct Curl_easy *data, for all upcoming ones in the ftp->dirs[] array */ result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", ftpc->entrypath); if(!result) - ftp_state(data, FTP_CWD); + state(data, FTP_CWD); } else { if(ftpc->dirdepth) { @@ -882,7 +897,7 @@ static CURLcode ftp_state_cwd(struct Curl_easy *data, result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", ftpc->dirs[ftpc->cwdcount -1]); if(!result) - ftp_state(data, FTP_CWD); + state(data, FTP_CWD); } else { /* No CWD necessary */ @@ -902,7 +917,7 @@ typedef enum { static CURLcode ftp_state_use_port(struct Curl_easy *data, ftpport fcmd) /* start with this */ { - CURLcode result = CURLE_FTP_PORT_FAILED; + CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; struct ftp_conn *ftpc = &conn->proto.ftpc; curl_socket_t portsock = CURL_SOCKET_BAD; @@ -951,10 +966,8 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, char *port_sep = NULL; addr = calloc(addrlen + 1, 1); - if(!addr) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } + if(!addr) + return CURLE_OUT_OF_MEMORY; #ifdef ENABLE_IPV6 if(*string_ftpport == '[') { @@ -1014,11 +1027,12 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(port_min > port_max) port_min = port_max = 0; + if(*addr != '\0') { /* attempt to get the address of the given interface name */ - switch(Curl_if2ip(conn->remote_addr->family, + switch(Curl_if2ip(conn->ip_addr->ai_family, #ifdef ENABLE_IPV6 - Curl_ipv6_scope(&conn->remote_addr->sa_addr), + Curl_ipv6_scope(conn->ip_addr->ai_addr), conn->scope_id, #endif addr, hbuf, sizeof(hbuf))) { @@ -1027,7 +1041,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, host = addr; break; case IF2IP_AF_NOT_SUPPORTED: - goto out; + return CURLE_FTP_PORT_FAILED; case IF2IP_FOUND: host = hbuf; /* use the hbuf for host name */ } @@ -1045,7 +1059,8 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) { failf(data, "getsockname() failed: %s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - goto out; + free(addr); + return CURLE_FTP_PORT_FAILED; } switch(sa->sa_family) { #ifdef ENABLE_IPV6 @@ -1057,9 +1072,8 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, r = Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf)); break; } - if(!r) { - goto out; - } + if(!r) + return CURLE_FTP_PORT_FAILED; host = hbuf; /* use this host name */ possibly_non_local = FALSE; /* we know it is local now */ } @@ -1079,15 +1093,20 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(!res) { failf(data, "failed to resolve the address provided to PORT: %s", host); - goto out; + free(addr); + return CURLE_FTP_PORT_FAILED; } + free(addr); host = NULL; /* step 2, create a socket for the requested address */ + + portsock = CURL_SOCKET_BAD; error = 0; for(ai = res; ai; ai = ai->ai_next) { - if(Curl_socket_open(data, ai, NULL, conn->transport, &portsock)) { + result = Curl_socket(data, ai, NULL, &portsock); + if(result) { error = SOCKERRNO; continue; } @@ -1096,9 +1115,8 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(!ai) { failf(data, "socket failure: %s", Curl_strerror(error, buffer, sizeof(buffer))); - goto out; + return CURLE_FTP_PORT_FAILED; } - DEBUGF(infof(data, "ftp_state_use_port(), opened socket")); /* step 3, bind to a suitable local address */ @@ -1127,7 +1145,8 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) { failf(data, "getsockname() failed: %s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - goto out; + Curl_closesocket(data, conn, portsock); + return CURLE_FTP_PORT_FAILED; } port = port_min; possibly_non_local = FALSE; /* don't try this again */ @@ -1136,7 +1155,8 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(error != EADDRINUSE && error != EACCES) { failf(data, "bind(port=%hu) failed: %s", port, Curl_strerror(error, buffer, sizeof(buffer))); - goto out; + Curl_closesocket(data, conn, portsock); + return CURLE_FTP_PORT_FAILED; } } else @@ -1145,30 +1165,31 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, port++; } - /* maybe all ports were in use already */ + /* maybe all ports were in use already*/ if(port > port_max) { failf(data, "bind() failed, we ran out of ports"); - goto out; + Curl_closesocket(data, conn, portsock); + return CURLE_FTP_PORT_FAILED; } /* get the name again after the bind() so that we can extract the port number it uses now */ sslen = sizeof(ss); - if(getsockname(portsock, sa, &sslen)) { + if(getsockname(portsock, (struct sockaddr *)sa, &sslen)) { failf(data, "getsockname() failed: %s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - goto out; + Curl_closesocket(data, conn, portsock); + return CURLE_FTP_PORT_FAILED; } - DEBUGF(infof(data, "ftp_state_use_port(), socket bound to port %d", port)); /* step 4, listen on the socket */ if(listen(portsock, 1)) { failf(data, "socket failure: %s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - goto out; + Curl_closesocket(data, conn, portsock); + return CURLE_FTP_PORT_FAILED; } - DEBUGF(infof(data, "ftp_state_use_port(), listening on %d", port)); /* step 5, send the proper FTP command */ @@ -1221,7 +1242,12 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(result) { failf(data, "Failure sending EPRT command: %s", curl_easy_strerror(result)); - goto out; + Curl_closesocket(data, conn, portsock); + /* don't retry using PORT */ + ftpc->count1 = PORT; + /* bail out */ + state(data, FTP_STOP); + return result; } break; } @@ -1247,7 +1273,10 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(result) { failf(data, "Failure sending PORT command: %s", curl_easy_strerror(result)); - goto out; + Curl_closesocket(data, conn, portsock); + /* bail out */ + state(data, FTP_STOP); + return result; } break; } @@ -1256,20 +1285,23 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, /* store which command was sent */ ftpc->count1 = fcmd; - /* Replace any filter on SECONDARY with one listening on this socket */ - result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock); - if(result) - goto out; - portsock = CURL_SOCKET_BAD; /* now held in filter */ - ftp_state(data, FTP_PORT); + close_secondarysocket(data, conn); -out: - if(result) { - ftp_state(data, FTP_STOP); - } - if(portsock != CURL_SOCKET_BAD) - Curl_socket_close(data, conn, portsock); - free(addr); + /* we set the secondary socket variable to this for now, it is only so that + the cleanup function will close it in case we fail before the true + secondary stuff is made */ + conn->sock[SECONDARYSOCKET] = portsock; + + /* this tcpconnect assignment below is a hackish work-around to make the + multi interface with active FTP work - as it will not wait for a + (passive) connect in Curl_is_connected(). + + The *proper* fix is to make sure that the active connection from the + server is done in a non-blocking way. Currently, it is still BLOCKING. + */ + conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE; + + state(data, FTP_PORT); return result; } @@ -1307,7 +1339,7 @@ static CURLcode ftp_state_use_pasv(struct Curl_easy *data, result = Curl_pp_sendf(data, &ftpc->pp, "%s", mode[modeoff]); if(!result) { ftpc->count1 = modeoff; - ftp_state(data, FTP_PASV); + state(data, FTP_PASV); infof(data, "Connect data stream passively"); } return result; @@ -1330,7 +1362,7 @@ static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data) /* doesn't transfer any data */ /* still possibly do PRE QUOTE jobs */ - ftp_state(data, FTP_RETR_PREQUOTE); + state(data, FTP_RETR_PREQUOTE); result = ftp_state_quote(data, TRUE, FTP_RETR_PREQUOTE); } else if(data->set.ftp_use_port) { @@ -1348,14 +1380,14 @@ static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data) data->set.str[STRING_CUSTOMREQUEST]? data->set.str[STRING_CUSTOMREQUEST]: (data->state.list_only?"NLST":"LIST")); - else if(data->state.upload) + else if(data->set.upload) result = Curl_pp_sendf(data, &ftpc->pp, "PRET STOR %s", conn->proto.ftpc.file); else result = Curl_pp_sendf(data, &ftpc->pp, "PRET RETR %s", conn->proto.ftpc.file); if(!result) - ftp_state(data, FTP_PRET); + state(data, FTP_PRET); } else result = ftp_state_use_pasv(data, conn); @@ -1377,7 +1409,7 @@ static CURLcode ftp_state_rest(struct Curl_easy *data, whether it supports range */ result = Curl_pp_sendf(data, &ftpc->pp, "REST %d", 0); if(!result) - ftp_state(data, FTP_REST); + state(data, FTP_REST); } else result = ftp_state_prepare_transfer(data); @@ -1398,7 +1430,7 @@ static CURLcode ftp_state_size(struct Curl_easy *data, /* we know ftpc->file is a valid pointer to a file name */ result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file); if(!result) - ftp_state(data, FTP_SIZE); + state(data, FTP_SIZE); } else result = ftp_state_rest(data, conn); @@ -1466,7 +1498,7 @@ static CURLcode ftp_state_list(struct Curl_easy *data) free(cmd); if(!result) - ftp_state(data, FTP_LIST); + state(data, FTP_LIST); return result; } @@ -1493,7 +1525,7 @@ static CURLcode ftp_state_type(struct Curl_easy *data) /* If we have selected NOBODY and HEADER, it means that we only want file information. Which in FTP can't be much more than the file size and date. */ - if(data->req.no_body && ftpc->file && + if(data->set.opt_no_body && ftpc->file && ftp_need_type(conn, data->state.prefer_ascii)) { /* The SIZE command is _not_ RFC 959 specified, and therefore many servers may not support it! It is however the only way we have to get a file's @@ -1530,7 +1562,7 @@ static CURLcode ftp_state_mdtm(struct Curl_easy *data) result = Curl_pp_sendf(data, &ftpc->pp, "MDTM %s", ftpc->file); if(!result) - ftp_state(data, FTP_MDTM); + state(data, FTP_MDTM); } else result = ftp_state_type(data); @@ -1569,7 +1601,7 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data, /* Got no given size to start from, figure it out */ result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file); if(!result) - ftp_state(data, FTP_STOR_SIZE); + state(data, FTP_STOR_SIZE); return result; } @@ -1624,7 +1656,7 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data, * ftp_done() because we didn't transfer anything! */ ftp->transfer = PPTRANSFER_NONE; - ftp_state(data, FTP_STOP); + state(data, FTP_STOP); return CURLE_OK; } } @@ -1634,7 +1666,7 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data, result = Curl_pp_sendf(data, &ftpc->pp, append?"APPE %s":"STOR %s", ftpc->file); if(!result) - ftp_state(data, FTP_STOR); + state(data, FTP_STOR); return result; } @@ -1695,7 +1727,7 @@ static CURLcode ftp_state_quote(struct Curl_easy *data, result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd); if(result) return result; - ftp_state(data, instate); + state(data, instate); quote = TRUE; } } @@ -1709,7 +1741,7 @@ static CURLcode ftp_state_quote(struct Curl_easy *data, break; case FTP_RETR_PREQUOTE: if(ftp->transfer != PPTRANSFER_BODY) - ftp_state(data, FTP_STOP); + state(data, FTP_STOP); else { if(ftpc->known_filesize != -1) { Curl_pgrsSetDownloadSize(data, ftpc->known_filesize); @@ -1731,12 +1763,12 @@ static CURLcode ftp_state_quote(struct Curl_easy *data, */ result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); if(!result) - ftp_state(data, FTP_RETR); + state(data, FTP_RETR); } else { result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file); if(!result) - ftp_state(data, FTP_RETR_SIZE); + state(data, FTP_RETR_SIZE); } } } @@ -1772,15 +1804,13 @@ static CURLcode ftp_epsv_disable(struct Curl_easy *data, infof(data, "Failed EPSV attempt. Disabling EPSV"); /* disable it for next transfer */ conn->bits.ftp_use_epsv = FALSE; - Curl_conn_close(data, SECONDARYSOCKET); - Curl_conn_cf_discard_all(data, conn, SECONDARYSOCKET); data->state.errorbuf = FALSE; /* allow error message to get rewritten */ result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PASV"); if(!result) { conn->proto.ftpc.count1++; /* remain in/go to the FTP_PASV state */ - ftp_state(data, FTP_PASV); + state(data, FTP_PASV); } return result; } @@ -1799,29 +1829,6 @@ static char *control_address(struct connectdata *conn) return conn->primary_ip; } -static bool match_pasv_6nums(const char *p, - unsigned int *array) /* 6 numbers */ -{ - int i; - for(i = 0; i < 6; i++) { - unsigned long num; - char *endp; - if(i) { - if(*p != ',') - return FALSE; - p++; - } - if(!ISDIGIT(*p)) - return FALSE; - num = strtoul(p, &endp, 10); - if(num > 255) - return FALSE; - array[i] = (unsigned int)num; - p = endp; - } - return TRUE; -} - static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, int ftpcode) { @@ -1841,18 +1848,27 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, /* positive EPSV response */ char *ptr = strchr(str, '('); if(ptr) { - char sep; + unsigned int num; + char separator[4]; ptr++; - /* |||12345| */ - sep = ptr[0]; - /* the ISDIGIT() check here is because strtoul() accepts leading minus - etc */ - if((ptr[1] == sep) && (ptr[2] == sep) && ISDIGIT(ptr[3])) { - char *endp; - unsigned long num = strtoul(&ptr[3], &endp, 10); - if(*endp != sep) - ptr = NULL; - else if(num > 0xffff) { + if(5 == sscanf(ptr, "%c%c%c%u%c", + &separator[0], + &separator[1], + &separator[2], + &num, + &separator[3])) { + const char sep1 = separator[0]; + int i; + + /* The four separators should be identical, or else this is an oddly + formatted reply and we bail out immediately. */ + for(i = 1; i<4; i++) { + if(separator[i] != sep1) { + ptr = NULL; /* set to NULL to signal error */ + break; + } + } + if(num > 0xffff) { failf(data, "Illegal port number in EPSV reply"); return CURLE_FTP_WEIRD_PASV_REPLY; } @@ -1874,7 +1890,8 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, else if((ftpc->count1 == 1) && (ftpcode == 227)) { /* positive PASV response */ - unsigned int ip[6]; + unsigned int ip[4] = {0, 0, 0, 0}; + unsigned int port[2] = {0, 0}; /* * Scan for a sequence of six comma-separated numbers and use them as @@ -1886,12 +1903,15 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, * "227 Entering passive mode. 127,0,0,1,4,51" */ while(*str) { - if(match_pasv_6nums(str, ip)) + if(6 == sscanf(str, "%u,%u,%u,%u,%u,%u", + &ip[0], &ip[1], &ip[2], &ip[3], + &port[0], &port[1])) break; str++; } - if(!*str) { + if(!*str || (ip[0] > 255) || (ip[1] > 255) || (ip[2] > 255) || + (ip[3] > 255) || (port[0] > 255) || (port[1] > 255) ) { failf(data, "Couldn't interpret the 227-response"); return CURLE_FTP_WEIRD_227_FORMAT; } @@ -1911,7 +1931,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, if(!ftpc->newhost) return CURLE_OUT_OF_MEMORY; - ftpc->newport = (unsigned short)(((ip[4]<<8) + ip[5]) & 0xffff); + ftpc->newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff); } else if(ftpc->count1 == 0) { /* EPSV failed, move on to PASV */ @@ -1931,7 +1951,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, */ const char * const host_name = conn->bits.socksproxy ? conn->socks_proxy.host.name : conn->http_proxy.host.name; - rc = Curl_resolv(data, host_name, conn->port, FALSE, &addr); + rc = Curl_resolv(data, host_name, (int)conn->port, FALSE, &addr); if(rc == CURLRESOLV_PENDING) /* BLOCKING, ignores the return code but 'addr' will be NULL in case of failure */ @@ -1953,7 +1973,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, /* postponed address resolution in case of tcp fastopen */ if(conn->bits.tcp_fastopen && !conn->bits.reuse && !ftpc->newhost[0]) { - Curl_conn_ev_update_info(data, conn); + Curl_conninfo_remote(data, conn, conn->sock[FIRSTSOCKET]); Curl_safefree(ftpc->newhost); ftpc->newhost = strdup(control_address(conn)); if(!ftpc->newhost) @@ -1973,9 +1993,8 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, } } - result = Curl_conn_setup(data, conn, SECONDARYSOCKET, addr, - conn->bits.ftp_use_data_ssl? - CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE); + conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE; + result = Curl_connecthost(data, conn, addr); if(result) { Curl_resolv_unlock(data, addr); /* we're done using this address */ @@ -2005,7 +2024,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; conn->bits.do_more = TRUE; - ftp_state(data, FTP_STOP); /* this phase is completed */ + state(data, FTP_STOP); /* this phase is completed */ return result; } @@ -2039,37 +2058,13 @@ static CURLcode ftp_state_port_resp(struct Curl_easy *data, } else { infof(data, "Connect data stream actively"); - ftp_state(data, FTP_STOP); /* end of DO phase */ + state(data, FTP_STOP); /* end of DO phase */ result = ftp_dophase_done(data, FALSE); } return result; } -static int twodigit(const char *p) -{ - return (p[0]-'0') * 10 + (p[1]-'0'); -} - -static bool ftp_213_date(const char *p, int *year, int *month, int *day, - int *hour, int *minute, int *second) -{ - size_t len = strlen(p); - if(len < 14) - return FALSE; - *year = twodigit(&p[0]) * 100 + twodigit(&p[2]); - *month = twodigit(&p[4]); - *day = twodigit(&p[6]); - *hour = twodigit(&p[8]); - *minute = twodigit(&p[10]); - *second = twodigit(&p[12]); - - if((*month > 12) || (*day > 31) || (*hour > 23) || (*minute > 59) || - (*second > 60)) - return FALSE; - return TRUE; -} - static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data, int ftpcode) { @@ -2084,8 +2079,8 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data, /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the last .sss part is optional and means fractions of a second */ int year, month, day, hour, minute, second; - if(ftp_213_date(&data->state.buffer[4], - &year, &month, &day, &hour, &minute, &second)) { + if(6 == sscanf(&data->state.buffer[4], "%04d%02d%02d%02d%02d%02d", + &year, &month, &day, &hour, &minute, &second)) { /* we have a time, reformat it */ char timebuf[24]; msnprintf(timebuf, sizeof(timebuf), @@ -2097,9 +2092,9 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data, #ifdef CURL_FTP_HTTPSTYLE_HEAD /* If we asked for a time of the file and we actually got one as well, - we "emulate" an HTTP-style header in our output. */ + we "emulate" a HTTP-style header in our output. */ - if(data->req.no_body && + if(data->set.opt_no_body && ftpc->file && data->set.get_filetime && (data->info.filetime >= 0) ) { @@ -2151,7 +2146,7 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data, infof(data, "The requested document is not new enough"); ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */ data->info.timecond = TRUE; - ftp_state(data, FTP_STOP); + state(data, FTP_STOP); return CURLE_OK; } break; @@ -2160,7 +2155,7 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data, infof(data, "The requested document is not old enough"); ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */ data->info.timecond = TRUE; - ftp_state(data, FTP_STOP); + state(data, FTP_STOP); return CURLE_OK; } break; @@ -2215,7 +2210,6 @@ static CURLcode ftp_state_retr(struct Curl_easy *data, struct connectdata *conn = data->conn; struct ftp_conn *ftpc = &conn->proto.ftpc; - DEBUGF(infof(data, "ftp_state_retr()")); if(data->set.max_filesize && (filesize > data->set.max_filesize)) { failf(data, "Maximum file size exceeded"); return CURLE_FILESIZE_EXCEEDED; @@ -2268,7 +2262,7 @@ static CURLcode ftp_state_retr(struct Curl_easy *data, /* Set ->transfer so that we won't get any error in ftp_done() * because we didn't transfer the any file */ ftp->transfer = PPTRANSFER_NONE; - ftp_state(data, FTP_STOP); + state(data, FTP_STOP); return CURLE_OK; } @@ -2279,13 +2273,13 @@ static CURLcode ftp_state_retr(struct Curl_easy *data, result = Curl_pp_sendf(data, &ftpc->pp, "REST %" CURL_FORMAT_CURL_OFF_T, data->state.resume_from); if(!result) - ftp_state(data, FTP_RETR_REST); + state(data, FTP_RETR_REST); } else { /* no resume */ result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); if(!result) - ftp_state(data, FTP_RETR); + state(data, FTP_RETR); } return result; @@ -2316,7 +2310,7 @@ static CURLcode ftp_state_size_resp(struct Curl_easy *data, else fdigit = start; /* ignores parsing errors, which will make the size remain unknown */ - (void)curlx_strtoofft(fdigit, NULL, 10, &filesize); + (void)curlx_strtoofft(fdigit, NULL, 0, &filesize); } else if(ftpcode == 550) { /* "No such file or directory" */ @@ -2385,7 +2379,7 @@ static CURLcode ftp_state_rest_resp(struct Curl_easy *data, else { result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); if(!result) - ftp_state(data, FTP_RETR); + state(data, FTP_RETR); } break; } @@ -2401,7 +2395,7 @@ static CURLcode ftp_state_stor_resp(struct Curl_easy *data, if(ftpcode >= 400) { failf(data, "Failed FTP upload: %0d", ftpcode); - ftp_state(data, FTP_STOP); + state(data, FTP_STOP); /* oops, we never close the sockets! */ return CURLE_UPLOAD_FAILED; } @@ -2412,7 +2406,7 @@ static CURLcode ftp_state_stor_resp(struct Curl_easy *data, if(data->set.ftp_use_port) { bool connected; - ftp_state(data, FTP_STOP); /* no longer in STOR state */ + state(data, FTP_STOP); /* no longer in STOR state */ result = AllowServerConnect(data, &connected); if(result) @@ -2471,7 +2465,6 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data, if((instate != FTP_LIST) && !data->state.prefer_ascii && - !data->set.ignorecl && (ftp->downloadsize < 1)) { /* * It seems directory listings either don't show the size or very @@ -2502,7 +2495,7 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data, if(bytes) { ++bytes; /* get the number! */ - (void)curlx_strtoofft(bytes, NULL, 10, &size); + (void)curlx_strtoofft(bytes, NULL, 0, &size); } } } @@ -2535,7 +2528,7 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data, if(!connected) { struct ftp_conn *ftpc = &conn->proto.ftpc; infof(data, "Data conn was not available immediately"); - ftp_state(data, FTP_STOP); + state(data, FTP_STOP); ftpc->wait_data_conn = TRUE; } } @@ -2546,7 +2539,7 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data, if((instate == FTP_LIST) && (ftpcode == 450)) { /* simply no matching files in the dir listing */ ftp->transfer = PPTRANSFER_NONE; /* don't download anything */ - ftp_state(data, FTP_STOP); /* this phase is over */ + state(data, FTP_STOP); /* this phase is over */ } else { failf(data, "RETR response: %03d", ftpcode); @@ -2582,7 +2575,7 @@ static CURLcode ftp_state_loggedin(struct Curl_easy *data) */ result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "PBSZ %d", 0); if(!result) - ftp_state(data, FTP_PBSZ); + state(data, FTP_PBSZ); } else { result = ftp_state_pwd(data, conn); @@ -2592,11 +2585,13 @@ static CURLcode ftp_state_loggedin(struct Curl_easy *data) /* for USER and PASS responses */ static CURLcode ftp_state_user_resp(struct Curl_easy *data, - int ftpcode) + int ftpcode, + ftpstate instate) { CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; struct ftp_conn *ftpc = &conn->proto.ftpc; + (void)instate; /* no use for this yet */ /* some need password anyway, and others just return 2xx ignored */ if((ftpcode == 331) && (ftpc->state == FTP_USER)) { @@ -2605,7 +2600,7 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data, result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s", conn->passwd?conn->passwd:""); if(!result) - ftp_state(data, FTP_PASS); + state(data, FTP_PASS); } else if(ftpcode/100 == 2) { /* 230 User ... logged in. @@ -2617,7 +2612,7 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data, result = Curl_pp_sendf(data, &ftpc->pp, "ACCT %s", data->set.str[STRING_FTP_ACCOUNT]); if(!result) - ftp_state(data, FTP_ACCT); + state(data, FTP_ACCT); } else { failf(data, "ACCT requested but none available"); @@ -2638,7 +2633,7 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data, data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]); if(!result) { ftpc->ftp_trying_alternative = TRUE; - ftp_state(data, FTP_USER); + state(data, FTP_USER); } } else { @@ -2673,7 +2668,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, int ftpcode; struct ftp_conn *ftpc = &conn->proto.ftpc; struct pingpong *pp = &ftpc->pp; - static const char * const ftpauth[] = { "SSL", "TLS" }; + static const char ftpauth[][4] = { "SSL", "TLS" }; size_t nread = 0; if(pp->sendleft) @@ -2691,7 +2686,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, /* 230 User logged in - already! Take as 220 if TLS required. */ if(data->set.use_ssl <= CURLUSESSL_TRY || conn->bits.ftp_use_control_ssl) - return ftp_state_user_resp(data, ftpcode); + return ftp_state_user_resp(data, ftpcode, ftpc->state); } else if(ftpcode != 220) { failf(data, "Got a %03d ftp-server response when 220 was expected", @@ -2741,7 +2736,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]); if(!result) - ftp_state(data, FTP_AUTH); + state(data, FTP_AUTH); } else result = ftp_state_user(data, conn); @@ -2761,16 +2756,8 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, */ if((ftpcode == 234) || (ftpcode == 334)) { - /* this was BLOCKING, keep it so for now */ - bool done; - if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); - if(result) { - /* we failed and bail out */ - return CURLE_USE_SSL_FAILED; - } - } - result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, &done); + /* Curl_ssl_connect is BLOCKING */ + result = Curl_ssl_connect(data, conn, FIRSTSOCKET); if(!result) { conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */ conn->bits.ftp_use_control_ssl = TRUE; /* SSL on control */ @@ -2796,7 +2783,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, case FTP_USER: case FTP_PASS: - result = ftp_state_user_resp(data, ftpcode); + result = ftp_state_user_resp(data, ftpcode, ftpc->state); break; case FTP_ACCT: @@ -2808,7 +2795,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, Curl_pp_sendf(data, &ftpc->pp, "PROT %c", data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P'); if(!result) - ftp_state(data, FTP_PROT); + state(data, FTP_PROT); break; case FTP_PROT: @@ -2827,7 +2814,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, */ result = Curl_pp_sendf(data, &ftpc->pp, "%s", "CCC"); if(!result) - ftp_state(data, FTP_CCC); + state(data, FTP_CCC); } else result = ftp_state_pwd(data, conn); @@ -2836,7 +2823,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, case FTP_CCC: if(ftpcode < 500) { /* First shut down the SSL layer (note: this call will block) */ - result = Curl_ssl_cfilter_remove(data, FIRSTSOCKET); + result = Curl_ssl_shutdown(data, conn, FIRSTSOCKET); if(result) failf(data, "Failed to clear the command channel (CCC)"); @@ -2919,7 +2906,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, infof(data, "Entry path is '%s'", ftpc->entrypath); /* also save it where getinfo can access it: */ data->state.most_recent_ftp_entrypath = ftpc->entrypath; - ftp_state(data, FTP_SYST); + state(data, FTP_SYST); break; } @@ -2935,7 +2922,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, infof(data, "Failed to figure out path"); } } - ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */ + state(data, FTP_STOP); /* we are done with the CONNECT phase! */ DEBUGF(infof(data, "protocol connect phase DONE")); break; @@ -2970,7 +2957,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, /* remember target server OS */ Curl_safefree(ftpc->server_os); ftpc->server_os = os; - ftp_state(data, FTP_NAMEFMT); + state(data, FTP_NAMEFMT); break; } /* Nothing special for the target server. */ @@ -2982,7 +2969,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, /* Cannot identify server OS. Continue anyway and cross fingers. */ } - ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */ + state(data, FTP_STOP); /* we are done with the CONNECT phase! */ DEBUGF(infof(data, "protocol connect phase DONE")); break; @@ -2993,7 +2980,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, break; } - ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */ + state(data, FTP_STOP); /* we are done with the CONNECT phase! */ DEBUGF(infof(data, "protocol connect phase DONE")); break; @@ -3026,7 +3013,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, result = Curl_pp_sendf(data, &ftpc->pp, "MKD %s", ftpc->dirs[ftpc->cwdcount - 1]); if(!result) - ftp_state(data, FTP_MKD); + state(data, FTP_MKD); } else { /* return failure */ @@ -3055,7 +3042,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, result = CURLE_REMOTE_ACCESS_DENIED; } else { - ftp_state(data, FTP_CWD); + state(data, FTP_CWD); /* send CWD */ result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", ftpc->dirs[ftpc->cwdcount - 1]); @@ -3114,7 +3101,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, /* fallthrough, just stop! */ default: /* internal error */ - ftp_state(data, FTP_STOP); + state(data, FTP_STOP); break; } } /* if(ftpcode) */ @@ -3180,7 +3167,7 @@ static CURLcode ftp_connect(struct Curl_easy *data, if(conn->handler->flags & PROTOPT_SSL) { /* BLOCKING */ - result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done); + result = Curl_ssl_connect(data, conn, FIRSTSOCKET); if(result) return result; conn->bits.ftp_use_control_ssl = TRUE; @@ -3191,7 +3178,7 @@ static CURLcode ftp_connect(struct Curl_easy *data, /* When we connect, we start in the state where we await the 220 response */ - ftp_state(data, FTP_WAIT220); + state(data, FTP_WAIT220); result = ftp_multi_statemach(data, done); @@ -3259,7 +3246,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, if(data->state.wildcardmatch) { if(data->set.chunk_end && ftpc->file) { Curl_set_in_callback(data, true); - data->set.chunk_end(data->set.wildcardptr); + data->set.chunk_end(data->wildcard.customptr); Curl_set_in_callback(data, false); } ftpc->known_filesize = -1; @@ -3323,6 +3310,14 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, } } + if(conn->ssl[SECONDARYSOCKET].use) { + /* The secondary socket is using SSL so we must close down that part + first before we close the socket for real */ + Curl_ssl_close(data, conn, SECONDARYSOCKET); + + /* Note that we keep "use" set to TRUE since that (next) connection is + still requested to use SSL */ + } close_secondarysocket(data, conn); } @@ -3384,7 +3379,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, /* the response code from the transfer showed an error already so no use checking further */ ; - else if(data->state.upload) { + else if(data->set.upload) { if((-1 != data->state.infilesize) && (data->state.infilesize != data->req.writebytecount) && !data->set.crlf && @@ -3516,13 +3511,13 @@ static CURLcode ftp_nb_type(struct Curl_easy *data, char want = (char)(ascii?'A':'I'); if(ftpc->transfertype == want) { - ftp_state(data, newstate); + state(data, newstate); return ftp_state_type_resp(data, 200, newstate); } result = Curl_pp_sendf(data, &ftpc->pp, "TYPE %c", want); if(!result) { - ftp_state(data, newstate); + state(data, newstate); /* keep track of our current transfer type */ ftpc->transfertype = want; @@ -3576,15 +3571,23 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) * complete */ struct FTP *ftp = NULL; - /* if the second connection isn't done yet, wait for it to have - * connected to the remote host. When using proxy tunneling, this - * means the tunnel needs to have been establish. However, we - * can not expect the remote host to talk to us in any way yet. - * So, when using ftps: the SSL handshake will not start until we - * tell the remote server that we are there. */ - if(conn->cfilter[SECONDARYSOCKET]) { - result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected); - if(result || !Curl_conn_is_ip_connected(data, SECONDARYSOCKET)) { + /* if the second connection isn't done yet, wait for it */ + if(!conn->bits.tcpconnect[SECONDARYSOCKET]) { + if(Curl_connect_ongoing(conn)) { + /* As we're in TUNNEL_CONNECT state now, we know the proxy name and port + aren't used so we blank their arguments. */ + result = Curl_proxyCONNECT(data, SECONDARYSOCKET, NULL, 0); + + return result; + } + + result = Curl_is_connected(data, conn, SECONDARYSOCKET, &connected); + + /* Ready to do more? */ + if(connected) { + DEBUGF(infof(data, "DO-MORE connected phase starts")); + } + else { if(result && (ftpc->count1 == 0)) { *completep = -1; /* go back to DOING please */ /* this is a EPSV connect failing, try PASV instead */ @@ -3594,6 +3597,19 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) } } +#ifndef CURL_DISABLE_PROXY + result = Curl_proxy_connect(data, SECONDARYSOCKET); + if(result) + return result; + + if(CONNECT_SECONDARYSOCKET_PROXY_SSL()) + return result; + + if(conn->bits.tunnel_proxy && conn->bits.httpproxy && + Curl_connect_ongoing(conn)) + return result; +#endif + /* Curl_proxy_connect might have moved the protocol state */ ftp = data->req.p.ftp; @@ -3619,7 +3635,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) /* a transfer is about to take place, or if not a file name was given so we'll do a SIZE on it later and then we need the right TYPE first */ - if(ftpc->wait_data_conn) { + if(ftpc->wait_data_conn == TRUE) { bool serv_conned; result = ReceivedServerConnect(data, &serv_conned); @@ -3640,14 +3656,20 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) connected back to us */ } } - else if(data->state.upload) { + else if(data->set.upload) { result = ftp_nb_type(data, conn, data->state.prefer_ascii, FTP_STOR_TYPE); if(result) return result; result = ftp_multi_statemach(data, &complete); - *completep = (int)complete; + if(ftpc->wait_data_conn) + /* if we reach the end of the FTP state machine here, *complete will be + TRUE but so is ftpc->wait_data_conn, which says we need to wait for + the data connection and therefore we're not actually complete */ + *completep = 0; + else + *completep = (int)complete; } else { /* download */ @@ -3717,10 +3739,11 @@ CURLcode ftp_perform(struct Curl_easy *data, { /* this is FTP and no proxy */ CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; DEBUGF(infof(data, "DO phase starts")); - if(data->req.no_body) { + if(data->set.opt_no_body) { /* requested no body means no transfer... */ struct FTP *ftp = data->req.p.ftp; ftp->transfer = PPTRANSFER_INFO; @@ -3736,7 +3759,7 @@ CURLcode ftp_perform(struct Curl_easy *data, /* run the state-machine */ result = ftp_multi_statemach(data, dophase_done); - *connected = Curl_conn_is_connected(data->conn, SECONDARYSOCKET); + *connected = conn->bits.tcpconnect[SECONDARYSOCKET]; infof(data, "ftp_perform ends with SECONDARY: %d", *connected); @@ -3759,7 +3782,7 @@ static CURLcode init_wc_data(struct Curl_easy *data) char *last_slash; struct FTP *ftp = data->req.p.ftp; char *path = ftp->path; - struct WildcardData *wildcard = data->wildcard; + struct WildcardData *wildcard = &(data->wildcard); CURLcode result = CURLE_OK; struct ftp_wc *ftpwc = NULL; @@ -3807,7 +3830,7 @@ static CURLcode init_wc_data(struct Curl_easy *data) goto fail; } - wildcard->ftpwc = ftpwc; /* put it to the WildcardData tmp pointer */ + wildcard->protdata = ftpwc; /* put it to the WildcardData tmp pointer */ wildcard->dtor = wc_data_dtor; /* wildcard does not support NOCWD option (assert it?) */ @@ -3838,20 +3861,20 @@ static CURLcode init_wc_data(struct Curl_easy *data) infof(data, "Wildcard - Parsing started"); return CURLE_OK; -fail: + fail: if(ftpwc) { Curl_ftp_parselist_data_free(&ftpwc->parser); free(ftpwc); } Curl_safefree(wildcard->pattern); wildcard->dtor = ZERO_NULL; - wildcard->ftpwc = NULL; + wildcard->protdata = NULL; return result; } static CURLcode wc_statemach(struct Curl_easy *data) { - struct WildcardData * const wildcard = data->wildcard; + struct WildcardData * const wildcard = &(data->wildcard); struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; @@ -3868,7 +3891,7 @@ static CURLcode wc_statemach(struct Curl_easy *data) case CURLWC_MATCHING: { /* In this state is LIST response successfully parsed, so lets restore previous WRITEFUNCTION callback and WRITEDATA pointer */ - struct ftp_wc *ftpwc = wildcard->ftpwc; + struct ftp_wc *ftpwc = wildcard->protdata; data->set.fwrite_func = ftpwc->backup.write_function; data->set.out = ftpwc->backup.file_descriptor; ftpwc->backup.write_function = ZERO_NULL; @@ -3907,7 +3930,7 @@ static CURLcode wc_statemach(struct Curl_easy *data) long userresponse; Curl_set_in_callback(data, true); userresponse = data->set.chunk_bgn( - finfo, data->set.wildcardptr, (int)wildcard->filelist.size); + finfo, wildcard->customptr, (int)wildcard->filelist.size); Curl_set_in_callback(data, false); switch(userresponse) { case CURL_CHUNK_BGN_FUNC_SKIP: @@ -3947,7 +3970,7 @@ static CURLcode wc_statemach(struct Curl_easy *data) case CURLWC_SKIP: { if(data->set.chunk_end) { Curl_set_in_callback(data, true); - data->set.chunk_end(data->set.wildcardptr); + data->set.chunk_end(data->wildcard.customptr); Curl_set_in_callback(data, false); } Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL); @@ -3957,7 +3980,7 @@ static CURLcode wc_statemach(struct Curl_easy *data) } case CURLWC_CLEAN: { - struct ftp_wc *ftpwc = wildcard->ftpwc; + struct ftp_wc *ftpwc = wildcard->protdata; result = CURLE_OK; if(ftpwc) result = Curl_ftp_parselist_geterror(ftpwc->parser); @@ -3969,10 +3992,8 @@ static CURLcode wc_statemach(struct Curl_easy *data) case CURLWC_DONE: case CURLWC_ERROR: case CURLWC_CLEAR: - if(wildcard->dtor) { - wildcard->dtor(wildcard->ftpwc); - wildcard->ftpwc = NULL; - } + if(wildcard->dtor) + wildcard->dtor(wildcard->protdata); return result; } } @@ -3999,8 +4020,8 @@ static CURLcode ftp_do(struct Curl_easy *data, bool *done) if(data->state.wildcardmatch) { result = wc_statemach(data); - if(data->wildcard->state == CURLWC_SKIP || - data->wildcard->state == CURLWC_DONE) { + if(data->wildcard.state == CURLWC_SKIP || + data->wildcard.state == CURLWC_DONE) { /* do not call ftp_regular_transfer */ return CURLE_OK; } @@ -4039,11 +4060,11 @@ static CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn) curl_easy_strerror(result)); conn->proto.ftpc.ctl_valid = FALSE; /* mark control connection as bad */ connclose(conn, "QUIT command failed"); /* mark for connection closure */ - ftp_state(data, FTP_STOP); + state(data, FTP_STOP); return result; } - ftp_state(data, FTP_QUIT); + state(data, FTP_QUIT); result = ftp_block_statemach(data, conn); } @@ -4086,8 +4107,6 @@ static CURLcode ftp_disconnect(struct Curl_easy *data, } freedirs(ftpc); - Curl_safefree(ftpc->account); - Curl_safefree(ftpc->alternative_to_user); Curl_safefree(ftpc->prevpath); Curl_safefree(ftpc->server_os); Curl_pp_disconnect(pp); @@ -4134,7 +4153,7 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data) case FTPFILE_NOCWD: /* fastest, but less standard-compliant */ if((pathLen > 0) && (rawPath[pathLen - 1] != '/')) - fileName = rawPath; /* this is a full file path */ + fileName = rawPath; /* this is a full file path */ /* else: ftpc->file is not used anywhere other than for operations on a file. In other words, never for directory operations. @@ -4149,7 +4168,7 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data) /* get path before last slash, except for / */ size_t dirlen = slashPos - rawPath; if(dirlen == 0) - dirlen = 1; + dirlen++; ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0])); if(!ftpc->dirs) { @@ -4176,14 +4195,13 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data) /* current position: begin of next path component */ const char *curPos = rawPath; - /* number of entries allocated for the 'dirs' array */ - size_t dirAlloc = 0; + int dirAlloc = 0; /* number of entries allocated for the 'dirs' array */ const char *str = rawPath; for(; *str != 0; ++str) - if(*str == '/') + if (*str == '/') ++dirAlloc; - if(dirAlloc) { + if(dirAlloc > 0) { ftpc->dirs = calloc(dirAlloc, sizeof(ftpc->dirs[0])); if(!ftpc->dirs) { free(rawPath); @@ -4213,7 +4231,7 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data) curPos = slashPos + 1; } } - DEBUGASSERT((size_t)ftpc->dirdepth <= dirAlloc); + DEBUGASSERT(ftpc->dirdepth <= dirAlloc); fileName = curPos; /* the rest is the file name (or empty) */ } break; @@ -4225,7 +4243,7 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data) ftpc->file = NULL; /* instead of point to a zero byte, we make it a NULL pointer */ - if(data->state.upload && !ftpc->file && (ftp->transfer == PPTRANSFER_BODY)) { + if(data->set.upload && !ftpc->file && (ftp->transfer == PPTRANSFER_BODY)) { /* We need a file name when uploading. Return error! */ failf(data, "Uploading to a URL without a file name"); free(rawPath); @@ -4356,32 +4374,11 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data, { char *type; struct FTP *ftp; - CURLcode result = CURLE_OK; - struct ftp_conn *ftpc = &conn->proto.ftpc; - ftp = calloc(sizeof(struct FTP), 1); + data->req.p.ftp = ftp = calloc(sizeof(struct FTP), 1); if(!ftp) return CURLE_OUT_OF_MEMORY; - /* clone connection related data that is FTP specific */ - if(data->set.str[STRING_FTP_ACCOUNT]) { - ftpc->account = strdup(data->set.str[STRING_FTP_ACCOUNT]); - if(!ftpc->account) { - free(ftp); - return CURLE_OUT_OF_MEMORY; - } - } - if(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]) { - ftpc->alternative_to_user = - strdup(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]); - if(!ftpc->alternative_to_user) { - Curl_safefree(ftpc->account); - free(ftp); - return CURLE_OUT_OF_MEMORY; - } - } - data->req.p.ftp = ftp; - ftp->path = &data->state.up.path[1]; /* don't include the initial slash */ /* FTP URLs support an extension like ";type=<typecode>" that @@ -4416,11 +4413,9 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data, /* get some initial data into the ftp struct */ ftp->transfer = PPTRANSFER_BODY; ftp->downloadsize = 0; - ftpc->known_filesize = -1; /* unknown size for now */ - ftpc->use_ssl = data->set.use_ssl; - ftpc->ccc = data->set.ftp_ccc; + conn->proto.ftpc.known_filesize = -1; /* unknown size for now */ - return result; + return CURLE_OK; } #endif /* CURL_DISABLE_FTP */ diff --git a/contrib/libs/curl/lib/ftp.h b/contrib/libs/curl/lib/ftp.h index 977fc883b1..7f6f4328d1 100644 --- a/contrib/libs/curl/lib/ftp.h +++ b/contrib/libs/curl/lib/ftp.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -42,7 +42,7 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data, ssize_t *nread, /**************************************************************************** * FTP unique setup ***************************************************************************/ -enum { +typedef enum { FTP_STOP, /* do nothing state, stops the state machine */ FTP_WAIT220, /* waiting for the initial 220 response immediately after a connect */ @@ -80,8 +80,7 @@ enum { FTP_STOR, /* generic state for STOR and APPE */ FTP_QUIT, FTP_LAST /* never used */ -}; -typedef unsigned char ftpstate; /* use the enum values */ +} ftpstate; struct ftp_parselist_data; /* defined later in ftplistparser.c */ @@ -120,46 +119,41 @@ struct FTP { struct */ struct ftp_conn { struct pingpong pp; - char *account; - char *alternative_to_user; char *entrypath; /* the PWD reply when we logged on */ char *file; /* url-decoded file name (or path) */ char **dirs; /* realloc()ed array for path components */ + int dirdepth; /* number of entries used in the 'dirs' array */ + bool dont_check; /* Set to TRUE to prevent the final (post-transfer) + file size and 226/250 status check. It should still + read the line, just ignore the result. */ + bool ctl_valid; /* Tells Curl_ftp_quit() whether or not to do anything. If + the connection has timed out or been closed, this + should be FALSE when it gets to Curl_ftp_quit() */ + bool cwddone; /* if it has been determined that the proper CWD combo + already has been done */ + int cwdcount; /* number of CWD commands issued */ + bool cwdfail; /* set TRUE if a CWD command fails, as then we must prevent + caching the current directory */ + bool wait_data_conn; /* this is set TRUE if data connection is waited */ + /* newhost is the (allocated) IP addr or host name to connect the data + connection to */ + unsigned short newport; char *newhost; char *prevpath; /* url-decoded conn->path from the previous transfer */ char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a and others (A/I or zero) */ + int count1; /* general purpose counter for the state machine */ + int count2; /* general purpose counter for the state machine */ + int count3; /* general purpose counter for the state machine */ + ftpstate state; /* always use ftp.c:state() to change state! */ + ftpstate state_saved; /* transfer type saved to be reloaded after + data connection is established */ curl_off_t retr_size_saved; /* Size of retrieved file saved */ char *server_os; /* The target server operating system. */ curl_off_t known_filesize; /* file size is different from -1, if wildcard LIST parsing was done and wc_statemach set it */ - int dirdepth; /* number of entries used in the 'dirs' array */ - int cwdcount; /* number of CWD commands issued */ - int count1; /* general purpose counter for the state machine */ - int count2; /* general purpose counter for the state machine */ - int count3; /* general purpose counter for the state machine */ - /* newhost is the (allocated) IP addr or host name to connect the data - connection to */ - unsigned short newport; - ftpstate state; /* always use ftp.c:state() to change state! */ - ftpstate state_saved; /* transfer type saved to be reloaded after data - connection is established */ - unsigned char use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or - IMAP or POP3 or others! (type: curl_usessl)*/ - unsigned char ccc; /* ccc level for this connection */ BIT(ftp_trying_alternative); - BIT(dont_check); /* Set to TRUE to prevent the final (post-transfer) - file size and 226/250 status check. It should still - read the line, just ignore the result. */ - BIT(ctl_valid); /* Tells Curl_ftp_quit() whether or not to do anything. If - the connection has timed out or been closed, this - should be FALSE when it gets to Curl_ftp_quit() */ - BIT(cwddone); /* if it has been determined that the proper CWD combo - already has been done */ - BIT(cwdfail); /* set TRUE if a CWD command fails, as then we must prevent - caching the current directory */ - BIT(wait_data_conn); /* this is set TRUE if data connection is waited */ }; #define DEFAULT_ACCEPT_TIMEOUT 60000 /* milliseconds == one minute */ diff --git a/contrib/libs/curl/lib/ftplistparser.c b/contrib/libs/curl/lib/ftplistparser.c index 226d9bc033..40f5f3f189 100644 --- a/contrib/libs/curl/lib/ftplistparser.c +++ b/contrib/libs/curl/lib/ftplistparser.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -181,43 +181,6 @@ struct ftp_parselist_data { } offsets; }; -static void fileinfo_dtor(void *user, void *element) -{ - (void)user; - Curl_fileinfo_cleanup(element); -} - -CURLcode Curl_wildcard_init(struct WildcardData *wc) -{ - Curl_llist_init(&wc->filelist, fileinfo_dtor); - wc->state = CURLWC_INIT; - - return CURLE_OK; -} - -void Curl_wildcard_dtor(struct WildcardData **wcp) -{ - struct WildcardData *wc = *wcp; - if(!wc) - return; - - if(wc->dtor) { - wc->dtor(wc->ftpwc); - wc->dtor = ZERO_NULL; - wc->ftpwc = NULL; - } - DEBUGASSERT(wc->ftpwc == NULL); - - Curl_llist_destroy(&wc->filelist, NULL); - free(wc->path); - wc->path = NULL; - free(wc->pattern); - wc->pattern = NULL; - wc->state = CURLWC_INIT; - free(wc); - *wcp = NULL; -} - struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void) { return calloc(1, sizeof(struct ftp_parselist_data)); @@ -242,9 +205,9 @@ CURLcode Curl_ftp_parselist_geterror(struct ftp_parselist_data *pl_data) #define FTP_LP_MALFORMATED_PERM 0x01000000 -static unsigned int ftp_pl_get_permission(const char *str) +static int ftp_pl_get_permission(const char *str) { - unsigned int permissions = 0; + int permissions = 0; /* USER */ if(str[0] == 'r') permissions |= 1 << 8; @@ -311,15 +274,15 @@ static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data, struct fileinfo *infop) { curl_fnmatch_callback compare; - struct WildcardData *wc = data->wildcard; - struct ftp_wc *ftpwc = wc->ftpwc; + struct WildcardData *wc = &data->wildcard; + struct ftp_wc *ftpwc = wc->protdata; struct Curl_llist *llist = &wc->filelist; struct ftp_parselist_data *parser = ftpwc->parser; bool add = TRUE; struct curl_fileinfo *finfo = &infop->info; - /* set the finfo pointers */ - char *str = Curl_dyn_ptr(&infop->buf); + /* move finfo pointers to b_data */ + char *str = finfo->b_data; finfo->filename = str + parser->offsets.filename; finfo->strings.group = parser->offsets.group ? str + parser->offsets.group : NULL; @@ -362,16 +325,16 @@ static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data, return CURLE_OK; } -#define MAX_FTPLIST_BUFFER 10000 /* arbitrarily set */ - size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, void *connptr) { size_t bufflen = size*nmemb; struct Curl_easy *data = (struct Curl_easy *)connptr; - struct ftp_wc *ftpwc = data->wildcard->ftpwc; + struct ftp_wc *ftpwc = data->wildcard.protdata; struct ftp_parselist_data *parser = ftpwc->parser; - size_t i = 0; + struct fileinfo *infop; + struct curl_fileinfo *finfo; + unsigned long i = 0; CURLcode result; size_t retsize = bufflen; @@ -387,35 +350,48 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, if(parser->os_type == OS_TYPE_UNKNOWN && bufflen > 0) { /* considering info about FILE response format */ - parser->os_type = ISDIGIT(buffer[0]) ? OS_TYPE_WIN_NT : OS_TYPE_UNIX; + parser->os_type = (buffer[0] >= '0' && buffer[0] <= '9') ? + OS_TYPE_WIN_NT : OS_TYPE_UNIX; } while(i < bufflen) { /* FSM */ - char *mem; - size_t len; /* number of bytes of data in the dynbuf */ + char c = buffer[i]; - struct fileinfo *infop; - struct curl_fileinfo *finfo; if(!parser->file_data) { /* tmp file data is not allocated yet */ parser->file_data = Curl_fileinfo_alloc(); if(!parser->file_data) { parser->error = CURLE_OUT_OF_MEMORY; goto fail; } + parser->file_data->info.b_data = malloc(FTP_BUFFER_ALLOCSIZE); + if(!parser->file_data->info.b_data) { + parser->error = CURLE_OUT_OF_MEMORY; + goto fail; + } + parser->file_data->info.b_size = FTP_BUFFER_ALLOCSIZE; parser->item_offset = 0; parser->item_length = 0; - Curl_dyn_init(&parser->file_data->buf, MAX_FTPLIST_BUFFER); } infop = parser->file_data; finfo = &infop->info; - - if(Curl_dyn_addn(&infop->buf, &c, 1)) { - parser->error = CURLE_OUT_OF_MEMORY; - goto fail; + finfo->b_data[finfo->b_used++] = c; + + if(finfo->b_used >= finfo->b_size - 1) { + /* if it is important, extend buffer space for file data */ + char *tmp = realloc(finfo->b_data, + finfo->b_size + FTP_BUFFER_ALLOCSIZE); + if(tmp) { + finfo->b_size += FTP_BUFFER_ALLOCSIZE; + finfo->b_data = tmp; + } + else { + Curl_fileinfo_cleanup(parser->file_data); + parser->file_data = NULL; + parser->error = CURLE_OUT_OF_MEMORY; + goto fail; + } } - len = Curl_dyn_len(&infop->buf); - mem = Curl_dyn_ptr(&infop->buf); switch(parser->os_type) { case OS_TYPE_UNIX: @@ -430,7 +406,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, else { parser->state.UNIX.main = PL_UNIX_FILETYPE; /* start FSM again not considering size of directory */ - Curl_dyn_reset(&infop->buf); + finfo->b_used = 0; continue; } break; @@ -438,12 +414,12 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->item_length++; if(c == '\r') { parser->item_length--; - Curl_dyn_setlen(&infop->buf, --len); + finfo->b_used--; } else if(c == '\n') { - mem[parser->item_length - 1] = 0; - if(!strncmp("total ", mem, 6)) { - char *endptr = mem + 6; + finfo->b_data[parser->item_length - 1] = 0; + if(strncmp("total ", finfo->b_data, 6) == 0) { + char *endptr = finfo->b_data + 6; /* here we can deal with directory size, pass the leading whitespace and then the digits */ while(ISBLANK(*endptr)) @@ -455,7 +431,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, goto fail; } parser->state.UNIX.main = PL_UNIX_FILETYPE; - Curl_dyn_reset(&infop->buf); + finfo->b_used = 0; } else { parser->error = CURLE_FTP_BAD_FILE_LIST; @@ -513,8 +489,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->error = CURLE_FTP_BAD_FILE_LIST; goto fail; } - mem[10] = 0; /* terminate permissions */ - perm = ftp_pl_get_permission(mem + parser->item_offset); + finfo->b_data[10] = 0; /* terminate permissions */ + perm = ftp_pl_get_permission(finfo->b_data + parser->item_offset); if(perm & FTP_LP_MALFORMATED_PERM) { parser->error = CURLE_FTP_BAD_FILE_LIST; goto fail; @@ -532,8 +508,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, switch(parser->state.UNIX.sub.hlinks) { case PL_UNIX_HLINKS_PRESPACE: if(c != ' ') { - if(ISDIGIT(c)) { - parser->item_offset = len - 1; + if(c >= '0' && c <= '9') { + parser->item_offset = finfo->b_used - 1; parser->item_length = 1; parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER; } @@ -548,8 +524,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, if(c == ' ') { char *p; long int hlinks; - mem[parser->item_offset + parser->item_length - 1] = 0; - hlinks = strtol(mem + parser->item_offset, &p, 10); + finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; + hlinks = strtol(finfo->b_data + parser->item_offset, &p, 10); if(p[0] == '\0' && hlinks != LONG_MAX && hlinks != LONG_MIN) { parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT; parser->file_data->info.hardlinks = hlinks; @@ -559,7 +535,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->state.UNIX.main = PL_UNIX_USER; parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE; } - else if(!ISDIGIT(c)) { + else if(c < '0' || c > '9') { parser->error = CURLE_FTP_BAD_FILE_LIST; goto fail; } @@ -570,7 +546,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, switch(parser->state.UNIX.sub.user) { case PL_UNIX_USER_PRESPACE: if(c != ' ') { - parser->item_offset = len - 1; + parser->item_offset = finfo->b_used - 1; parser->item_length = 1; parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING; } @@ -578,7 +554,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, case PL_UNIX_USER_PARSING: parser->item_length++; if(c == ' ') { - mem[parser->item_offset + parser->item_length - 1] = 0; + finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; parser->offsets.user = parser->item_offset; parser->state.UNIX.main = PL_UNIX_GROUP; parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE; @@ -592,7 +568,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, switch(parser->state.UNIX.sub.group) { case PL_UNIX_GROUP_PRESPACE: if(c != ' ') { - parser->item_offset = len - 1; + parser->item_offset = finfo->b_used - 1; parser->item_length = 1; parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME; } @@ -600,7 +576,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, case PL_UNIX_GROUP_NAME: parser->item_length++; if(c == ' ') { - mem[parser->item_offset + parser->item_length - 1] = 0; + finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; parser->offsets.group = parser->item_offset; parser->state.UNIX.main = PL_UNIX_SIZE; parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE; @@ -614,8 +590,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, switch(parser->state.UNIX.sub.size) { case PL_UNIX_SIZE_PRESPACE: if(c != ' ') { - if(ISDIGIT(c)) { - parser->item_offset = len - 1; + if(c >= '0' && c <= '9') { + parser->item_offset = finfo->b_used - 1; parser->item_length = 1; parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER; } @@ -630,8 +606,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, if(c == ' ') { char *p; curl_off_t fsize; - mem[parser->item_offset + parser->item_length - 1] = 0; - if(!curlx_strtoofft(mem + parser->item_offset, + finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; + if(!curlx_strtoofft(finfo->b_data + parser->item_offset, &p, 10, &fsize)) { if(p[0] == '\0' && fsize != CURL_OFF_T_MAX && fsize != CURL_OFF_T_MIN) { @@ -656,7 +632,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, case PL_UNIX_TIME_PREPART1: if(c != ' ') { if(ISALNUM(c)) { - parser->item_offset = len -1; + parser->item_offset = finfo->b_used -1; parser->item_length = 1; parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1; } @@ -713,10 +689,10 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, case PL_UNIX_TIME_PART3: parser->item_length++; if(c == ' ') { - mem[parser->item_offset + parser->item_length -1] = 0; + finfo->b_data[parser->item_offset + parser->item_length -1] = 0; parser->offsets.time = parser->item_offset; /* - if(ftp_pl_gettime(parser, finfo->mem + parser->item_offset)) { + if(ftp_pl_gettime(parser, finfo->b_data + parser->item_offset)) { parser->file_data->flags |= CURLFINFOFLAG_KNOWN_TIME; } */ @@ -740,7 +716,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, switch(parser->state.UNIX.sub.filename) { case PL_UNIX_FILENAME_PRESPACE: if(c != ' ') { - parser->item_offset = len - 1; + parser->item_offset = finfo->b_used - 1; parser->item_length = 1; parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME; } @@ -751,7 +727,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL; } else if(c == '\n') { - mem[parser->item_offset + parser->item_length - 1] = 0; + finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; parser->offsets.filename = parser->item_offset; parser->state.UNIX.main = PL_UNIX_FILETYPE; result = ftp_pl_insert_finfo(data, infop); @@ -763,7 +739,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, break; case PL_UNIX_FILENAME_WINDOWSEOL: if(c == '\n') { - mem[parser->item_offset + parser->item_length - 1] = 0; + finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; parser->offsets.filename = parser->item_offset; parser->state.UNIX.main = PL_UNIX_FILETYPE; result = ftp_pl_insert_finfo(data, infop); @@ -783,7 +759,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, switch(parser->state.UNIX.sub.symlink) { case PL_UNIX_SYMLINK_PRESPACE: if(c != ' ') { - parser->item_offset = len - 1; + parser->item_offset = finfo->b_used - 1; parser->item_length = 1; parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; } @@ -829,7 +805,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, if(c == ' ') { parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4; /* now place where is symlink following */ - mem[parser->item_offset + parser->item_length - 4] = 0; + finfo->b_data[parser->item_offset + parser->item_length - 4] = 0; parser->offsets.filename = parser->item_offset; parser->item_length = 0; parser->item_offset = 0; @@ -845,7 +821,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, case PL_UNIX_SYMLINK_PRETARGET4: if(c != '\r' && c != '\n') { parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET; - parser->item_offset = len - 1; + parser->item_offset = finfo->b_used - 1; parser->item_length = 1; } else { @@ -859,7 +835,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL; } else if(c == '\n') { - mem[parser->item_offset + parser->item_length - 1] = 0; + finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; parser->offsets.symlink_target = parser->item_offset; result = ftp_pl_insert_finfo(data, infop); if(result) { @@ -871,7 +847,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, break; case PL_UNIX_SYMLINK_WINDOWSEOL: if(c == '\n') { - mem[parser->item_offset + parser->item_length - 1] = 0; + finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; parser->offsets.symlink_target = parser->item_offset; result = ftp_pl_insert_finfo(data, infop); if(result) { @@ -925,7 +901,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, case PL_WINNT_TIME_TIME: if(c == ' ') { parser->offsets.time = parser->item_offset; - mem[parser->item_offset + parser->item_length -1] = 0; + finfo->b_data[parser->item_offset + parser->item_length -1] = 0; parser->state.NT.main = PL_WINNT_DIRORSIZE; parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE; parser->item_length = 0; @@ -941,7 +917,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, switch(parser->state.NT.sub.dirorsize) { case PL_WINNT_DIRORSIZE_PRESPACE: if(c != ' ') { - parser->item_offset = len - 1; + parser->item_offset = finfo->b_used - 1; parser->item_length = 1; parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT; } @@ -949,14 +925,14 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, case PL_WINNT_DIRORSIZE_CONTENT: parser->item_length ++; if(c == ' ') { - mem[parser->item_offset + parser->item_length - 1] = 0; - if(strcmp("<DIR>", mem + parser->item_offset) == 0) { + finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; + if(strcmp("<DIR>", finfo->b_data + parser->item_offset) == 0) { finfo->filetype = CURLFILETYPE_DIRECTORY; finfo->size = 0; } else { char *endptr; - if(curlx_strtoofft(mem + + if(curlx_strtoofft(finfo->b_data + parser->item_offset, &endptr, 10, &finfo->size)) { parser->error = CURLE_FTP_BAD_FILE_LIST; @@ -978,7 +954,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, switch(parser->state.NT.sub.filename) { case PL_WINNT_FILENAME_PRESPACE: if(c != ' ') { - parser->item_offset = len -1; + parser->item_offset = finfo->b_used -1; parser->item_length = 1; parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT; } @@ -987,11 +963,11 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->item_length++; if(c == '\r') { parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL; - mem[len - 1] = 0; + finfo->b_data[finfo->b_used - 1] = 0; } else if(c == '\n') { parser->offsets.filename = parser->item_offset; - mem[len - 1] = 0; + finfo->b_data[finfo->b_used - 1] = 0; result = ftp_pl_insert_finfo(data, infop); if(result) { parser->error = result; diff --git a/contrib/libs/curl/lib/ftplistparser.h b/contrib/libs/curl/lib/ftplistparser.h index 5ba1f6a97d..0a80543417 100644 --- a/contrib/libs/curl/lib/ftplistparser.h +++ b/contrib/libs/curl/lib/ftplistparser.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,39 +39,5 @@ struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void); void Curl_ftp_parselist_data_free(struct ftp_parselist_data **pl_data); -/* list of wildcard process states */ -typedef enum { - CURLWC_CLEAR = 0, - CURLWC_INIT = 1, - CURLWC_MATCHING, /* library is trying to get list of addresses for - downloading */ - CURLWC_DOWNLOADING, - CURLWC_CLEAN, /* deallocate resources and reset settings */ - CURLWC_SKIP, /* skip over concrete file */ - CURLWC_ERROR, /* error cases */ - CURLWC_DONE /* if is wildcard->state == CURLWC_DONE wildcard loop - will end */ -} wildcard_states; - -typedef void (*wildcard_dtor)(void *ptr); - -/* struct keeping information about wildcard download process */ -struct WildcardData { - char *path; /* path to the directory, where we trying wildcard-match */ - char *pattern; /* wildcard pattern */ - struct Curl_llist filelist; /* llist with struct Curl_fileinfo */ - struct ftp_wc *ftpwc; /* pointer to FTP wildcard data */ - wildcard_dtor dtor; - unsigned char state; /* wildcard_states */ -}; - -CURLcode Curl_wildcard_init(struct WildcardData *wc); -void Curl_wildcard_dtor(struct WildcardData **wcp); - -struct Curl_easy; - -#else -/* FTP is disabled */ -#define Curl_wildcard_dtor(x) #endif /* CURL_DISABLE_FTP */ #endif /* HEADER_CURL_FTPLISTPARSER_H */ diff --git a/contrib/libs/curl/lib/functypes.h b/contrib/libs/curl/lib/functypes.h index 075c02e54f..8891b1d5d6 100644 --- a/contrib/libs/curl/lib/functypes.h +++ b/contrib/libs/curl/lib/functypes.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/getenv.c b/contrib/libs/curl/lib/getenv.c index 8069784728..5f00fd13a4 100644 --- a/contrib/libs/curl/lib/getenv.c +++ b/contrib/libs/curl/lib/getenv.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/getinfo.c b/contrib/libs/curl/lib/getinfo.c index f1574e097b..c3556b3102 100644 --- a/contrib/libs/curl/lib/getinfo.c +++ b/contrib/libs/curl/lib/getinfo.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -415,13 +415,6 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info, case CURLINFO_RETRY_AFTER: *param_offt = data->info.retry_after; break; - case CURLINFO_XFER_ID: - *param_offt = data->id; - break; - case CURLINFO_CONN_ID: - *param_offt = data->conn? - data->conn->connection_id : data->state.recent_conn_id; - break; default: return CURLE_UNKNOWN_OPTION; } @@ -540,7 +533,13 @@ static CURLcode getinfo_slist(struct Curl_easy *data, CURLINFO info, #ifdef USE_SSL if(conn && tsi->backend != CURLSSLBACKEND_NONE) { - tsi->internals = Curl_ssl_get_internals(data, FIRSTSOCKET, info, 0); + unsigned int i; + for(i = 0; i < (sizeof(conn->ssl) / sizeof(conn->ssl[0])); ++i) { + if(conn->ssl[i].use) { + tsi->internals = Curl_ssl->get_internals(&conn->ssl[i], info); + break; + } + } } #endif } diff --git a/contrib/libs/curl/lib/getinfo.h b/contrib/libs/curl/lib/getinfo.h index 56bb440b43..1b5e8c20f8 100644 --- a/contrib/libs/curl/lib/getinfo.h +++ b/contrib/libs/curl/lib/getinfo.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/gopher.c b/contrib/libs/curl/lib/gopher.c index 4a11d9364e..01f4bdef0c 100644 --- a/contrib/libs/curl/lib/gopher.c +++ b/contrib/libs/curl/lib/gopher.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -30,7 +30,6 @@ #include <curl/curl.h> #include "transfer.h" #include "sendf.h" -#include "cfilters.h" #include "connect.h" #include "progress.h" #include "gopher.h" @@ -118,9 +117,7 @@ static CURLcode gopher_connect(struct Curl_easy *data, bool *done) static CURLcode gopher_connecting(struct Curl_easy *data, bool *done) { struct connectdata *conn = data->conn; - CURLcode result; - - result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done); + CURLcode result = Curl_ssl_connect(data, conn, FIRSTSOCKET); if(result) connclose(conn, "Failed TLS connection"); *done = TRUE; @@ -239,4 +236,4 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done) Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); return CURLE_OK; } -#endif /* CURL_DISABLE_GOPHER */ +#endif /*CURL_DISABLE_GOPHER*/ diff --git a/contrib/libs/curl/lib/gopher.h b/contrib/libs/curl/lib/gopher.h index 9e3365b71c..4ea269d2b4 100644 --- a/contrib/libs/curl/lib/gopher.h +++ b/contrib/libs/curl/lib/gopher.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/h2h3.c b/contrib/libs/curl/lib/h2h3.c new file mode 100644 index 0000000000..50254ad0fa --- /dev/null +++ b/contrib/libs/curl/lib/h2h3.c @@ -0,0 +1,309 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" +#include "urldata.h" +#include "h2h3.h" +#include "transfer.h" +#include "sendf.h" +#include "strcase.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Curl_pseudo_headers() creates the array with pseudo headers to be + * used in a HTTP/2 or HTTP/3 request. + */ + +#if defined(USE_NGHTTP2) || defined(ENABLE_QUIC) + +/* Index where :authority header field will appear in request header + field list. */ +#define AUTHORITY_DST_IDX 3 + +/* USHRT_MAX is 65535 == 0xffff */ +#define HEADER_OVERFLOW(x) \ + (x.namelen > 0xffff || x.valuelen > 0xffff - x.namelen) + +/* + * Check header memory for the token "trailers". + * Parse the tokens as separated by comma and surrounded by whitespace. + * Returns TRUE if found or FALSE if not. + */ +static bool contains_trailers(const char *p, size_t len) +{ + const char *end = p + len; + for(;;) { + for(; p != end && (*p == ' ' || *p == '\t'); ++p) + ; + if(p == end || (size_t)(end - p) < sizeof("trailers") - 1) + return FALSE; + if(strncasecompare("trailers", p, sizeof("trailers") - 1)) { + p += sizeof("trailers") - 1; + for(; p != end && (*p == ' ' || *p == '\t'); ++p) + ; + if(p == end || *p == ',') + return TRUE; + } + /* skip to next token */ + for(; p != end && *p != ','; ++p) + ; + if(p == end) + return FALSE; + ++p; + } +} + +typedef enum { + /* Send header to server */ + HEADERINST_FORWARD, + /* Don't send header to server */ + HEADERINST_IGNORE, + /* Discard header, and replace it with "te: trailers" */ + HEADERINST_TE_TRAILERS +} header_instruction; + +/* Decides how to treat given header field. */ +static header_instruction inspect_header(const char *name, size_t namelen, + const char *value, size_t valuelen) { + switch(namelen) { + case 2: + if(!strncasecompare("te", name, namelen)) + return HEADERINST_FORWARD; + + return contains_trailers(value, valuelen) ? + HEADERINST_TE_TRAILERS : HEADERINST_IGNORE; + case 7: + return strncasecompare("upgrade", name, namelen) ? + HEADERINST_IGNORE : HEADERINST_FORWARD; + case 10: + return (strncasecompare("connection", name, namelen) || + strncasecompare("keep-alive", name, namelen)) ? + HEADERINST_IGNORE : HEADERINST_FORWARD; + case 16: + return strncasecompare("proxy-connection", name, namelen) ? + HEADERINST_IGNORE : HEADERINST_FORWARD; + case 17: + return strncasecompare("transfer-encoding", name, namelen) ? + HEADERINST_IGNORE : HEADERINST_FORWARD; + default: + return HEADERINST_FORWARD; + } +} + +CURLcode Curl_pseudo_headers(struct Curl_easy *data, + const char *mem, /* the request */ + const size_t len /* size of request */, + struct h2h3req **hp) +{ + struct connectdata *conn = data->conn; + size_t nheader = 0; + size_t i; + size_t authority_idx; + char *hdbuf = (char *)mem; + char *end, *line_end; + struct h2h3pseudo *nva = NULL; + struct h2h3req *hreq = NULL; + char *vptr; + + /* Calculate number of headers contained in [mem, mem + len). Assumes a + correctly generated HTTP header field block. */ + for(i = 1; i < len; ++i) { + if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') { + ++nheader; + ++i; + } + } + if(nheader < 2) { + goto fail; + } + /* We counted additional 2 \r\n in the first and last line. We need 3 + new headers: :method, :path and :scheme. Therefore we need one + more space. */ + nheader += 1; + hreq = malloc(sizeof(struct h2h3req) + + sizeof(struct h2h3pseudo) * (nheader - 1)); + if(!hreq) { + goto fail; + } + + nva = &hreq->header[0]; + + /* Extract :method, :path from request line + We do line endings with CRLF so checking for CR is enough */ + line_end = memchr(hdbuf, '\r', len); + if(!line_end) { + goto fail; + } + + /* Method does not contain spaces */ + end = memchr(hdbuf, ' ', line_end - hdbuf); + if(!end || end == hdbuf) + goto fail; + nva[0].name = H2H3_PSEUDO_METHOD; + nva[0].namelen = sizeof(H2H3_PSEUDO_METHOD) - 1; + nva[0].value = hdbuf; + nva[0].valuelen = (size_t)(end - hdbuf); + + hdbuf = end + 1; + + /* Path may contain spaces so scan backwards */ + end = NULL; + for(i = (size_t)(line_end - hdbuf); i; --i) { + if(hdbuf[i - 1] == ' ') { + end = &hdbuf[i - 1]; + break; + } + } + if(!end || end == hdbuf) + goto fail; + nva[1].name = H2H3_PSEUDO_PATH; + nva[1].namelen = sizeof(H2H3_PSEUDO_PATH) - 1; + nva[1].value = hdbuf; + nva[1].valuelen = (end - hdbuf); + + nva[2].name = H2H3_PSEUDO_SCHEME; + nva[2].namelen = sizeof(H2H3_PSEUDO_SCHEME) - 1; + vptr = Curl_checkheaders(data, STRCONST(H2H3_PSEUDO_SCHEME)); + if(vptr) { + vptr += sizeof(H2H3_PSEUDO_SCHEME); + while(*vptr && ISBLANK(*vptr)) + vptr++; + nva[2].value = vptr; + infof(data, "set pseudo header %s to %s", H2H3_PSEUDO_SCHEME, vptr); + } + else { + if(conn->handler->flags & PROTOPT_SSL) + nva[2].value = "https"; + else + nva[2].value = "http"; + } + nva[2].valuelen = strlen((char *)nva[2].value); + + authority_idx = 0; + i = 3; + while(i < nheader) { + size_t hlen; + + hdbuf = line_end + 2; + + /* check for next CR, but only within the piece of data left in the given + buffer */ + line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem)); + if(!line_end || (line_end == hdbuf)) + goto fail; + + /* header continuation lines are not supported */ + if(*hdbuf == ' ' || *hdbuf == '\t') + goto fail; + + for(end = hdbuf; end < line_end && *end != ':'; ++end) + ; + if(end == hdbuf || end == line_end) + goto fail; + hlen = end - hdbuf; + + if(hlen == 4 && strncasecompare("host", hdbuf, 4)) { + authority_idx = i; + nva[i].name = H2H3_PSEUDO_AUTHORITY; + nva[i].namelen = sizeof(H2H3_PSEUDO_AUTHORITY) - 1; + } + else { + nva[i].namelen = (size_t)(end - hdbuf); + /* Lower case the header name for HTTP/3 */ + Curl_strntolower((char *)hdbuf, hdbuf, nva[i].namelen); + nva[i].name = hdbuf; + } + hdbuf = end + 1; + while(*hdbuf == ' ' || *hdbuf == '\t') + ++hdbuf; + end = line_end; + + switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf, + end - hdbuf)) { + case HEADERINST_IGNORE: + /* skip header fields prohibited by HTTP/2 specification. */ + --nheader; + continue; + case HEADERINST_TE_TRAILERS: + nva[i].value = "trailers"; + nva[i].valuelen = sizeof("trailers") - 1; + break; + default: + nva[i].value = hdbuf; + nva[i].valuelen = (end - hdbuf); + } + + ++i; + } + + /* :authority must come before non-pseudo header fields */ + if(authority_idx && authority_idx != AUTHORITY_DST_IDX) { + struct h2h3pseudo authority = nva[authority_idx]; + for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) { + nva[i] = nva[i - 1]; + } + nva[i] = authority; + } + + /* Warn stream may be rejected if cumulative length of headers is too + large. */ +#define MAX_ACC 60000 /* <64KB to account for some overhead */ + { + size_t acc = 0; + + for(i = 0; i < nheader; ++i) { + acc += nva[i].namelen + nva[i].valuelen; + + infof(data, "h2h3 [%.*s: %.*s]", + (int)nva[i].namelen, nva[i].name, + (int)nva[i].valuelen, nva[i].value); + } + + if(acc > MAX_ACC) { + infof(data, "http_request: Warning: The cumulative length of all " + "headers exceeds %d bytes and that could cause the " + "stream to be rejected.", MAX_ACC); + } + } + + hreq->entries = nheader; + *hp = hreq; + + return CURLE_OK; + + fail: + free(hreq); + return CURLE_OUT_OF_MEMORY; +} + +void Curl_pseudo_free(struct h2h3req *hp) +{ + free(hp); +} + +#endif /* USE_NGHTTP2 or HTTP/3 enabled */ diff --git a/contrib/libs/curl/lib/h2h3.h b/contrib/libs/curl/lib/h2h3.h new file mode 100644 index 0000000000..84caec5d5e --- /dev/null +++ b/contrib/libs/curl/lib/h2h3.h @@ -0,0 +1,61 @@ +#ifndef HEADER_CURL_H2H3_H +#define HEADER_CURL_H2H3_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#define H2H3_PSEUDO_METHOD ":method" +#define H2H3_PSEUDO_SCHEME ":scheme" +#define H2H3_PSEUDO_AUTHORITY ":authority" +#define H2H3_PSEUDO_PATH ":path" +#define H2H3_PSEUDO_STATUS ":status" + +struct h2h3pseudo { + const char *name; + size_t namelen; + const char *value; + size_t valuelen; +}; + +struct h2h3req { + size_t entries; + struct h2h3pseudo header[1]; /* the array is allocated to contain entries */ +}; + +/* + * Curl_pseudo_headers() creates the array with pseudo headers to be + * used in a HTTP/2 or HTTP/3 request. Returns an allocated struct. + * Free it with Curl_pseudo_free(). + */ +CURLcode Curl_pseudo_headers(struct Curl_easy *data, + const char *request, + const size_t len, + struct h2h3req **hp); + +/* + * Curl_pseudo_free() frees a h2h3req struct. + */ +void Curl_pseudo_free(struct h2h3req *hp); + +#endif /* HEADER_CURL_H2H3_H */ diff --git a/contrib/libs/curl/lib/hash.c b/contrib/libs/curl/lib/hash.c index 30f28e2352..b6a2a33c72 100644 --- a/contrib/libs/curl/lib/hash.c +++ b/contrib/libs/curl/lib/hash.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -330,6 +330,7 @@ Curl_hash_next_element(struct Curl_hash_iterator *iter) struct Curl_hash_element *he = iter->current_element->ptr; return he; } + iter->current_element = NULL; return NULL; } diff --git a/contrib/libs/curl/lib/hash.h b/contrib/libs/curl/lib/hash.h index 9cfffc25b0..5b59bf1118 100644 --- a/contrib/libs/curl/lib/hash.h +++ b/contrib/libs/curl/lib/hash.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/headers.c b/contrib/libs/curl/lib/headers.c index 4367ce797c..978c918f4f 100644 --- a/contrib/libs/curl/lib/headers.c +++ b/contrib/libs/curl/lib/headers.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2022, 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 @@ -38,13 +38,14 @@ /* Generate the curl_header struct for the user. This function MUST assign all struct fields in the output struct. */ -static void copy_header_external(struct Curl_header_store *hs, +static void copy_header_external(struct Curl_easy *data, + struct Curl_header_store *hs, size_t index, size_t amount, struct Curl_llist_element *e, - struct curl_header *hout) + struct curl_header **hout) { - struct curl_header *h = hout; + struct curl_header *h = *hout = &data->state.headerout; h->name = hs->name; h->value = hs->value; h->amount = amount; @@ -117,9 +118,7 @@ CURLHcode curl_easy_header(CURL *easy, return CURLHE_MISSING; } /* this is the name we want */ - copy_header_external(hs, nameindex, amount, e_pick, - &data->state.headerout[0]); - *hout = &data->state.headerout[0]; + copy_header_external(data, hs, nameindex, amount, e_pick, hout); return CURLHE_OK; } @@ -133,6 +132,7 @@ struct curl_header *curl_easy_nextheader(CURL *easy, struct Curl_llist_element *pick; struct Curl_llist_element *e; struct Curl_header_store *hs; + struct curl_header *hout; size_t amount = 0; size_t index = 0; @@ -179,9 +179,8 @@ struct curl_header *curl_easy_nextheader(CURL *easy, index = amount - 1; } - copy_header_external(hs, index, amount, pick, - &data->state.headerout[1]); - return &data->state.headerout[1]; + copy_header_external(data, hs, index, amount, pick, &hout); + return hout; } static CURLcode namevalue(char *header, size_t hlen, unsigned int type, @@ -325,7 +324,7 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header, hs, &hs->node); data->state.prevhead = hs; return CURLE_OK; -fail: + fail: free(hs); return result; } @@ -336,7 +335,6 @@ fail: static void headers_init(struct Curl_easy *data) { Curl_llist_init(&data->state.httphdrs, NULL); - data->state.prevhead = NULL; } /* diff --git a/contrib/libs/curl/lib/headers.h b/contrib/libs/curl/lib/headers.h index a5229ea22f..96332dbd0b 100644 --- a/contrib/libs/curl/lib/headers.h +++ b/contrib/libs/curl/lib/headers.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/hmac.c b/contrib/libs/curl/lib/hmac.c index 8d8de1757d..dfb0db5757 100644 --- a/contrib/libs/curl/lib/hmac.c +++ b/contrib/libs/curl/lib/hmac.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/hostasyn.c b/contrib/libs/curl/lib/hostasyn.c index 2f6762ca4e..0bfbe2ef86 100644 --- a/contrib/libs/curl/lib/hostasyn.c +++ b/contrib/libs/curl/lib/hostasyn.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -43,6 +43,10 @@ #include <inet.h> #endif +#ifdef HAVE_PROCESS_H +#include <process.h> +#endif + #include "urldata.h" #include "sendf.h" #include "hostip.h" @@ -78,7 +82,7 @@ CURLcode Curl_addrinfo_callback(struct Curl_easy *data, Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); dns = Curl_cache_addr(data, ai, - data->state.async.hostname, 0, + data->state.async.hostname, data->state.async.port); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); diff --git a/contrib/libs/curl/lib/hostip.c b/contrib/libs/curl/lib/hostip.c index 0e27e0d1d9..5d087b7ff1 100644 --- a/contrib/libs/curl/lib/hostip.c +++ b/contrib/libs/curl/lib/hostip.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -48,6 +48,10 @@ #include <signal.h> #endif +#ifdef HAVE_PROCESS_H +#include <process.h> +#endif + #include "urldata.h" #include "sendf.h" #include "hostip.h" @@ -61,25 +65,23 @@ #include "doh.h" #include "warnless.h" #include "strcase.h" -#include "easy_lock.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" -#if defined(CURLRES_SYNCH) && \ - defined(HAVE_ALARM) && \ - defined(SIGALRM) && \ - defined(HAVE_SIGSETJMP) && \ - defined(GLOBAL_INIT_IS_THREADSAFE) +#if defined(ENABLE_IPV6) && defined(CURL_OSX_CALL_COPYPROXIES) +#include <SystemConfiguration/SCDynamicStoreCopySpecific.h> +#endif + +#if defined(CURLRES_SYNCH) && \ + defined(HAVE_ALARM) && defined(SIGALRM) && defined(HAVE_SIGSETJMP) /* alarm-based timeouts can only be used with all the dependencies satisfied */ #define USE_ALARM_TIMEOUT #endif #define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */ -#define MAX_DNS_CACHE_SIZE 29999 - /* * hostip.c explained * ================== @@ -124,7 +126,7 @@ static void freednsentry(void *freethis); /* * Return # of addresses in a Curl_addrinfo struct */ -static int num_addresses(const struct Curl_addrinfo *addr) +int Curl_num_addresses(const struct Curl_addrinfo *addr) { int i = 0; while(addr) { @@ -169,31 +171,23 @@ void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf, /* * Create a hostcache id string for the provided host + port, to be used by - * the DNS caching. Without alloc. Return length of the id string. + * the DNS caching. Without alloc. */ -static size_t -create_hostcache_id(const char *name, - size_t nlen, /* 0 or actual name length */ - int port, char *ptr, size_t buflen) +static void +create_hostcache_id(const char *name, int port, char *ptr, size_t buflen) { - size_t len = nlen ? nlen : strlen(name); - size_t olen = 0; - DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN); + size_t len = strlen(name); if(len > (buflen - 7)) len = buflen - 7; /* store and lower case the name */ - while(len--) { + while(len--) *ptr++ = Curl_raw_tolower(*name++); - olen++; - } - olen += msnprintf(ptr, 7, ":%u", port); - return olen; + msnprintf(ptr, 7, ":%u", port); } struct hostcache_prune_data { + long cache_timeout; time_t now; - time_t oldest; /* oldest time in cache not pruned. */ - int cache_timeout; }; /* @@ -206,40 +200,28 @@ struct hostcache_prune_data { static int hostcache_timestamp_remove(void *datap, void *hc) { - struct hostcache_prune_data *prune = + struct hostcache_prune_data *data = (struct hostcache_prune_data *) datap; struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc; - if(c->timestamp) { - /* age in seconds */ - time_t age = prune->now - c->timestamp; - if(age >= prune->cache_timeout) - return TRUE; - if(age > prune->oldest) - prune->oldest = age; - } - return FALSE; + return (0 != c->timestamp) + && (data->now - c->timestamp >= data->cache_timeout); } /* * Prune the DNS cache. This assumes that a lock has already been taken. - * Returns the 'age' of the oldest still kept entry. */ -static time_t -hostcache_prune(struct Curl_hash *hostcache, int cache_timeout, - time_t now) +static void +hostcache_prune(struct Curl_hash *hostcache, long cache_timeout, time_t now) { struct hostcache_prune_data user; user.cache_timeout = cache_timeout; user.now = now; - user.oldest = 0; Curl_hash_clean_with_criterium(hostcache, (void *) &user, hostcache_timestamp_remove); - - return user.oldest; } /* @@ -249,11 +231,10 @@ hostcache_prune(struct Curl_hash *hostcache, int cache_timeout, void Curl_hostcache_prune(struct Curl_easy *data) { time_t now; - /* the timeout may be set -1 (forever) */ - int timeout = data->set.dns_cache_timeout; - if(!data->dns.hostcache) - /* NULL hostcache means we can't do it */ + if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache) + /* cache forever means never prune, and NULL hostcache means + we can't do it */ return; if(data->share) @@ -261,29 +242,20 @@ void Curl_hostcache_prune(struct Curl_easy *data) time(&now); - do { - /* Remove outdated and unused entries from the hostcache */ - time_t oldest = hostcache_prune(data->dns.hostcache, timeout, now); - - if(oldest < INT_MAX) - timeout = (int)oldest; /* we know it fits */ - else - timeout = INT_MAX - 1; - - /* if the cache size is still too big, use the oldest age as new - prune limit */ - } while(timeout && (data->dns.hostcache->size > MAX_DNS_CACHE_SIZE)); + /* Remove outdated and unused entries from the hostcache */ + hostcache_prune(data->dns.hostcache, + data->set.dns_cache_timeout, + now); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); } -#ifdef USE_ALARM_TIMEOUT +#ifdef HAVE_SIGSETJMP /* Beware this is a global and unique instance. This is used to store the return address that we can jump back to from inside a signal handler. This is not thread-safe stuff. */ -static sigjmp_buf curl_jmpenv; -static curl_simple_lock curl_jmpenv_lock; +sigjmp_buf curl_jmpenv; #endif /* lookup address, returns entry if found and not stale */ @@ -292,18 +264,20 @@ static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data, int port) { struct Curl_dns_entry *dns = NULL; + size_t entry_len; char entry_id[MAX_HOSTCACHE_LEN]; /* Create an entry id, based upon the hostname and port */ - size_t entry_len = create_hostcache_id(hostname, 0, port, - entry_id, sizeof(entry_id)); + create_hostcache_id(hostname, port, entry_id, sizeof(entry_id)); + entry_len = strlen(entry_id); /* See if its already in our dns cache */ dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); /* No entry found in cache, check if we might have a wildcard entry */ if(!dns && data->state.wildcard_resolve) { - entry_len = create_hostcache_id("*", 1, port, entry_id, sizeof(entry_id)); + create_hostcache_id("*", port, entry_id, sizeof(entry_id)); + entry_len = strlen(entry_id); /* See if it's already in our dns cache */ dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); @@ -315,7 +289,6 @@ static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data, time(&user.now); user.cache_timeout = data->set.dns_cache_timeout; - user.oldest = 0; if(hostcache_timestamp_remove(&user, dns)) { infof(data, "Hostname in DNS cache was stale, zapped"); @@ -406,7 +379,7 @@ UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, struct Curl_addrinfo **addr) { CURLcode result = CURLE_OK; - const int num_addrs = num_addresses(*addr); + const int num_addrs = Curl_num_addresses(*addr); if(num_addrs > 1) { struct Curl_addrinfo **nodes; @@ -469,7 +442,6 @@ struct Curl_dns_entry * Curl_cache_addr(struct Curl_easy *data, struct Curl_addrinfo *addr, const char *hostname, - size_t hostlen, /* length or zero */ int port) { char entry_id[MAX_HOSTCACHE_LEN]; @@ -493,8 +465,8 @@ Curl_cache_addr(struct Curl_easy *data, } /* Create an entry id, based upon the hostname and port */ - entry_len = create_hostcache_id(hostname, hostlen, port, - entry_id, sizeof(entry_id)); + create_hostcache_id(hostname, port, entry_id, sizeof(entry_id)); + entry_len = strlen(entry_id); dns->inuse = 1; /* the cache has the first reference */ dns->addr = addr; /* this is the address(es) */ @@ -557,7 +529,6 @@ static struct Curl_addrinfo *get_localhost6(int port, const char *name) static struct Curl_addrinfo *get_localhost(int port, const char *name) { struct Curl_addrinfo *ca; - struct Curl_addrinfo *ca6; const size_t ss_size = sizeof(struct sockaddr_in); const size_t hostlen = strlen(name); struct sockaddr_in sa; @@ -584,12 +555,8 @@ static struct Curl_addrinfo *get_localhost(int port, const char *name) memcpy(ca->ai_addr, &sa, ss_size); ca->ai_canonname = (char *)ca->ai_addr + ss_size; strcpy(ca->ai_canonname, name); - - ca6 = get_localhost6(port, name); - if(!ca6) - return ca; - ca6->ai_next = ca; - return ca6; + ca->ai_next = get_localhost6(port, name); + return ca; } #ifdef ENABLE_IPV6 @@ -683,14 +650,6 @@ enum resolve_t Curl_resolv(struct Curl_easy *data, CURLcode result; enum resolve_t rc = CURLRESOLV_ERROR; /* default to failure */ struct connectdata *conn = data->conn; - /* We should intentionally error and not resolve .onion TLDs */ - size_t hostname_len = strlen(hostname); - if(hostname_len >= 7 && - (curl_strequal(&hostname[hostname_len - 6], ".onion") || - curl_strequal(&hostname[hostname_len - 7], ".onion."))) { - failf(data, "Not resolving .onion address (RFC 7686)"); - return CURLRESOLV_ERROR; - } *entry = NULL; #ifndef CURL_DISABLE_DOH conn->bits.doh = FALSE; /* default is not */ @@ -744,6 +703,23 @@ enum resolve_t Curl_resolv(struct Curl_easy *data, return CURLRESOLV_ERROR; } +#if defined(ENABLE_IPV6) && defined(CURL_OSX_CALL_COPYPROXIES) + { + /* + * The automagic conversion from IPv4 literals to IPv6 literals only + * works if the SCDynamicStoreCopyProxies system function gets called + * first. As Curl currently doesn't support system-wide HTTP proxies, we + * therefore don't use any value this function might return. + * + * This function is only available on a macOS and is not needed for + * IPv4-only builds, hence the conditions above. + */ + CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL); + if(dict) + CFRelease(dict); + } +#endif + #ifndef USE_RESOLVE_ON_IPS /* First check if this is an IPv4 address string */ if(Curl_inet_pton(AF_INET, hostname, &in) > 0) @@ -819,7 +795,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data, 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, addr, hostname, 0, port); + dns = Curl_cache_addr(data, addr, hostname, port); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); @@ -846,6 +822,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data, static void alarmfunc(int sig) { + /* this is for "-ansi -Wall -pedantic" to stop complaining! (rabe) */ (void)sig; siglongjmp(curl_jmpenv, 1); } @@ -925,8 +902,6 @@ enum resolve_t Curl_resolv_timeout(struct Curl_easy *data, This should be the last thing we do before calling Curl_resolv(), as otherwise we'd have to worry about variables that get modified before we invoke Curl_resolv() (and thus use "volatile"). */ - curl_simple_lock_lock(&curl_jmpenv_lock); - if(sigsetjmp(curl_jmpenv, 1)) { /* this is coming from a siglongjmp() after an alarm signal */ failf(data, "name lookup timed out"); @@ -995,8 +970,6 @@ clean_up: #endif #endif /* HAVE_SIGACTION */ - curl_simple_lock_unlock(&curl_jmpenv_lock); - /* switch back the alarm() to either zero or to what it was before minus the time we spent until now! */ if(prev_alarm) { @@ -1090,7 +1063,8 @@ void Curl_hostcache_clean(struct Curl_easy *data, CURLcode Curl_loadhostpairs(struct Curl_easy *data) { struct curl_slist *hostp; - char *host_end; + char hostname[256]; + int port = 0; /* Default is no wildcard found */ data->state.wildcard_resolve = false; @@ -1100,25 +1074,18 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) if(!hostp->data) continue; if(hostp->data[0] == '-') { - unsigned long num = 0; size_t entry_len; - size_t hlen = 0; - host_end = strchr(&hostp->data[1], ':'); - - if(host_end) { - hlen = host_end - &hostp->data[1]; - num = strtoul(++host_end, NULL, 10); - if(!hlen || (num > 0xffff)) - host_end = NULL; - } - if(!host_end) { - infof(data, "Bad syntax CURLOPT_RESOLVE removal entry '%s'", + + if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) { + infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'", hostp->data); continue; } + /* Create an entry id, based upon the hostname and port */ - entry_len = create_hostcache_id(&hostp->data[1], hlen, (int)num, - entry_id, sizeof(entry_id)); + create_hostcache_id(hostname, port, entry_id, sizeof(entry_id)); + entry_len = strlen(entry_id); + if(data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); @@ -1139,22 +1106,25 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) char *addr_begin; char *addr_end; char *port_ptr; - int port = 0; char *end_ptr; bool permanent = TRUE; + char *host_begin; + char *host_end; unsigned long tmp_port; bool error = true; - char *host_begin = hostp->data; - size_t hlen = 0; + host_begin = hostp->data; if(host_begin[0] == '+') { host_begin++; permanent = FALSE; } host_end = strchr(host_begin, ':'); - if(!host_end) + if(!host_end || + ((host_end - host_begin) >= (ptrdiff_t)sizeof(hostname))) goto err; - hlen = host_end - host_begin; + + memcpy(hostname, host_begin, host_end - host_begin); + hostname[host_end - host_begin] = '\0'; port_ptr = host_end + 1; tmp_port = strtoul(port_ptr, &end_ptr, 10); @@ -1221,7 +1191,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) goto err; error = false; -err: + err: if(error) { failf(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'", hostp->data); @@ -1230,8 +1200,8 @@ err: } /* Create an entry id, based upon the hostname and port */ - entry_len = create_hostcache_id(host_begin, hlen, port, - entry_id, sizeof(entry_id)); + create_hostcache_id(hostname, port, entry_id, sizeof(entry_id)); + entry_len = strlen(entry_id); if(data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); @@ -1240,8 +1210,8 @@ err: dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); if(dns) { - infof(data, "RESOLVE %.*s:%d is - old addresses discarded", - (int)hlen, host_begin, port); + infof(data, "RESOLVE %s:%d is - old addresses discarded", + hostname, port); /* delete old entry, there are two reasons for this 1. old entry may have different addresses. 2. even if entry with correct addresses is already in the cache, @@ -1257,7 +1227,7 @@ err: } /* put this new host in the cache */ - dns = Curl_cache_addr(data, head, host_begin, hlen, port); + dns = Curl_cache_addr(data, head, hostname, port); if(dns) { if(permanent) dns->timestamp = 0; /* mark as permanent */ @@ -1273,13 +1243,13 @@ err: Curl_freeaddrinfo(head); return CURLE_OUT_OF_MEMORY; } - infof(data, "Added %.*s:%d:%s to DNS cache%s", - (int)hlen, host_begin, port, addresses, - permanent ? "" : " (non-permanent)"); + infof(data, "Added %s:%d:%s to DNS cache%s", + hostname, port, addresses, permanent ? "" : " (non-permanent)"); /* Wildcard hostname */ - if((hlen == 1) && (host_begin[0] == '*')) { - infof(data, "RESOLVE *:%d using wildcard", port); + if(hostname[0] == '*' && hostname[1] == '\0') { + infof(data, "RESOLVE %s:%d is wildcard, enabling wildcard checks", + hostname, port); data->state.wildcard_resolve = true; } } diff --git a/contrib/libs/curl/lib/hostip.h b/contrib/libs/curl/lib/hostip.h index 06d0867277..9d3170737c 100644 --- a/contrib/libs/curl/lib/hostip.h +++ b/contrib/libs/curl/lib/hostip.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -34,6 +34,11 @@ #include <setjmp.h> #endif +#ifdef NETWARE +#undef in_addr_t +#define in_addr_t unsigned long +#endif + /* Allocate enough memory to hold the full name information structs and * everything. OSF1 is known to require at least 8872 bytes. The buffer * required for storing all possible aliases and IP numbers is according to @@ -132,6 +137,9 @@ void Curl_init_dnscache(struct Curl_hash *hash, int hashsize); /* prune old entries from the DNS cache */ void Curl_hostcache_prune(struct Curl_easy *data); +/* Return # of addresses in a Curl_addrinfo struct */ +int Curl_num_addresses(const struct Curl_addrinfo *addr); + /* IPv4 threadsafe resolve function used for synch and asynch builds */ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port); @@ -175,7 +183,7 @@ Curl_fetch_addr(struct Curl_easy *data, */ struct Curl_dns_entry * Curl_cache_addr(struct Curl_easy *data, struct Curl_addrinfo *addr, - const char *hostname, size_t hostlen, int port); + const char *hostname, int port); #ifndef INADDR_NONE #define CURL_INADDR_NONE (in_addr_t) ~0 @@ -183,6 +191,15 @@ Curl_cache_addr(struct Curl_easy *data, struct Curl_addrinfo *addr, #define CURL_INADDR_NONE INADDR_NONE #endif +#ifdef HAVE_SIGSETJMP +/* Forward-declaration of variable defined in hostip.c. Beware this + * is a global and unique instance. This is used to store the return + * address that we can jump back to from inside a signal handler. + * This is not thread-safe stuff. + */ +extern sigjmp_buf curl_jmpenv; +#endif + /* * Function provided by the resolver backend to set DNS servers to use. */ diff --git a/contrib/libs/curl/lib/hostip4.c b/contrib/libs/curl/lib/hostip4.c index 9140180ffd..1dd54e879d 100644 --- a/contrib/libs/curl/lib/hostip4.c +++ b/contrib/libs/curl/lib/hostip4.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -43,6 +43,10 @@ #include <inet.h> #endif +#ifdef HAVE_PROCESS_H +#include <process.h> +#endif + #include "urldata.h" #include "sendf.h" #include "hostip.h" @@ -121,15 +125,14 @@ struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data, struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port) { -#if !(defined(HAVE_GETADDRINFO) && defined(HAVE_GETADDRINFO_THREADSAFE)) && \ - defined(HAVE_GETHOSTBYNAME_R_3) +#if !defined(HAVE_GETADDRINFO_THREADSAFE) && defined(HAVE_GETHOSTBYNAME_R_3) int res; #endif struct Curl_addrinfo *ai = NULL; struct hostent *h = NULL; struct hostent *buf = NULL; -#if defined(HAVE_GETADDRINFO) && defined(HAVE_GETADDRINFO_THREADSAFE) +#if defined(HAVE_GETADDRINFO_THREADSAFE) struct addrinfo hints; char sbuf[12]; char *sbufptr = NULL; @@ -277,16 +280,14 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, h = NULL; /* set return code to NULL */ free(buf); } -#else /* (HAVE_GETADDRINFO && HAVE_GETADDRINFO_THREADSAFE) || - HAVE_GETHOSTBYNAME_R */ +#else /* HAVE_GETADDRINFO_THREADSAFE || HAVE_GETHOSTBYNAME_R */ /* * Here is code for platforms that don't have a thread safe * getaddrinfo() nor gethostbyname_r() function or for which * gethostbyname() is the preferred one. */ h = gethostbyname((void *)hostname); -#endif /* (HAVE_GETADDRINFO && HAVE_GETADDRINFO_THREADSAFE) || - HAVE_GETHOSTBYNAME_R */ +#endif /* HAVE_GETADDRINFO_THREADSAFE || HAVE_GETHOSTBYNAME_R */ if(h) { ai = Curl_he2ai(h, port); diff --git a/contrib/libs/curl/lib/hostip6.c b/contrib/libs/curl/lib/hostip6.c index 6b0ba55e9f..c62c254c72 100644 --- a/contrib/libs/curl/lib/hostip6.c +++ b/contrib/libs/curl/lib/hostip6.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -43,6 +43,10 @@ #include <inet.h> #endif +#ifdef HAVE_PROCESS_H +#include <process.h> +#endif + #include "urldata.h" #include "sendf.h" #include "hostip.h" diff --git a/contrib/libs/curl/lib/hostsyn.c b/contrib/libs/curl/lib/hostsyn.c index ca8b0758c4..ee54363bf9 100644 --- a/contrib/libs/curl/lib/hostsyn.c +++ b/contrib/libs/curl/lib/hostsyn.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -43,6 +43,10 @@ #include <inet.h> #endif +#ifdef HAVE_PROCESS_H +#include <process.h> +#endif + #include "urldata.h" #include "sendf.h" #include "hostip.h" diff --git a/contrib/libs/curl/lib/hsts.c b/contrib/libs/curl/lib/hsts.c index 7ecf0042a5..e3b686ebfa 100644 --- a/contrib/libs/curl/lib/hsts.c +++ b/contrib/libs/curl/lib/hsts.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2020 - 2022, 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 @@ #include "parsedate.h" #include "fopen.h" #include "rename.h" -#include "share.h" +#include "strtoofft.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -57,7 +57,7 @@ /* to play well with debug builds, we can *set* a fixed time this will return */ time_t deltatime; /* allow for "adjustments" for unit test purposes */ -static time_t hsts_debugtime(void *unused) +static time_t debugtime(void *unused) { char *timestr = getenv("CURL_TIME"); (void)unused; @@ -70,8 +70,7 @@ static time_t hsts_debugtime(void *unused) } return time(NULL); } -#undef time -#define time(x) hsts_debugtime(x) +#define time(x) debugtime(x) #endif struct hsts *Curl_hsts_init(void) @@ -159,7 +158,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, do { while(*p && ISBLANK(*p)) p++; - if(strncasecompare("max-age=", p, 8)) { + if(Curl_strncasecompare("max-age=", p, 8)) { bool quoted = FALSE; CURLofft offt; char *endp; @@ -188,7 +187,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, } gotma = TRUE; } - else if(strncasecompare("includesubdomains", p, 17)) { + else if(Curl_strncasecompare("includesubdomains", p, 17)) { if(gotinc) return CURLE_BAD_FUNCTION_ARGUMENT; subdomains = TRUE; @@ -205,7 +204,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, p++; if(*p == ';') p++; - } while(*p); + } while (*p); if(!gotma) /* max-age is mandatory */ @@ -279,11 +278,11 @@ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, if(ntail < hlen) { size_t offs = hlen - ntail; if((hostname[offs-1] == '.') && - strncasecompare(&hostname[offs], sts->host, ntail)) + Curl_strncasecompare(&hostname[offs], sts->host, ntail)) return sts; } } - if(strcasecompare(hostname, sts->host)) + if(Curl_strcasecompare(hostname, sts->host)) return sts; } } @@ -391,7 +390,7 @@ CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h, unlink(tempstore); } free(tempstore); -skipsave: + skipsave: if(data->set.hsts_write) { /* if there's a write callback */ struct curl_index i; /* count */ @@ -427,23 +426,14 @@ static CURLcode hsts_add(struct hsts *h, char *line) if(2 == rc) { time_t expires = strcmp(date, UNLIMITED) ? Curl_getdate_capped(date) : TIME_T_MAX; - CURLcode result = CURLE_OK; + CURLcode result; char *p = host; bool subdomain = FALSE; - struct stsentry *e; if(p[0] == '.') { p++; subdomain = TRUE; } - /* only add it if not already present */ - e = Curl_hsts(h, p, subdomain); - if(!e) - result = hsts_create(h, p, subdomain, expires); - else { - /* the same host name, use the largest expire time */ - if(expires > e->expires) - e->expires = expires; - } + result = hsts_create(h, p, subdomain, expires); if(result) return result; } @@ -535,7 +525,7 @@ static CURLcode hsts_load(struct hsts *h, const char *file) } return result; -fail: + fail: Curl_safefree(h->filename); fclose(fp); return CURLE_OUT_OF_MEMORY; @@ -562,18 +552,4 @@ CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h) return CURLE_OK; } -void Curl_hsts_loadfiles(struct Curl_easy *data) -{ - struct curl_slist *l = data->set.hstslist; - if(l) { - Curl_share_lock(data, CURL_LOCK_DATA_HSTS, CURL_LOCK_ACCESS_SINGLE); - - while(l) { - (void)Curl_hsts_loadfile(data, data->hsts, l->data); - l = l->next; - } - Curl_share_unlock(data, CURL_LOCK_DATA_HSTS); - } -} - #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */ diff --git a/contrib/libs/curl/lib/hsts.h b/contrib/libs/curl/lib/hsts.h index d3431a5d7a..0e36a7756b 100644 --- a/contrib/libs/curl/lib/hsts.h +++ b/contrib/libs/curl/lib/hsts.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2020 - 2022, 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 @@ -59,11 +59,9 @@ CURLcode Curl_hsts_loadfile(struct Curl_easy *data, struct hsts *h, const char *file); CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h); -void Curl_hsts_loadfiles(struct Curl_easy *data); #else #define Curl_hsts_cleanup(x) #define Curl_hsts_loadcb(x,y) CURLE_OK #define Curl_hsts_save(x,y,z) -#define Curl_hsts_loadfiles(x) #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */ #endif /* HEADER_CURL_HSTS_H */ diff --git a/contrib/libs/curl/lib/http.c b/contrib/libs/curl/lib/http.c index 7df4488c0e..ea0a53632d 100644 --- a/contrib/libs/curl/lib/http.c +++ b/contrib/libs/curl/lib/http.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -62,7 +62,6 @@ #include "cookie.h" #include "vauth/vauth.h" #include "vtls/vtls.h" -#include "vquic/vquic.h" #include "http_digest.h" #include "http_ntlm.h" #include "curl_ntlm_wb.h" @@ -71,7 +70,6 @@ #include "url.h" #include "share.h" #include "hostip.h" -#include "dynhds.h" #include "http.h" #include "select.h" #include "parsedate.h" /* for the week day and month names */ @@ -82,14 +80,12 @@ #include "http_proxy.h" #include "warnless.h" #include "http2.h" -#include "cfilters.h" #include "connect.h" #include "strdup.h" #include "altsvc.h" #include "hsts.h" #include "ws.h" #include "c-hyper.h" -#include "curl_ctype.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -105,6 +101,18 @@ static int http_getsock_do(struct Curl_easy *data, curl_socket_t *socks); static bool http_should_fail(struct Curl_easy *data); +#ifndef CURL_DISABLE_PROXY +static CURLcode add_haproxy_protocol_header(struct Curl_easy *data); +#endif + +#ifdef USE_SSL +static CURLcode https_connecting(struct Curl_easy *data, bool *done); +static int https_getsock(struct Curl_easy *data, + struct connectdata *conn, + curl_socket_t *socks); +#else +#define https_connecting(x,y) CURLE_COULDNT_CONNECT +#endif static CURLcode http_setup_conn(struct Curl_easy *data, struct connectdata *conn); #ifdef USE_WEBSOCKETS @@ -153,7 +161,7 @@ const struct Curl_handler Curl_handler_ws = { http_getsock_do, /* doing_getsock */ ZERO_NULL, /* domore_getsock */ ZERO_NULL, /* perform_getsock */ - Curl_ws_disconnect, /* disconnect */ + ZERO_NULL, /* disconnect */ ZERO_NULL, /* readwrite */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ @@ -176,9 +184,9 @@ const struct Curl_handler Curl_handler_https = { Curl_http_done, /* done */ ZERO_NULL, /* do_more */ Curl_http_connect, /* connect_it */ - NULL, /* connecting */ + https_connecting, /* connecting */ ZERO_NULL, /* doing */ - NULL, /* proto_getsock */ + https_getsock, /* proto_getsock */ http_getsock_do, /* doing_getsock */ ZERO_NULL, /* domore_getsock */ ZERO_NULL, /* perform_getsock */ @@ -201,13 +209,13 @@ const struct Curl_handler Curl_handler_wss = { Curl_http_done, /* done */ ZERO_NULL, /* do_more */ Curl_http_connect, /* connect_it */ - NULL, /* connecting */ + https_connecting, /* connecting */ ZERO_NULL, /* doing */ - NULL, /* proto_getsock */ + https_getsock, /* proto_getsock */ http_getsock_do, /* doing_getsock */ ZERO_NULL, /* domore_getsock */ ZERO_NULL, /* perform_getsock */ - Curl_ws_disconnect, /* disconnect */ + ZERO_NULL, /* disconnect */ ZERO_NULL, /* readwrite */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ @@ -233,16 +241,25 @@ static CURLcode http_setup_conn(struct Curl_easy *data, if(!http) return CURLE_OUT_OF_MEMORY; - Curl_mime_initpart(&http->form); + Curl_mime_initpart(&http->form, data); data->req.p.http = http; - connkeep(conn, "HTTP default"); - if(data->state.httpwant == CURL_HTTP_VERSION_3ONLY) { - CURLcode result = Curl_conn_may_http3(data, conn); - if(result) - return result; + if(data->state.httpwant == CURL_HTTP_VERSION_3) { + if(conn->handler->flags & PROTOPT_SSL) + /* Only go HTTP/3 directly on HTTPS URLs. It needs a UDP socket and does + the QUIC dance. */ + conn->transport = TRNSPRT_QUIC; + else { + failf(data, "HTTP/3 requested for non-HTTPS URL"); + return CURLE_URL_MALFORMAT; + } + } + else { + if(!CONN_INUSE(conn)) + /* if not already multi-using, setup connection details */ + Curl_http2_setup_conn(conn); + Curl_http2_setup_req(data); } - return CURLE_OK; } @@ -398,7 +415,7 @@ static CURLcode http_output_basic(struct Curl_easy *data, bool proxy) goto fail; } -fail: + fail: free(out); return result; } @@ -424,7 +441,7 @@ static CURLcode http_output_bearer(struct Curl_easy *data) goto fail; } -fail: + fail: return result; } @@ -538,7 +555,7 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data, } } - data->state.rewindbeforesend = FALSE; /* default */ + conn->bits.rewindaftersend = FALSE; /* default */ if((expectsend == -1) || (expectsend > bytessent)) { #if defined(USE_NTLM) @@ -555,8 +572,8 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data, /* rewind data when completely done sending! */ if(!conn->bits.authneg && (conn->writesockfd != CURL_SOCKET_BAD)) { - data->state.rewindbeforesend = TRUE; - infof(data, "Rewind stream before next send"); + conn->bits.rewindaftersend = TRUE; + infof(data, "Rewind stream after send"); } return CURLE_OK; @@ -583,8 +600,8 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data, /* rewind data when completely done sending! */ if(!conn->bits.authneg && (conn->writesockfd != CURL_SOCKET_BAD)) { - data->state.rewindbeforesend = TRUE; - infof(data, "Rewind stream before next send"); + conn->bits.rewindaftersend = TRUE; + infof(data, "Rewind stream after send"); } return CURLE_OK; @@ -608,11 +625,9 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data, closure so we can safely do the rewind right now */ } - if(bytessent) { - /* mark for rewind since if we already sent something */ - data->state.rewindbeforesend = TRUE; - infof(data, "Please rewind output before next send"); - } + if(bytessent) + /* we rewind now at once since if we already sent something */ + return Curl_readrewind(data); return CURLE_OK; } @@ -635,7 +650,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) if(!data->set.str[STRING_BEARER]) authmask &= (unsigned long)~CURLAUTH_BEARER; - if(100 <= data->req.httpcode && data->req.httpcode <= 199) + if(100 <= data->req.httpcode && 199 >= data->req.httpcode) /* this is a transient response code, ignore */ return CURLE_OK; @@ -669,7 +684,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) if(pickhost || pickproxy) { if((data->state.httpreq != HTTPREQ_GET) && (data->state.httpreq != HTTPREQ_HEAD) && - !data->state.rewindbeforesend) { + !conn->bits.rewindaftersend) { result = http_perhapsrewind(data, conn); if(result) return result; @@ -1010,7 +1025,7 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, if(authp->picked == CURLAUTH_NEGOTIATE) { CURLcode result = Curl_input_negotiate(data, conn, proxy, auth); if(!result) { - free(data->req.newurl); + DEBUGASSERT(!data->req.newurl); data->req.newurl = strdup(data->state.url); if(!data->req.newurl) return CURLE_OUT_OF_MEMORY; @@ -1218,15 +1233,15 @@ static size_t readmoredata(char *buffer, size_t nitems, void *userp) { - struct HTTP *http = (struct HTTP *)userp; - struct Curl_easy *data = http->backup.data; + struct Curl_easy *data = (struct Curl_easy *)userp; + struct HTTP *http = data->req.p.http; size_t fullsize = size * nitems; if(!http->postsize) /* nothing to return */ return 0; - /* make sure that an HTTP request is never sent away chunked! */ + /* make sure that a HTTP request is never sent away chunked! */ data->req.forbidchunk = (http->sending == HTTPSEND_REQUEST)?TRUE:FALSE; if(data->set.max_send_speed && @@ -1271,7 +1286,6 @@ static size_t readmoredata(char *buffer, */ CURLcode Curl_buffer_send(struct dynbuf *in, struct Curl_easy *data, - struct HTTP *http, /* add the number of sent bytes to this counter */ curl_off_t *bytes_written, @@ -1284,13 +1298,14 @@ CURLcode Curl_buffer_send(struct dynbuf *in, char *ptr; size_t size; struct connectdata *conn = data->conn; + struct HTTP *http = data->req.p.http; size_t sendsize; curl_socket_t sockfd; size_t headersize; DEBUGASSERT(socketindex <= SECONDARYSOCKET); - sockfd = Curl_conn_get_socket(data, socketindex); + sockfd = conn->sock[socketindex]; /* The looping below is required since we use non-blocking sockets, but due to the circumstances we will just loop and try again and again etc */ @@ -1305,10 +1320,10 @@ CURLcode Curl_buffer_send(struct dynbuf *in, if((conn->handler->flags & PROTOPT_SSL #ifndef CURL_DISABLE_PROXY - || IS_HTTPS_PROXY(conn->http_proxy.proxytype) + || conn->http_proxy.proxytype == CURLPROXY_HTTPS #endif ) - && conn->httpversion < 20) { + && conn->httpversion != 20) { /* Make sure this doesn't send more body bytes than what the max send speed says. The request bytes do not count to the max speed. */ @@ -1418,11 +1433,10 @@ CURLcode Curl_buffer_send(struct dynbuf *in, http->backup.fread_in = data->state.in; http->backup.postdata = http->postdata; http->backup.postsize = http->postsize; - http->backup.data = data; /* set the new pointers for the request-sending */ data->state.fread_func = (curl_read_callback)readmoredata; - data->state.in = (void *)http; + data->state.in = (void *)data; http->postdata = ptr; http->postsize = (curl_off_t)size; @@ -1431,6 +1445,7 @@ CURLcode Curl_buffer_send(struct dynbuf *in, http->send_buffer = *in; /* copy the whole struct */ http->sending = HTTPSEND_REQUEST; + return CURLE_OK; } http->sending = HTTPSEND_BODY; @@ -1524,13 +1539,48 @@ Curl_compareheader(const char *headerline, /* line to check */ */ CURLcode Curl_http_connect(struct Curl_easy *data, bool *done) { + CURLcode result; struct connectdata *conn = data->conn; /* We default to persistent connections. We set this already in this connect function to make the re-use checks properly be able to check this bit. */ connkeep(conn, "HTTP default"); - return Curl_conn_connect(data, FIRSTSOCKET, FALSE, done); +#ifndef CURL_DISABLE_PROXY + /* the CONNECT procedure might not have been completed */ + result = Curl_proxy_connect(data, FIRSTSOCKET); + if(result) + return result; + + if(conn->bits.proxy_connect_closed) + /* this is not an error, just part of the connection negotiation */ + return CURLE_OK; + + if(CONNECT_FIRSTSOCKET_PROXY_SSL()) + return CURLE_OK; /* wait for HTTPS proxy SSL initialization to complete */ + + if(Curl_connect_ongoing(conn)) + /* nothing else to do except wait right now - we're not done here. */ + return CURLE_OK; + + if(data->set.haproxyprotocol) { + /* add HAProxy PROXY protocol header */ + result = add_haproxy_protocol_header(data); + if(result) + return result; + } +#endif + + if(conn->given->flags & PROTOPT_SSL) { + /* perform SSL initialization */ + result = https_connecting(data, done); + if(result) + return result; + } + else + *done = TRUE; + + return CURLE_OK; } /* this returns the socket to wait for in the DO and DOING state for the multi @@ -1541,11 +1591,80 @@ static int http_getsock_do(struct Curl_easy *data, curl_socket_t *socks) { /* write mode */ - (void)conn; - socks[0] = Curl_conn_get_socket(data, FIRSTSOCKET); + (void)data; + socks[0] = conn->sock[FIRSTSOCKET]; return GETSOCK_WRITESOCK(0); } +#ifndef CURL_DISABLE_PROXY +static CURLcode add_haproxy_protocol_header(struct Curl_easy *data) +{ + struct dynbuf req; + CURLcode result; + const char *tcp_version; + DEBUGASSERT(data->conn); + Curl_dyn_init(&req, DYN_HAXPROXY); + +#ifdef USE_UNIX_SOCKETS + if(data->conn->unix_domain_socket) + /* the buffer is large enough to hold this! */ + result = Curl_dyn_addn(&req, STRCONST("PROXY UNKNOWN\r\n")); + else { +#endif + /* Emit the correct prefix for IPv6 */ + tcp_version = data->conn->bits.ipv6 ? "TCP6" : "TCP4"; + + result = Curl_dyn_addf(&req, "PROXY %s %s %s %i %i\r\n", + tcp_version, + data->info.conn_local_ip, + data->info.conn_primary_ip, + data->info.conn_local_port, + data->info.conn_primary_port); + +#ifdef USE_UNIX_SOCKETS + } +#endif + + if(!result) + result = Curl_buffer_send(&req, data, &data->info.request_size, + 0, FIRSTSOCKET); + return result; +} +#endif + +#ifdef USE_SSL +static CURLcode https_connecting(struct Curl_easy *data, bool *done) +{ + CURLcode result; + struct connectdata *conn = data->conn; + DEBUGASSERT((data) && (data->conn->handler->flags & PROTOPT_SSL)); + +#ifdef ENABLE_QUIC + if(conn->transport == TRNSPRT_QUIC) { + *done = TRUE; + return CURLE_OK; + } +#endif + + /* perform SSL initialization for this socket */ + result = Curl_ssl_connect_nonblocking(data, conn, FALSE, FIRSTSOCKET, done); + if(result) + connclose(conn, "Failed HTTPS connection"); + + return result; +} + +static int https_getsock(struct Curl_easy *data, + struct connectdata *conn, + curl_socket_t *socks) +{ + (void)data; + if(conn->handler->flags & PROTOPT_SSL) + return Curl_ssl->getsock(conn, socks); + return GETSOCK_BLANK; +} +#endif /* USE_SSL */ + /* * Curl_http_done() gets called after a single HTTP request has been * performed. @@ -1572,6 +1691,8 @@ CURLcode Curl_http_done(struct Curl_easy *data, return CURLE_OK; Curl_dyn_free(&http->send_buffer); + Curl_http2_done(data, premature); + Curl_quic_done(data, premature); Curl_mime_cleanpart(&http->form); Curl_dyn_reset(&data->state.headerb); Curl_hyper_done(data); @@ -1624,10 +1745,17 @@ bool Curl_use_http_1_1plus(const struct Curl_easy *data, static const char *get_http_string(const struct Curl_easy *data, const struct connectdata *conn) { - if(Curl_conn_is_http3(data, conn, FIRSTSOCKET)) +#ifdef ENABLE_QUIC + if((data->state.httpwant == CURL_HTTP_VERSION_3) || + (conn->httpversion == 30)) return "3"; - if(Curl_conn_is_http2(data, conn, FIRSTSOCKET)) +#endif + +#ifdef USE_NGHTTP2 + if(conn->proto.httpc.h2) return "2"; +#endif + if(Curl_use_http_1_1plus(data, conn)) return "1.1"; @@ -1714,157 +1842,6 @@ CURLcode Curl_http_compile_trailers(struct curl_slist *trailers, return result; } -static bool hd_name_eq(const char *n1, size_t n1len, - const char *n2, size_t n2len) -{ - if(n1len == n2len) { - return strncasecompare(n1, n2, n1len); - } - return FALSE; -} - -CURLcode Curl_dynhds_add_custom(struct Curl_easy *data, - bool is_connect, - struct dynhds *hds) -{ - struct connectdata *conn = data->conn; - char *ptr; - struct curl_slist *h[2]; - struct curl_slist *headers; - int numlists = 1; /* by default */ - int i; - -#ifndef CURL_DISABLE_PROXY - enum proxy_use proxy; - - if(is_connect) - proxy = HEADER_CONNECT; - else - proxy = conn->bits.httpproxy && !conn->bits.tunnel_proxy? - HEADER_PROXY:HEADER_SERVER; - - switch(proxy) { - case HEADER_SERVER: - h[0] = data->set.headers; - break; - case HEADER_PROXY: - h[0] = data->set.headers; - if(data->set.sep_headers) { - h[1] = data->set.proxyheaders; - numlists++; - } - break; - case HEADER_CONNECT: - if(data->set.sep_headers) - h[0] = data->set.proxyheaders; - else - h[0] = data->set.headers; - break; - } -#else - (void)is_connect; - h[0] = data->set.headers; -#endif - - /* loop through one or two lists */ - for(i = 0; i < numlists; i++) { - for(headers = h[i]; headers; headers = headers->next) { - const char *name, *value; - size_t namelen, valuelen; - - /* There are 2 quirks in place for custom headers: - * 1. setting only 'name:' to suppress a header from being sent - * 2. setting only 'name;' to send an empty (illegal) header - */ - ptr = strchr(headers->data, ':'); - if(ptr) { - name = headers->data; - namelen = ptr - headers->data; - ptr++; /* pass the colon */ - while(*ptr && ISSPACE(*ptr)) - ptr++; - if(*ptr) { - value = ptr; - valuelen = strlen(value); - } - else { - /* quirk #1, suppress this header */ - continue; - } - } - else { - ptr = strchr(headers->data, ';'); - - if(!ptr) { - /* neither : nor ; in provided header value. We seem - * to ignore this silently */ - continue; - } - - name = headers->data; - namelen = ptr - headers->data; - ptr++; /* pass the semicolon */ - while(*ptr && ISSPACE(*ptr)) - ptr++; - if(!*ptr) { - /* quirk #2, send an empty header */ - value = ""; - valuelen = 0; - } - else { - /* this may be used for something else in the future, - * ignore this for now */ - continue; - } - } - - DEBUGASSERT(name && value); - if(data->state.aptr.host && - /* a Host: header was sent already, don't pass on any custom Host: - header as that will produce *two* in the same request! */ - hd_name_eq(name, namelen, STRCONST("Host:"))) - ; - else if(data->state.httpreq == HTTPREQ_POST_FORM && - /* this header (extended by formdata.c) is sent later */ - hd_name_eq(name, namelen, STRCONST("Content-Type:"))) - ; - else if(data->state.httpreq == HTTPREQ_POST_MIME && - /* this header is sent later */ - hd_name_eq(name, namelen, STRCONST("Content-Type:"))) - ; - else if(conn->bits.authneg && - /* while doing auth neg, don't allow the custom length since - we will force length zero then */ - hd_name_eq(name, namelen, STRCONST("Content-Length:"))) - ; - else if(data->state.aptr.te && - /* when asking for Transfer-Encoding, don't pass on a custom - Connection: */ - hd_name_eq(name, namelen, STRCONST("Connection:"))) - ; - else if((conn->httpversion >= 20) && - hd_name_eq(name, namelen, STRCONST("Transfer-Encoding:"))) - /* HTTP/2 doesn't support chunked requests */ - ; - else if((hd_name_eq(name, namelen, STRCONST("Authorization:")) || - hd_name_eq(name, namelen, STRCONST("Cookie:"))) && - /* be careful of sending this potentially sensitive header to - other hosts */ - !Curl_auth_allowed_to_host(data)) - ; - else { - CURLcode result; - - result = Curl_dynhds_add(hds, name, namelen, value, valuelen); - if(result) - return result; - } - } - } - - return CURLE_OK; -} - CURLcode Curl_add_custom_headers(struct Curl_easy *data, bool is_connect, #ifndef USE_HYPER @@ -2112,14 +2089,14 @@ void Curl_http_method(struct Curl_easy *data, struct connectdata *conn, Curl_HttpReq httpreq = (Curl_HttpReq)data->state.httpreq; const char *request; if((conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_FTP)) && - data->state.upload) + data->set.upload) httpreq = HTTPREQ_PUT; /* Now set the 'request' pointer to the proper request string */ if(data->set.str[STRING_CUSTOMREQUEST]) request = data->set.str[STRING_CUSTOMREQUEST]; else { - if(data->req.no_body) + if(data->set.opt_no_body) request = "HEAD"; else { DEBUGASSERT((httpreq >= HTTPREQ_GET) && (httpreq <= HTTPREQ_HEAD)); @@ -2163,9 +2140,8 @@ CURLcode Curl_http_useragent(struct Curl_easy *data) CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn) { const char *ptr; - struct dynamically_allocated_data *aptr = &data->state.aptr; if(!data->state.this_is_a_follow) { - /* Free to avoid leaking memory on multiple requests */ + /* Free to avoid leaking memory on multiple requests*/ free(data->state.first_host); data->state.first_host = strdup(conn->host.name); @@ -2175,7 +2151,7 @@ CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn) data->state.first_remote_port = conn->remote_port; data->state.first_remote_protocol = conn->handler->protocol; } - Curl_safefree(aptr->host); + Curl_safefree(data->state.aptr.host); ptr = Curl_checkheaders(data, STRCONST("Host")); if(ptr && (!data->state.this_is_a_follow || @@ -2210,16 +2186,19 @@ CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn) if(colon) *colon = 0; /* The host must not include an embedded port number */ } - Curl_safefree(aptr->cookiehost); - aptr->cookiehost = cookiehost; + Curl_safefree(data->state.aptr.cookiehost); + data->state.aptr.cookiehost = cookiehost; } #endif if(strcmp("Host:", ptr)) { - aptr->host = aprintf("Host:%s\r\n", &ptr[5]); - if(!aptr->host) + data->state.aptr.host = aprintf("Host:%s\r\n", &ptr[5]); + if(!data->state.aptr.host) return CURLE_OUT_OF_MEMORY; } + else + /* when clearing the header */ + data->state.aptr.host = NULL; } else { /* When building Host: headers, we must put the host name within @@ -2232,14 +2211,18 @@ CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn) (conn->remote_port == PORT_HTTP)) ) /* if(HTTPS on port 443) OR (HTTP on port 80) then don't include the port number in the host string */ - aptr->host = aprintf("Host: %s%s%s\r\n", conn->bits.ipv6_ip?"[":"", - host, conn->bits.ipv6_ip?"]":""); + data->state.aptr.host = aprintf("Host: %s%s%s\r\n", + conn->bits.ipv6_ip?"[":"", + host, + conn->bits.ipv6_ip?"]":""); else - aptr->host = aprintf("Host: %s%s%s:%d\r\n", conn->bits.ipv6_ip?"[":"", - host, conn->bits.ipv6_ip?"]":"", - conn->remote_port); + data->state.aptr.host = aprintf("Host: %s%s%s:%d\r\n", + conn->bits.ipv6_ip?"[":"", + host, + conn->bits.ipv6_ip?"]":"", + conn->remote_port); - if(!aptr->host) + if(!data->state.aptr.host) /* without Host: we can't make a nice request */ return CURLE_OUT_OF_MEMORY; } @@ -2401,7 +2384,7 @@ CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn, cthdr = "multipart/form-data"; curl_mime_headers(http->sendit, data->set.headers, 0); - result = Curl_mime_prepare_headers(data, http->sendit, cthdr, + result = Curl_mime_prepare_headers(http->sendit, cthdr, NULL, MIMESTRATEGY_FORM); curl_mime_headers(http->sendit, NULL, 0); if(!result) @@ -2423,7 +2406,7 @@ CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn, if((conn->handler->protocol & PROTO_FAMILY_HTTP) && (((httpreq == HTTPREQ_POST_MIME || httpreq == HTTPREQ_POST_FORM) && http->postsize < 0) || - ((data->state.upload || httpreq == HTTPREQ_POST) && + ((data->set.upload || httpreq == HTTPREQ_POST) && data->state.infilesize == -1))) { if(conn->bits.authneg) /* don't enable chunked during auth neg */ @@ -2457,7 +2440,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, curl_off_t included_body = 0; #else /* from this point down, this function should not be used */ -#define Curl_buffer_send(a,b,c,d,e,f) CURLE_OK +#define Curl_buffer_send(a,b,c,d,e) CURLE_OK #endif CURLcode result = CURLE_OK; struct HTTP *http = data->req.p.http; @@ -2486,16 +2469,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, return result; } - /* For really small puts we don't use Expect: headers at all, and for - the somewhat bigger ones we allow the app to disable it. Just make - sure that the expect100header is always set to the preferred value - here. */ - ptr = Curl_checkheaders(data, STRCONST("Expect")); - if(ptr) { - data->state.expect100header = - Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue")); - } - else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) { + if(http->postsize) { result = expect100(data, conn, r); if(result) return result; @@ -2510,8 +2484,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, Curl_pgrsSetUploadSize(data, http->postsize); /* this sends the buffer and frees all the buffer resources */ - result = Curl_buffer_send(r, data, data->req.p.http, - &data->info.request_size, 0, + result = Curl_buffer_send(r, data, &data->info.request_size, 0, FIRSTSOCKET); if(result) failf(data, "Failed sending PUT request"); @@ -2532,8 +2505,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, if(result) return result; - result = Curl_buffer_send(r, data, data->req.p.http, - &data->info.request_size, 0, + result = Curl_buffer_send(r, data, &data->info.request_size, 0, FIRSTSOCKET); if(result) failf(data, "Failed sending POST request"); @@ -2549,7 +2521,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, we don't upload data chunked, as RFC2616 forbids us to set both kinds of headers (Transfer-Encoding: chunked and Content-Length) */ if(http->postsize != -1 && !data->req.upload_chunky && - (!Curl_checkheaders(data, STRCONST("Content-Length")))) { + (conn->bits.authneg || + !Curl_checkheaders(data, STRCONST("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_dyn_addf(r, @@ -2603,8 +2576,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, http->sending = HTTPSEND_BODY; /* this sends the buffer and frees all the buffer resources */ - result = Curl_buffer_send(r, data, data->req.p.http, - &data->info.request_size, 0, + result = Curl_buffer_send(r, data, &data->info.request_size, 0, FIRSTSOCKET); if(result) failf(data, "Failed sending POST request"); @@ -2667,7 +2639,11 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, #ifndef USE_HYPER /* With Hyper the body is always passed on separately */ if(data->set.postfields) { - if(!data->state.expect100header && + + /* In HTTP2, we send request body in DATA frame regardless of + its size. */ + if(conn->httpversion != 20 && + !data->state.expect100header && (http->postsize < MAX_INITIAL_POST_SIZE)) { /* if we don't use expect: 100 AND postsize is less than MAX_INITIAL_POST_SIZE @@ -2717,10 +2693,11 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, else { /* A huge POST coming up, do data separate from the request */ http->postdata = data->set.postfields; + http->sending = HTTPSEND_BODY; - http->backup.data = data; + data->state.fread_func = (curl_read_callback)readmoredata; - data->state.in = (void *)http; + data->state.in = (void *)data; /* set the upload size to the progress meter */ Curl_pgrsSetUploadSize(data, http->postsize); @@ -2759,8 +2736,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, } } /* issue the request */ - result = Curl_buffer_send(r, data, data->req.p.http, - &data->info.request_size, included_body, + result = Curl_buffer_send(r, data, &data->info.request_size, included_body, FIRSTSOCKET); if(result) @@ -2776,8 +2752,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, return result; /* issue the request */ - result = Curl_buffer_send(r, data, data->req.p.http, - &data->info.request_size, 0, + result = Curl_buffer_send(r, data, &data->info.request_size, 0, FIRSTSOCKET); if(result) failf(data, "Failed sending HTTP request"); @@ -2820,7 +2795,7 @@ CURLcode Curl_http_cookies(struct Curl_easy *data, conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) || strcasecompare("localhost", host) || !strcmp(host, "127.0.0.1") || - !strcmp(host, "::1") ? TRUE : FALSE; + !strcmp(host, "[::1]") ? TRUE : FALSE; Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); co = Curl_cookie_getlist(data, data->cookies, host, data->state.up.path, secure_context); @@ -2828,18 +2803,16 @@ CURLcode Curl_http_cookies(struct Curl_easy *data, } if(co) { struct Cookie *store = co; - size_t clen = 8; /* hold the size of the generated Cookie: header */ /* now loop through all cookies that matched */ while(co) { if(co->value) { - size_t add; - if(!count) { + if(0 == count) { result = Curl_dyn_addn(r, STRCONST("Cookie: ")); if(result) break; } - add = strlen(co->name) + strlen(co->value) + 1; - if(clen + add >= MAX_COOKIE_HEADER_LEN) { + if((Curl_dyn_len(r) + strlen(co->name) + strlen(co->value) + 1) >= + MAX_COOKIE_HEADER_LEN) { infof(data, "Restricted outgoing cookies due to header size, " "'%s' not sent", co->name); linecap = TRUE; @@ -2849,7 +2822,6 @@ CURLcode Curl_http_cookies(struct Curl_easy *data, co->name, co->value); if(result) break; - clen += add + (count ? 2 : 0); count++; } co = co->next; /* next cookie please */ @@ -2953,8 +2925,8 @@ CURLcode Curl_http_resume(struct Curl_easy *data, data->state.resume_from = 0; } - if(data->state.resume_from && !data->state.followlocation) { - /* only act on the first request */ + if(data->state.resume_from && !data->state.this_is_a_follow) { + /* do we still game? */ /* Now, let's read off the proper amount of bytes from the input. */ @@ -3055,14 +3027,14 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data, if(data->set.timecondition && !data->state.range) { /* A time condition has been set AND no ranges have been requested. This seems to be what chapter 13.3.4 of RFC 2616 defines to be the correct - action for an HTTP/1.1 client */ + action for a HTTP/1.1 client */ if(!Curl_meets_timecondition(data, k->timeofdoc)) { *done = TRUE; - /* We're simulating an HTTP 304 from server so we return + /* We're simulating a http 304 from server so we return what should have been returned from the server */ data->info.httpcode = 304; - infof(data, "Simulate an HTTP 304 response"); + infof(data, "Simulate a HTTP 304 response"); /* we abort the transfer before it is completed == we ruin the re-use ability. Close the connection */ streamclose(conn, "Simulated 304 handling"); @@ -3108,7 +3080,7 @@ CURLcode Curl_transferencode(struct Curl_easy *data) #ifndef USE_HYPER /* - * Curl_http() gets called from the generic multi_do() function when an HTTP + * Curl_http() gets called from the generic multi_do() function when a HTTP * request is to be performed. This creates and sends a properly constructed * HTTP request. */ @@ -3130,37 +3102,50 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) the rest of the request in the PERFORM phase. */ *done = TRUE; - switch(conn->alpn) { - case CURL_HTTP_VERSION_3: - DEBUGASSERT(Curl_conn_is_http3(data, conn, FIRSTSOCKET)); - break; - case CURL_HTTP_VERSION_2: + if(conn->transport != TRNSPRT_QUIC) { + if(conn->httpversion < 20) { /* unless the connection is re-used and + already http2 */ + switch(conn->alpn) { + case CURL_HTTP_VERSION_2: + conn->httpversion = 20; /* we know we're on HTTP/2 now */ + + result = Curl_http2_switched(data, NULL, 0); + if(result) + return result; + break; + case CURL_HTTP_VERSION_1_1: + /* continue with HTTP/1.1 when explicitly requested */ + break; + default: + /* Check if user wants to use HTTP/2 with clear TCP*/ +#ifdef USE_NGHTTP2 + if(data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) { #ifndef CURL_DISABLE_PROXY - if(!Curl_conn_is_http2(data, conn, FIRSTSOCKET) && - conn->bits.proxy && !conn->bits.tunnel_proxy - ) { - result = Curl_http2_switch(data, conn, FIRSTSOCKET); - if(result) - return result; - } - else + if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { + /* We don't support HTTP/2 proxies yet. Also it's debatable + whether or not this setting should apply to HTTP/2 proxies. */ + infof(data, "Ignoring HTTP/2 prior knowledge due to proxy"); + break; + } #endif - DEBUGASSERT(Curl_conn_is_http2(data, conn, FIRSTSOCKET)); - break; - case CURL_HTTP_VERSION_1_1: - /* continue with HTTP/1.1 when explicitly requested */ - break; - default: - /* Check if user wants to use HTTP/2 with clear TCP */ - if(Curl_http2_may_switch(data, conn, FIRSTSOCKET)) { - DEBUGF(infof(data, "HTTP/2 over clean TCP")); - result = Curl_http2_switch(data, conn, FIRSTSOCKET); + DEBUGF(infof(data, "HTTP/2 over clean TCP")); + conn->httpversion = 20; + + result = Curl_http2_switched(data, NULL, 0); + if(result) + return result; + } +#endif + break; + } + } + else { + /* prepare for a http2 request */ + result = Curl_http2_setup(data, conn); if(result) return result; } - break; } - http = data->req.p.http; DEBUGASSERT(http); @@ -3320,7 +3305,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) } if(!(conn->handler->flags&PROTOPT_SSL) && - conn->httpversion < 20 && + conn->httpversion != 20 && (data->state.httpwant == CURL_HTTP_VERSION_2)) { /* append HTTP2 upgrade magic stuff to the HTTP request if it isn't done over SSL */ @@ -3332,10 +3317,8 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) } result = Curl_http_cookies(data, conn, &req); -#ifdef USE_WEBSOCKETS if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS)) result = Curl_ws_request(data, &req); -#endif if(!result) result = Curl_add_timecondition(data, &req); if(!result) @@ -3380,10 +3363,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) } } - if(data->req.upload_done) - Curl_conn_ev_data_done_send(data); - - if((conn->httpversion >= 20) && data->req.upload_chunky) + if((conn->httpversion == 20) && data->req.upload_chunky) /* upload_chunky was set above to set up the request in a chunky fashion, but is disabled here again to avoid that the chunked encoded version is actually used when sending the request body over h2 */ @@ -3517,7 +3497,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, STRCONST("Proxy-Connection:"), STRCONST("keep-alive"))) { /* - * When an HTTP/1.0 reply comes when using a proxy, the + * When a HTTP/1.0 reply comes when using a proxy, the * 'Proxy-Connection: keep-alive' line tells us the * connection will be kept alive for our pleasure. * Default action for 1.0 is to close. @@ -3531,7 +3511,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, STRCONST("Proxy-Connection:"), STRCONST("close"))) { /* - * We get an HTTP/1.1 response from a proxy and it says it'll + * We get a HTTP/1.1 response from a proxy and it says it'll * close down after this transfer. */ connclose(conn, "Proxy-Connection: asked to close after done"); @@ -3543,7 +3523,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, STRCONST("Connection:"), STRCONST("keep-alive"))) { /* - * An HTTP/1.0 reply with the 'Connection: keep-alive' line + * A HTTP/1.0 reply with the 'Connection: keep-alive' line * tells us the connection will be kept alive for our * pleasure. Default action for 1.0 is to close. * @@ -3578,7 +3558,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, TRUE); if(result) return result; - if(!k->chunk && data->set.http_transfer_encoding) { + if(!k->chunk) { /* if this isn't chunked, only close can signal the end of this transfer as Content-Length is said not to be trusted for transfer-encoding! */ connclose(conn, "HTTP/1.1 transfer-encoding without chunks"); @@ -3654,7 +3634,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) || strcasecompare("localhost", host) || !strcmp(host, "127.0.0.1") || - !strcmp(host, "::1") ? TRUE : FALSE; + !strcmp(host, "[::1]") ? TRUE : FALSE; Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); @@ -3728,9 +3708,6 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, result = http_perhapsrewind(data, conn); if(result) return result; - - /* mark the next request as a followed location: */ - data->state.this_is_a_follow = TRUE; } } } @@ -3747,7 +3724,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, #endif )) { CURLcode check = - Curl_hsts_parse(data->hsts, conn->host.name, + Curl_hsts_parse(data->hsts, data->state.up.hostname, headp + strlen("Strict-Transport-Security:")); if(check) infof(data, "Illegal STS header skipped"); @@ -3770,12 +3747,11 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, #endif )) { /* the ALPN of the current request */ - enum alpnid id = (conn->httpversion == 30)? ALPN_h3 : - (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1; + enum alpnid id = (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1; result = Curl_altsvc_parse(data, data->asi, headp + strlen("Alt-Svc:"), id, conn->host.name, - curlx_uitous((unsigned int)conn->remote_port)); + curlx_uitous(conn->remote_port)); if(result) return result; } @@ -4036,7 +4012,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, switch(k->httpcode) { case 100: /* - * We have made an HTTP PUT or POST and this is 1.1-lingo + * We have made a HTTP PUT or POST and this is 1.1-lingo * that tells us that the server is OK with this and ready * to receive the data. * However, we'll get more headers now so we must get @@ -4065,8 +4041,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, /* switch to http2 now. The bytes after response headers are also processed here, otherwise they are lost. */ - result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, - k->str, *nread); + result = Curl_http2_switched(data, k->str, *nread); if(result) return result; *nread = 0; @@ -4074,7 +4049,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, #ifdef USE_WEBSOCKETS else if(k->upgr101 == UPGR101_WS) { /* verify the response */ - result = Curl_ws_accept(data, k->str, *nread); + result = Curl_ws_accept(data); if(result) return result; k->header = FALSE; /* no more header to parse! */ @@ -4201,7 +4176,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if(k->httpcode >= 300) { if((!conn->bits.authneg) && !conn->bits.close && - !data->state.rewindbeforesend) { + !conn->bits.rewindaftersend) { /* * General treatment of errors when about to send data. Including : * "417 Expectation Failed", while waiting for 100-continue. @@ -4211,7 +4186,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, * something else should've considered the big picture and we * avoid this check. * - * rewindbeforesend indicates that something has told libcurl to + * rewindaftersend indicates that something has told libcurl to * continue sending even if it gets discarded */ @@ -4260,9 +4235,9 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, } } - if(data->state.rewindbeforesend && - (conn->writesockfd != CURL_SOCKET_BAD)) { - /* We rewind before next send, continue sending now */ + if(conn->bits.rewindaftersend) { + /* We rewind after a complete send, so thus we continue + sending now */ infof(data, "Keep sending data to get tossed away"); k->keepon |= KEEP_SEND; } @@ -4275,7 +4250,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, * If we requested a "no body", this is a good time to get * out and return home. */ - if(data->req.no_body) + if(data->set.opt_no_body) *stop_reading = TRUE; #ifndef CURL_DISABLE_RTSP else if((conn->handler->protocol & CURLPROTO_RTSP) && @@ -4294,8 +4269,11 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, stream. In order to do this, we keep reading until we close the stream. */ if(0 == k->maxdownload - && !Curl_conn_is_http2(data, conn, FIRSTSOCKET) - && !Curl_conn_is_http3(data, conn, FIRSTSOCKET)) +#if defined(USE_NGHTTP2) + && !((conn->handler->protocol & PROTO_FAMILY_HTTP) && + conn->httpversion == 20) +#endif + ) *stop_reading = TRUE; if(*stop_reading) { @@ -4320,7 +4298,11 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if(!k->headerline++) { /* This is the first header, it MUST be the error code line or else we consider this to be the body right away! */ - bool fine_statusline = FALSE; + int httpversion_major; + int rtspversion_major; + int nc = 0; +#define HEADER1 headp /* no conversion needed, just use headp */ + if(conn->handler->protocol & PROTO_FAMILY_HTTP) { /* * https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2 @@ -4329,60 +4311,39 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, * says. We allow any three-digit number here, but we cannot make * guarantees on future behaviors since it isn't within the protocol. */ + char separator; + char twoorthree[2]; int httpversion = 0; - char *p = headp; - - while(*p && ISBLANK(*p)) - p++; - if(!strncmp(p, "HTTP/", 5)) { - p += 5; - switch(*p) { - case '1': - p++; - if((p[0] == '.') && (p[1] == '0' || p[1] == '1')) { - if(ISBLANK(p[2])) { - httpversion = 10 + (p[1] - '0'); - p += 3; - if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) { - k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 + - (p[2] - '0'); - p += 3; - if(ISSPACE(*p)) - fine_statusline = TRUE; - } - } - } - if(!fine_statusline) { - failf(data, "Unsupported HTTP/1 subversion in response"); - return CURLE_UNSUPPORTED_PROTOCOL; - } - break; - case '2': - case '3': - if(!ISBLANK(p[1])) - break; - httpversion = (*p - '0') * 10; - p += 2; - if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) { - k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 + - (p[2] - '0'); - p += 3; - if(!ISSPACE(*p)) - break; - fine_statusline = TRUE; - } - break; - default: /* unsupported */ - failf(data, "Unsupported HTTP version in response"); - return CURLE_UNSUPPORTED_PROTOCOL; - } + char digit4 = 0; + nc = sscanf(HEADER1, + " HTTP/%1d.%1d%c%3d%c", + &httpversion_major, + &httpversion, + &separator, + &k->httpcode, + &digit4); + + if(nc == 1 && httpversion_major >= 2 && + 2 == sscanf(HEADER1, " HTTP/%1[23] %d", twoorthree, &k->httpcode)) { + conn->httpversion = 0; + nc = 4; + separator = ' '; } - if(fine_statusline) { - if(k->httpcode < 100) { - failf(data, "Unsupported response code in HTTP response"); - return CURLE_UNSUPPORTED_PROTOCOL; - } + /* There can only be a 4th response code digit stored in 'digit4' if + all the other fields were parsed and stored first, so nc is 5 when + digit4 a digit. + + The sscanf() line above will also allow zero-prefixed and negative + numbers, so we check for that too here. + */ + else if(ISDIGIT(digit4) || (nc >= 4 && k->httpcode < 100)) { + failf(data, "Unsupported response code in HTTP response"); + return CURLE_UNSUPPORTED_PROTOCOL; + } + + if((nc >= 4) && (' ' == separator)) { + httpversion += 10 * httpversion_major; switch(httpversion) { case 10: case 11: @@ -4407,52 +4368,54 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, } if(conn->httpversion < 20) { conn->bundle->multiuse = BUNDLE_NO_MULTIUSE; + infof(data, "Mark bundle as not supporting multiuse"); } } - else { + else if(!nc) { + /* this is the real world, not a Nirvana + NCSA 1.5.x returns this crap when asked for HTTP/1.1 + */ + nc = sscanf(HEADER1, " HTTP %3d", &k->httpcode); + conn->httpversion = 10; + /* If user has set option HTTP200ALIASES, compare header line against list of aliases */ - statusline check = - checkhttpprefix(data, - Curl_dyn_ptr(&data->state.headerb), - Curl_dyn_len(&data->state.headerb)); - if(check == STATUS_DONE) { - fine_statusline = TRUE; - k->httpcode = 200; - conn->httpversion = 10; + if(!nc) { + statusline check = + checkhttpprefix(data, + Curl_dyn_ptr(&data->state.headerb), + Curl_dyn_len(&data->state.headerb)); + if(check == STATUS_DONE) { + nc = 1; + k->httpcode = 200; + conn->httpversion = 10; + } } } + else { + failf(data, "Unsupported HTTP version in response"); + return CURLE_UNSUPPORTED_PROTOCOL; + } } else if(conn->handler->protocol & CURLPROTO_RTSP) { - char *p = headp; - while(*p && ISBLANK(*p)) - p++; - if(!strncmp(p, "RTSP/", 5)) { - p += 5; - if(ISDIGIT(*p)) { - p++; - if((p[0] == '.') && ISDIGIT(p[1])) { - if(ISBLANK(p[2])) { - p += 3; - if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) { - k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 + - (p[2] - '0'); - p += 3; - if(ISSPACE(*p)) { - fine_statusline = TRUE; - conn->httpversion = 11; /* RTSP acts like HTTP 1.1 */ - } - } - } - } - } - if(!fine_statusline) - return CURLE_WEIRD_SERVER_REPLY; + char separator; + int rtspversion; + nc = sscanf(HEADER1, + " RTSP/%1d.%1d%c%3d", + &rtspversion_major, + &rtspversion, + &separator, + &k->httpcode); + if((nc == 4) && (' ' == separator)) { + conn->httpversion = 11; /* For us, RTSP acts like HTTP 1.1 */ + } + else { + nc = 0; } } - if(fine_statusline) { + if(nc) { result = Curl_http_statusline(data, conn); if(result) return result; @@ -4502,385 +4465,4 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, return CURLE_OK; } - -/* Decode HTTP status code string. */ -CURLcode Curl_http_decode_status(int *pstatus, const char *s, size_t len) -{ - CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT; - int status = 0; - int i; - - if(len != 3) - goto out; - - for(i = 0; i < 3; ++i) { - char c = s[i]; - - if(c < '0' || c > '9') - goto out; - - status *= 10; - status += c - '0'; - } - result = CURLE_OK; -out: - *pstatus = result? -1 : status; - return result; -} - -/* simple implementation of strndup(), which isn't portable */ -static char *my_strndup(const char *ptr, size_t len) -{ - char *copy = malloc(len + 1); - if(!copy) - return NULL; - memcpy(copy, ptr, len); - copy[len] = '\0'; - return copy; -} - -CURLcode Curl_http_req_make(struct httpreq **preq, - const char *method, size_t m_len, - const char *scheme, size_t s_len, - const char *authority, size_t a_len, - const char *path, size_t p_len) -{ - struct httpreq *req; - CURLcode result = CURLE_OUT_OF_MEMORY; - - DEBUGASSERT(method); - if(m_len + 1 >= sizeof(req->method)) - return CURLE_BAD_FUNCTION_ARGUMENT; - - req = calloc(1, sizeof(*req)); - if(!req) - goto out; - memcpy(req->method, method, m_len); - if(scheme) { - req->scheme = my_strndup(scheme, s_len); - if(!req->scheme) - goto out; - } - if(authority) { - req->authority = my_strndup(authority, a_len); - if(!req->authority) - goto out; - } - if(path) { - req->path = my_strndup(path, p_len); - if(!req->path) - goto out; - } - Curl_dynhds_init(&req->headers, 0, DYN_HTTP_REQUEST); - Curl_dynhds_init(&req->trailers, 0, DYN_HTTP_REQUEST); - result = CURLE_OK; - -out: - if(result && req) - Curl_http_req_free(req); - *preq = result? NULL : req; - return result; -} - -static CURLcode req_assign_url_authority(struct httpreq *req, CURLU *url) -{ - char *user, *pass, *host, *port; - struct dynbuf buf; - CURLUcode uc; - CURLcode result = CURLE_URL_MALFORMAT; - - user = pass = host = port = NULL; - Curl_dyn_init(&buf, DYN_HTTP_REQUEST); - - uc = curl_url_get(url, CURLUPART_HOST, &host, 0); - if(uc && uc != CURLUE_NO_HOST) - goto out; - if(!host) { - req->authority = NULL; - result = CURLE_OK; - goto out; - } - - uc = curl_url_get(url, CURLUPART_PORT, &port, CURLU_NO_DEFAULT_PORT); - if(uc && uc != CURLUE_NO_PORT) - goto out; - uc = curl_url_get(url, CURLUPART_USER, &user, 0); - if(uc && uc != CURLUE_NO_USER) - goto out; - if(user) { - uc = curl_url_get(url, CURLUPART_PASSWORD, &pass, 0); - if(uc && uc != CURLUE_NO_PASSWORD) - goto out; - } - - if(user) { - result = Curl_dyn_add(&buf, user); - if(result) - goto out; - if(pass) { - result = Curl_dyn_addf(&buf, ":%s", pass); - if(result) - goto out; - } - result = Curl_dyn_add(&buf, "@"); - if(result) - goto out; - } - result = Curl_dyn_add(&buf, host); - if(result) - goto out; - if(port) { - result = Curl_dyn_addf(&buf, ":%s", port); - if(result) - goto out; - } - req->authority = strdup(Curl_dyn_ptr(&buf)); - if(!req->authority) - goto out; - result = CURLE_OK; - -out: - free(user); - free(pass); - free(host); - free(port); - Curl_dyn_free(&buf); - return result; -} - -static CURLcode req_assign_url_path(struct httpreq *req, CURLU *url) -{ - char *path, *query; - struct dynbuf buf; - CURLUcode uc; - CURLcode result = CURLE_URL_MALFORMAT; - - path = query = NULL; - Curl_dyn_init(&buf, DYN_HTTP_REQUEST); - - uc = curl_url_get(url, CURLUPART_PATH, &path, CURLU_PATH_AS_IS); - if(uc) - goto out; - uc = curl_url_get(url, CURLUPART_QUERY, &query, 0); - if(uc && uc != CURLUE_NO_QUERY) - goto out; - - if(!path && !query) { - req->path = NULL; - } - else if(path && !query) { - req->path = path; - path = NULL; - } - else { - if(path) { - result = Curl_dyn_add(&buf, path); - if(result) - goto out; - } - if(query) { - result = Curl_dyn_addf(&buf, "?%s", query); - if(result) - goto out; - } - req->path = strdup(Curl_dyn_ptr(&buf)); - if(!req->path) - goto out; - } - result = CURLE_OK; - -out: - free(path); - free(query); - Curl_dyn_free(&buf); - return result; -} - -CURLcode Curl_http_req_make2(struct httpreq **preq, - const char *method, size_t m_len, - CURLU *url, const char *scheme_default) -{ - struct httpreq *req; - CURLcode result = CURLE_OUT_OF_MEMORY; - CURLUcode uc; - - DEBUGASSERT(method); - if(m_len + 1 >= sizeof(req->method)) - return CURLE_BAD_FUNCTION_ARGUMENT; - - req = calloc(1, sizeof(*req)); - if(!req) - goto out; - memcpy(req->method, method, m_len); - - uc = curl_url_get(url, CURLUPART_SCHEME, &req->scheme, 0); - if(uc && uc != CURLUE_NO_SCHEME) - goto out; - if(!req->scheme && scheme_default) { - req->scheme = strdup(scheme_default); - if(!req->scheme) - goto out; - } - - result = req_assign_url_authority(req, url); - if(result) - goto out; - result = req_assign_url_path(req, url); - if(result) - goto out; - - Curl_dynhds_init(&req->headers, 0, DYN_HTTP_REQUEST); - Curl_dynhds_init(&req->trailers, 0, DYN_HTTP_REQUEST); - result = CURLE_OK; - -out: - if(result && req) - Curl_http_req_free(req); - *preq = result? NULL : req; - return result; -} - -void Curl_http_req_free(struct httpreq *req) -{ - if(req) { - free(req->scheme); - free(req->authority); - free(req->path); - Curl_dynhds_free(&req->headers); - Curl_dynhds_free(&req->trailers); - free(req); - } -} - -struct name_const { - const char *name; - size_t namelen; -}; - -static struct name_const H2_NON_FIELD[] = { - { STRCONST("Host") }, - { STRCONST("Upgrade") }, - { STRCONST("Connection") }, - { STRCONST("Keep-Alive") }, - { STRCONST("Proxy-Connection") }, - { STRCONST("Transfer-Encoding") }, -}; - -static bool h2_non_field(const char *name, size_t namelen) -{ - size_t i; - for(i = 0; i < sizeof(H2_NON_FIELD)/sizeof(H2_NON_FIELD[0]); ++i) { - if(namelen < H2_NON_FIELD[i].namelen) - return FALSE; - if(namelen == H2_NON_FIELD[i].namelen && - strcasecompare(H2_NON_FIELD[i].name, name)) - return TRUE; - } - return FALSE; -} - -CURLcode Curl_http_req_to_h2(struct dynhds *h2_headers, - struct httpreq *req, struct Curl_easy *data) -{ - const char *scheme = NULL, *authority = NULL; - struct dynhds_entry *e; - size_t i; - CURLcode result; - - DEBUGASSERT(req); - DEBUGASSERT(h2_headers); - - if(req->scheme) { - scheme = req->scheme; - } - else if(strcmp("CONNECT", req->method)) { - scheme = Curl_checkheaders(data, STRCONST(HTTP_PSEUDO_SCHEME)); - if(scheme) { - scheme += sizeof(HTTP_PSEUDO_SCHEME); - while(*scheme && ISBLANK(*scheme)) - scheme++; - infof(data, "set pseudo header %s to %s", HTTP_PSEUDO_SCHEME, scheme); - } - else { - scheme = (data->conn && data->conn->handler->flags & PROTOPT_SSL)? - "https" : "http"; - } - } - - if(req->authority) { - authority = req->authority; - } - else { - e = Curl_dynhds_get(&req->headers, STRCONST("Host")); - if(e) - authority = e->value; - } - - Curl_dynhds_reset(h2_headers); - Curl_dynhds_set_opts(h2_headers, DYNHDS_OPT_LOWERCASE); - result = Curl_dynhds_add(h2_headers, STRCONST(HTTP_PSEUDO_METHOD), - req->method, strlen(req->method)); - if(!result && scheme) { - result = Curl_dynhds_add(h2_headers, STRCONST(HTTP_PSEUDO_SCHEME), - scheme, strlen(scheme)); - } - if(!result && authority) { - result = Curl_dynhds_add(h2_headers, STRCONST(HTTP_PSEUDO_AUTHORITY), - authority, strlen(authority)); - } - if(!result && req->path) { - result = Curl_dynhds_add(h2_headers, STRCONST(HTTP_PSEUDO_PATH), - req->path, strlen(req->path)); - } - for(i = 0; !result && i < Curl_dynhds_count(&req->headers); ++i) { - e = Curl_dynhds_getn(&req->headers, i); - if(!h2_non_field(e->name, e->namelen)) { - result = Curl_dynhds_add(h2_headers, e->name, e->namelen, - e->value, e->valuelen); - } - } - - return result; -} - -CURLcode Curl_http_resp_make(struct http_resp **presp, - int status, - const char *description) -{ - struct http_resp *resp; - CURLcode result = CURLE_OUT_OF_MEMORY; - - resp = calloc(1, sizeof(*resp)); - if(!resp) - goto out; - - resp->status = status; - if(description) { - resp->description = strdup(description); - if(!resp->description) - goto out; - } - Curl_dynhds_init(&resp->headers, 0, DYN_HTTP_REQUEST); - Curl_dynhds_init(&resp->trailers, 0, DYN_HTTP_REQUEST); - result = CURLE_OK; - -out: - if(result && resp) - Curl_http_resp_free(resp); - *presp = result? NULL : resp; - return result; -} - -void Curl_http_resp_free(struct http_resp *resp) -{ - if(resp) { - free(resp->description); - Curl_dynhds_free(&resp->headers); - Curl_dynhds_free(&resp->trailers); - if(resp->prev) - Curl_http_resp_free(resp->prev); - free(resp); - } -} - #endif /* CURL_DISABLE_HTTP */ diff --git a/contrib/libs/curl/lib/http.h b/contrib/libs/curl/lib/http.h index df3b4e38b8..f7cbb34244 100644 --- a/contrib/libs/curl/lib/http.h +++ b/contrib/libs/curl/lib/http.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,13 +24,6 @@ * ***************************************************************************/ #include "curl_setup.h" - -#if defined(USE_MSH3) && !defined(_WIN32) -#include <pthread.h> -#endif - -#include "bufq.h" -#include "dynhds.h" #include "ws.h" typedef enum { @@ -44,7 +37,11 @@ typedef enum { #ifndef CURL_DISABLE_HTTP -#if defined(ENABLE_QUIC) +#ifdef USE_NGHTTP2 +#include <nghttp2/nghttp2.h> +#endif + +#if defined(_WIN32) && defined(ENABLE_QUIC) #include <stdint.h> #endif @@ -62,7 +59,6 @@ extern const struct Curl_handler Curl_handler_wss; #endif #endif /* websockets */ -struct dynhds; /* Header specific functions */ bool Curl_compareheader(const char *headerline, /* line to check */ @@ -77,10 +73,8 @@ char *Curl_checkProxyheaders(struct Curl_easy *data, const struct connectdata *conn, const char *thisheader, const size_t thislen); -struct HTTP; /* see below */ CURLcode Curl_buffer_send(struct dynbuf *in, struct Curl_easy *data, - struct HTTP *http, curl_off_t *bytes_written, curl_off_t included_body_bytes, int socketindex); @@ -100,10 +94,6 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, void *headers #endif ); -CURLcode Curl_dynhds_add_custom(struct Curl_easy *data, - bool is_connect, - struct dynhds *hds); - CURLcode Curl_http_compile_trailers(struct curl_slist *trailers, struct dynbuf *buf, struct Curl_easy *handle); @@ -185,6 +175,33 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data); #endif /* CURL_DISABLE_HTTP */ +#ifdef USE_NGHTTP3 +struct h3out; /* see ngtcp2 */ +#endif + +#ifdef USE_MSH3 +#ifdef _WIN32 +#define msh3_lock CRITICAL_SECTION +#define msh3_lock_initialize(lock) InitializeCriticalSection(lock) +#define msh3_lock_uninitialize(lock) DeleteCriticalSection(lock) +#define msh3_lock_acquire(lock) EnterCriticalSection(lock) +#define msh3_lock_release(lock) LeaveCriticalSection(lock) +#else /* !_WIN32 */ +#include <pthread.h> +#define msh3_lock pthread_mutex_t +#define msh3_lock_initialize(lock) { \ + pthread_mutexattr_t attr; \ + pthread_mutexattr_init(&attr); \ + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \ + pthread_mutex_init(lock, &attr); \ + pthread_mutexattr_destroy(&attr); \ +} +#define msh3_lock_uninitialize(lock) pthread_mutex_destroy(lock) +#define msh3_lock_acquire(lock) pthread_mutex_lock(lock) +#define msh3_lock_release(lock) pthread_mutex_unlock(lock) +#endif /* _WIN32 */ +#endif /* USE_MSH3 */ + /**************************************************************************** * HTTP unique setup ***************************************************************************/ @@ -203,7 +220,6 @@ struct HTTP { void *fread_in; /* backup storage for fread_in pointer */ const char *postdata; curl_off_t postsize; - struct Curl_easy *data; } backup; enum { @@ -212,13 +228,125 @@ struct HTTP { HTTPSEND_BODY /* sending body */ } sending; +#ifdef USE_WEBSOCKETS + struct websocket ws; +#endif + #ifndef CURL_DISABLE_HTTP - void *h2_ctx; /* HTTP/2 implementation context */ - void *h3_ctx; /* HTTP/3 implementation context */ struct dynbuf send_buffer; /* used if the request couldn't be sent in one chunk, points to an allocated send_buffer struct */ #endif +#ifdef USE_NGHTTP2 + /*********** for HTTP/2 we store stream-local data here *************/ + int32_t stream_id; /* stream we are interested in */ + + /* We store non-final and final response headers here, per-stream */ + struct dynbuf header_recvbuf; + size_t nread_header_recvbuf; /* number of bytes in header_recvbuf fed into + upper layer */ + struct dynbuf trailer_recvbuf; + const uint8_t *pausedata; /* pointer to data received in on_data_chunk */ + size_t pauselen; /* the number of bytes left in data */ + bool close_handled; /* TRUE if stream closure is handled by libcurl */ + + char **push_headers; /* allocated array */ + size_t push_headers_used; /* number of entries filled in */ + size_t push_headers_alloc; /* number of entries allocated */ + uint32_t error; /* HTTP/2 stream error code */ +#endif +#if defined(USE_NGHTTP2) || defined(USE_NGHTTP3) + bool bodystarted; + int status_code; /* HTTP status code */ + bool closed; /* TRUE on HTTP2 stream close */ + char *mem; /* points to a buffer in memory to store received data */ + size_t len; /* size of the buffer 'mem' points to */ + size_t memlen; /* size of data copied to mem */ +#endif +#if defined(USE_NGHTTP2) || defined(ENABLE_QUIC) + /* fields used by both HTTP/2 and HTTP/3 */ + const uint8_t *upload_mem; /* points to a buffer to read from */ + size_t upload_len; /* size of the buffer 'upload_mem' points to */ + curl_off_t upload_left; /* number of bytes left to upload */ +#endif + +#ifdef ENABLE_QUIC +#ifndef USE_MSH3 + /*********** for HTTP/3 we store stream-local data here *************/ + int64_t stream3_id; /* stream we are interested in */ + uint64_t error3; /* HTTP/3 stream error code */ + bool firstheader; /* FALSE until headers arrive */ + bool firstbody; /* FALSE until body arrives */ + bool h3req; /* FALSE until request is issued */ +#endif + bool upload_done; +#endif +#ifdef USE_NGHTTP3 + size_t unacked_window; + struct h3out *h3out; /* per-stream buffers for upload */ + struct dynbuf overflow; /* excess data received during a single Curl_read */ +#endif +#ifdef USE_MSH3 + struct MSH3_REQUEST *req; + msh3_lock recv_lock; + /* Receive Buffer (Headers and Data) */ + uint8_t* recv_buf; + size_t recv_buf_alloc; + /* Receive Headers */ + size_t recv_header_len; + bool recv_header_complete; + /* Receive Data */ + size_t recv_data_len; + bool recv_data_complete; + /* General Receive Error */ + CURLcode recv_error; +#endif +}; + +#ifdef USE_NGHTTP2 +/* h2 settings for this connection */ +struct h2settings { + uint32_t max_concurrent_streams; + bool enable_push; +}; +#endif + +struct http_conn { +#ifdef USE_NGHTTP2 +#define H2_BINSETTINGS_LEN 80 + uint8_t binsettings[H2_BINSETTINGS_LEN]; + size_t binlen; /* length of the binsettings data */ + + /* We associate the connnectdata struct with the connection, but we need to + make sure we can identify the current "driving" transfer. This is a + work-around for the lack of nghttp2_session_set_user_data() in older + nghttp2 versions that we want to support. (Added in 1.31.0) */ + struct Curl_easy *trnsfr; + + nghttp2_session *h2; + Curl_send *send_underlying; /* underlying send Curl_send callback */ + Curl_recv *recv_underlying; /* underlying recv Curl_recv callback */ + char *inbuf; /* buffer to receive data from underlying socket */ + size_t inbuflen; /* number of bytes filled in inbuf */ + size_t nread_inbuf; /* number of bytes read from in inbuf */ + /* We need separate buffer for transmission and reception because we + may call nghttp2_session_send() after the + nghttp2_session_mem_recv() but mem buffer is still not full. In + this case, we wrongly sends the content of mem buffer if we share + them for both cases. */ + int32_t pause_stream_id; /* stream ID which paused + nghttp2_session_mem_recv */ + size_t drain_total; /* sum of all stream's UrlState.drain */ + + /* this is a hash of all individual streams (Curl_easy structs) */ + struct h2settings settings; + + /* list of settings that will be sent */ + nghttp2_settings_entry local_settings[3]; + size_t local_settings_num; +#else + int unused; /* prevent a compiler warning */ +#endif }; CURLcode Curl_http_size(struct Curl_easy *data); @@ -253,79 +381,4 @@ Curl_http_output_auth(struct Curl_easy *data, bool proxytunnel); /* TRUE if this is the request setting up the proxy tunnel */ -/* Decode HTTP status code string. */ -CURLcode Curl_http_decode_status(int *pstatus, const char *s, size_t len); - - -/** - * All about a core HTTP request, excluding body and trailers - */ -struct httpreq { - char method[12]; - char *scheme; - char *authority; - char *path; - struct dynhds headers; - struct dynhds trailers; -}; - -/** - * Create a HTTP request struct. - */ -CURLcode Curl_http_req_make(struct httpreq **preq, - const char *method, size_t m_len, - const char *scheme, size_t s_len, - const char *authority, size_t a_len, - const char *path, size_t p_len); - -CURLcode Curl_http_req_make2(struct httpreq **preq, - const char *method, size_t m_len, - CURLU *url, const char *scheme_default); - -void Curl_http_req_free(struct httpreq *req); - -#define HTTP_PSEUDO_METHOD ":method" -#define HTTP_PSEUDO_SCHEME ":scheme" -#define HTTP_PSEUDO_AUTHORITY ":authority" -#define HTTP_PSEUDO_PATH ":path" -#define HTTP_PSEUDO_STATUS ":status" - -/** - * Create the list of HTTP/2 headers which represent the request, - * using HTTP/2 pseudo headers preceeding the `req->headers`. - * - * Applies the following transformations: - * - if `authority` is set, any "Host" header is removed. - * - if `authority` is unset and a "Host" header is present, use - * that as `authority` and remove "Host" - * - removes and Connection header fields as defined in rfc9113 ch. 8.2.2 - * - lower-cases the header field names - * - * @param h2_headers will contain the HTTP/2 headers on success - * @param req the request to transform - * @param data the handle to lookup defaults like ' :scheme' from - */ -CURLcode Curl_http_req_to_h2(struct dynhds *h2_headers, - struct httpreq *req, struct Curl_easy *data); - -/** - * All about a core HTTP response, excluding body and trailers - */ -struct http_resp { - int status; - char *description; - struct dynhds headers; - struct dynhds trailers; - struct http_resp *prev; -}; - -/** - * Create a HTTP response struct. - */ -CURLcode Curl_http_resp_make(struct http_resp **presp, - int status, - const char *description); - -void Curl_http_resp_free(struct http_resp *resp); - #endif /* HEADER_CURL_HTTP_H */ diff --git a/contrib/libs/curl/lib/http1.c b/contrib/libs/curl/lib/http1.c deleted file mode 100644 index a442d3eaa8..0000000000 --- a/contrib/libs/curl/lib/http1.c +++ /dev/null @@ -1,322 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifndef CURL_DISABLE_HTTP - -#include "urldata.h" -#include <curl/curl.h> -#include "http.h" -#include "http1.h" -#include "urlapi-int.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - - -#define H1_MAX_URL_LEN (8*1024) - -void Curl_h1_req_parse_init(struct h1_req_parser *parser, size_t max_line_len) -{ - memset(parser, 0, sizeof(*parser)); - parser->max_line_len = max_line_len; - Curl_dyn_init(&parser->scratch, max_line_len); -} - -void Curl_h1_req_parse_free(struct h1_req_parser *parser) -{ - if(parser) { - Curl_http_req_free(parser->req); - Curl_dyn_free(&parser->scratch); - parser->req = NULL; - parser->done = FALSE; - } -} - -static CURLcode trim_line(struct h1_req_parser *parser, int options) -{ - DEBUGASSERT(parser->line); - if(parser->line_len) { - if(parser->line[parser->line_len - 1] == '\n') - --parser->line_len; - if(parser->line_len) { - if(parser->line[parser->line_len - 1] == '\r') - --parser->line_len; - else if(options & H1_PARSE_OPT_STRICT) - return CURLE_URL_MALFORMAT; - } - else if(options & H1_PARSE_OPT_STRICT) - return CURLE_URL_MALFORMAT; - } - else if(options & H1_PARSE_OPT_STRICT) - return CURLE_URL_MALFORMAT; - - if(parser->line_len > parser->max_line_len) { - return CURLE_URL_MALFORMAT; - } - return CURLE_OK; -} - -static ssize_t detect_line(struct h1_req_parser *parser, - const char *buf, const size_t buflen, - CURLcode *err) -{ - const char *line_end; - - DEBUGASSERT(!parser->line); - line_end = memchr(buf, '\n', buflen); - if(!line_end) { - *err = CURLE_AGAIN; - return -1; - } - parser->line = buf; - parser->line_len = line_end - buf + 1; - *err = CURLE_OK; - return (ssize_t)parser->line_len; -} - -static ssize_t next_line(struct h1_req_parser *parser, - const char *buf, const size_t buflen, int options, - CURLcode *err) -{ - ssize_t nread = 0; - - if(parser->line) { - parser->line = NULL; - parser->line_len = 0; - Curl_dyn_reset(&parser->scratch); - } - - nread = detect_line(parser, buf, buflen, err); - if(nread >= 0) { - if(Curl_dyn_len(&parser->scratch)) { - /* append detected line to scratch to have the complete line */ - *err = Curl_dyn_addn(&parser->scratch, parser->line, parser->line_len); - if(*err) - return -1; - parser->line = Curl_dyn_ptr(&parser->scratch); - parser->line_len = Curl_dyn_len(&parser->scratch); - } - *err = trim_line(parser, options); - if(*err) - return -1; - } - else if(*err == CURLE_AGAIN) { - /* no line end in `buf`, add it to our scratch */ - *err = Curl_dyn_addn(&parser->scratch, (const unsigned char *)buf, buflen); - nread = (*err)? -1 : (ssize_t)buflen; - } - return nread; -} - -static CURLcode start_req(struct h1_req_parser *parser, - const char *scheme_default, int options) -{ - const char *p, *m, *target, *hv, *scheme, *authority, *path; - size_t m_len, target_len, hv_len, scheme_len, authority_len, path_len; - size_t i; - CURLU *url = NULL; - CURLcode result = CURLE_URL_MALFORMAT; /* Use this as default fail */ - - DEBUGASSERT(!parser->req); - /* line must match: "METHOD TARGET HTTP_VERSION" */ - p = memchr(parser->line, ' ', parser->line_len); - if(!p || p == parser->line) - goto out; - - m = parser->line; - m_len = p - parser->line; - target = p + 1; - target_len = hv_len = 0; - hv = NULL; - - /* URL may contain spaces so scan backwards */ - for(i = parser->line_len; i > m_len; --i) { - if(parser->line[i] == ' ') { - hv = &parser->line[i + 1]; - hv_len = parser->line_len - i; - target_len = (hv - target) - 1; - break; - } - } - /* no SPACE found or empty TARGET or empy HTTP_VERSION */ - if(!target_len || !hv_len) - goto out; - - /* TODO: we do not check HTTP_VERSION for conformity, should - + do that when STRICT option is supplied. */ - (void)hv; - - /* The TARGET can be (rfc 9112, ch. 3.2): - * origin-form: path + optional query - * absolute-form: absolute URI - * authority-form: host+port for CONNECT - * asterisk-form: '*' for OPTIONS - * - * from TARGET, we derive `scheme` `authority` `path` - * origin-form -- -- TARGET - * absolute-form URL* URL* URL* - * authority-form -- TARGET -- - * asterisk-form -- -- TARGET - */ - scheme = authority = path = NULL; - scheme_len = authority_len = path_len = 0; - - if(target_len == 1 && target[0] == '*') { - /* asterisk-form */ - path = target; - path_len = target_len; - } - else if(!strncmp("CONNECT", m, m_len)) { - /* authority-form */ - authority = target; - authority_len = target_len; - } - else if(target[0] == '/') { - /* origin-form */ - path = target; - path_len = target_len; - } - else { - /* origin-form OR absolute-form */ - CURLUcode uc; - char tmp[H1_MAX_URL_LEN]; - - /* default, unless we see an absolute URL */ - path = target; - path_len = target_len; - - /* URL parser wants 0-termination */ - if(target_len >= sizeof(tmp)) - goto out; - memcpy(tmp, target, target_len); - tmp[target_len] = '\0'; - /* See if treating TARGET as an absolute URL makes sense */ - if(Curl_is_absolute_url(tmp, NULL, 0, FALSE)) { - int url_options; - - url = curl_url(); - if(!url) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - url_options = (CURLU_NON_SUPPORT_SCHEME| - CURLU_PATH_AS_IS| - CURLU_NO_DEFAULT_PORT); - if(!(options & H1_PARSE_OPT_STRICT)) - url_options |= CURLU_ALLOW_SPACE; - uc = curl_url_set(url, CURLUPART_URL, tmp, url_options); - if(uc) { - goto out; - } - } - - if(!url && (options & H1_PARSE_OPT_STRICT)) { - /* we should have an absolute URL or have seen `/` earlier */ - goto out; - } - } - - if(url) { - result = Curl_http_req_make2(&parser->req, m, m_len, url, scheme_default); - } - else { - if(!scheme && scheme_default) { - scheme = scheme_default; - scheme_len = strlen(scheme_default); - } - result = Curl_http_req_make(&parser->req, m, m_len, scheme, scheme_len, - authority, authority_len, path, path_len); - } - -out: - curl_url_cleanup(url); - return result; -} - -ssize_t Curl_h1_req_parse_read(struct h1_req_parser *parser, - const char *buf, size_t buflen, - const char *scheme_default, int options, - CURLcode *err) -{ - ssize_t nread = 0, n; - - *err = CURLE_OK; - while(!parser->done) { - n = next_line(parser, buf, buflen, options, err); - if(n < 0) { - if(*err != CURLE_AGAIN) { - nread = -1; - } - *err = CURLE_OK; - goto out; - } - - /* Consume this line */ - nread += (size_t)n; - buf += (size_t)n; - buflen -= (size_t)n; - - if(!parser->line) { - /* consumed bytes, but line not complete */ - if(!buflen) - goto out; - } - else if(!parser->req) { - *err = start_req(parser, scheme_default, options); - if(*err) { - nread = -1; - goto out; - } - } - else if(parser->line_len == 0) { - /* last, empty line, we are finished */ - if(!parser->req) { - *err = CURLE_URL_MALFORMAT; - nread = -1; - goto out; - } - parser->done = TRUE; - Curl_dyn_reset(&parser->scratch); - /* last chance adjustments */ - } - else { - *err = Curl_dynhds_h1_add_line(&parser->req->headers, - parser->line, parser->line_len); - if(*err) { - nread = -1; - goto out; - } - } - } - -out: - return nread; -} - - -#endif /* !CURL_DISABLE_HTTP */ diff --git a/contrib/libs/curl/lib/http1.h b/contrib/libs/curl/lib/http1.h deleted file mode 100644 index b1eaa969d9..0000000000 --- a/contrib/libs/curl/lib/http1.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef HEADER_CURL_HTTP1_H -#define HEADER_CURL_HTTP1_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifndef CURL_DISABLE_HTTP -#include "bufq.h" -#include "http.h" - -#define H1_PARSE_OPT_NONE (0) -#define H1_PARSE_OPT_STRICT (1 << 0) - -#define H1_PARSE_DEFAULT_MAX_LINE_LEN DYN_HTTP_REQUEST - -struct h1_req_parser { - struct httpreq *req; - struct dynbuf scratch; - size_t scratch_skip; - const char *line; - size_t max_line_len; - size_t line_len; - bool done; -}; - -void Curl_h1_req_parse_init(struct h1_req_parser *parser, size_t max_line_len); -void Curl_h1_req_parse_free(struct h1_req_parser *parser); - -ssize_t Curl_h1_req_parse_read(struct h1_req_parser *parser, - const char *buf, size_t buflen, - const char *scheme_default, int options, - CURLcode *err); - -CURLcode Curl_h1_req_dprint(const struct httpreq *req, - struct dynbuf *dbuf); - - -#endif /* !CURL_DISABLE_HTTP */ -#endif /* HEADER_CURL_HTTP1_H */ diff --git a/contrib/libs/curl/lib/http2.c b/contrib/libs/curl/lib/http2.c index 6c09ec1a1f..b7409b027d 100644 --- a/contrib/libs/curl/lib/http2.c +++ b/contrib/libs/curl/lib/http2.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -25,11 +25,8 @@ #include "curl_setup.h" #ifdef USE_NGHTTP2 -#include <stdint.h> #include <nghttp2/nghttp2.h> #include "urldata.h" -#include "bufq.h" -#include "http1.h" #include "http2.h" #include "http.h" #include "sendf.h" @@ -38,19 +35,20 @@ #include "strcase.h" #include "multiif.h" #include "url.h" -#include "urlapi-int.h" -#include "cfilters.h" #include "connect.h" #include "strtoofft.h" #include "strdup.h" #include "transfer.h" #include "dynbuf.h" +#include "h2h3.h" #include "headers.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" +#define H2_BUFSIZE 32768 + #if (NGHTTP2_VERSION_NUM < 0x010c00) #error too old nghttp2 version, upgrade! #endif @@ -63,572 +61,304 @@ #define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1 #endif +#define HTTP2_HUGE_WINDOW_SIZE (32 * 1024 * 1024) /* 32 MB */ -/* buffer dimensioning: - * use 16K as chunk size, as that fits H2 DATA frames well */ -#define H2_CHUNK_SIZE (16 * 1024) -/* this is how much we want "in flight" for a stream */ -#define H2_STREAM_WINDOW_SIZE (10 * 1024 * 1024) -/* on receving from TLS, we prep for holding a full stream window */ -#define H2_NW_RECV_CHUNKS (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) -/* on send into TLS, we just want to accumulate small frames */ -#define H2_NW_SEND_CHUNKS 1 -/* stream recv/send chunks are a result of window / chunk sizes */ -#define H2_STREAM_RECV_CHUNKS (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) -/* keep smaller stream upload buffer (default h2 window size) to have - * our progress bars and "upload done" reporting closer to reality */ -#define H2_STREAM_SEND_CHUNKS ((64 * 1024) / H2_CHUNK_SIZE) -/* spare chunks we keep for a full window */ -#define H2_STREAM_POOL_SPARES (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) - -/* We need to accommodate the max number of streams with their window - * sizes on the overall connection. Streams might become PAUSED which - * will block their received QUOTA in the connection window. And if we - * run out of space, the server is blocked from sending us any data. - * See #10988 for an issue with this. */ -#define HTTP2_HUGE_WINDOW_SIZE (100 * H2_STREAM_WINDOW_SIZE) - -#define H2_SETTINGS_IV_LEN 3 -#define H2_BINSETTINGS_LEN 80 - -static int populate_settings(nghttp2_settings_entry *iv, - struct Curl_easy *data) -{ - iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[0].value = Curl_multi_max_concurrent_streams(data->multi); - - iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[1].value = H2_STREAM_WINDOW_SIZE; - - iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; - iv[2].value = data->multi->push_cb != NULL; +#ifdef DEBUG_HTTP2 +#define H2BUGF(x) x +#else +#define H2BUGF(x) do { } while(0) +#endif - return 3; -} +static ssize_t http2_recv(struct Curl_easy *data, int sockindex, + char *mem, size_t len, CURLcode *err); +static bool http2_connisdead(struct Curl_easy *data, + struct connectdata *conn); +static int h2_session_send(struct Curl_easy *data, + nghttp2_session *h2); +static int h2_process_pending_input(struct Curl_easy *data, + struct http_conn *httpc, + CURLcode *err); -static size_t populate_binsettings(uint8_t *binsettings, - struct Curl_easy *data) +/* + * Curl_http2_init_state() is called when the easy handle is created and + * allows for HTTP/2 specific init of state. + */ +void Curl_http2_init_state(struct UrlState *state) { - nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN]; - int ivlen; - - ivlen = populate_settings(iv, data); - /* this returns number of bytes it wrote */ - return nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN, - iv, ivlen); + state->stream_weight = NGHTTP2_DEFAULT_WEIGHT; } -struct cf_h2_ctx { - nghttp2_session *h2; - uint32_t max_concurrent_streams; - /* The easy handle used in the current filter call, cleared at return */ - struct cf_call_data call_data; - - struct bufq inbufq; /* network input */ - struct bufq outbufq; /* network output */ - struct bufc_pool stream_bufcp; /* spares for stream buffers */ - - size_t drain_total; /* sum of all stream's UrlState drain */ - int32_t goaway_error; - int32_t last_stream_id; - BIT(conn_closed); - BIT(goaway); - BIT(enable_push); - BIT(nw_out_blocked); -}; - -/* How to access `call_data` from a cf_h2 filter */ -#undef CF_CTX_CALL_DATA -#define CF_CTX_CALL_DATA(cf) \ - ((struct cf_h2_ctx *)(cf)->ctx)->call_data - -static void cf_h2_ctx_clear(struct cf_h2_ctx *ctx) +/* + * Curl_http2_init_userset() is called when the easy handle is created and + * allows for HTTP/2 specific user-set fields. + */ +void Curl_http2_init_userset(struct UserDefined *set) { - struct cf_call_data save = ctx->call_data; - - if(ctx->h2) { - nghttp2_session_del(ctx->h2); - } - Curl_bufq_free(&ctx->inbufq); - Curl_bufq_free(&ctx->outbufq); - Curl_bufcp_free(&ctx->stream_bufcp); - memset(ctx, 0, sizeof(*ctx)); - ctx->call_data = save; + set->stream_weight = NGHTTP2_DEFAULT_WEIGHT; } -static void cf_h2_ctx_free(struct cf_h2_ctx *ctx) +static int http2_getsock(struct Curl_easy *data, + struct connectdata *conn, + curl_socket_t *sock) { - if(ctx) { - cf_h2_ctx_clear(ctx); - free(ctx); - } -} + const struct http_conn *c = &conn->proto.httpc; + struct SingleRequest *k = &data->req; + int bitmap = GETSOCK_BLANK; + struct HTTP *stream = data->req.p.http; -static CURLcode h2_progress_egress(struct Curl_cfilter *cf, - struct Curl_easy *data); + sock[0] = conn->sock[FIRSTSOCKET]; -/** - * All about the H3 internals of a stream - */ -struct stream_ctx { - /*********** for HTTP/2 we store stream-local data here *************/ - int32_t id; /* HTTP/2 protocol identifier for stream */ - struct bufq recvbuf; /* response buffer */ - struct bufq sendbuf; /* request buffer */ - struct dynhds resp_trailers; /* response trailer fields */ - size_t resp_hds_len; /* amount of response header bytes in recvbuf */ - size_t upload_blocked_len; - curl_off_t upload_left; /* number of request bytes left to upload */ - - char **push_headers; /* allocated array */ - size_t push_headers_used; /* number of entries filled in */ - size_t push_headers_alloc; /* number of entries allocated */ - - int status_code; /* HTTP response status code */ - uint32_t error; /* stream error code */ - uint32_t local_window_size; /* the local recv window size */ - bool closed; /* TRUE on stream close */ - bool reset; /* TRUE on stream reset */ - bool close_handled; /* TRUE if stream closure is handled by libcurl */ - bool bodystarted; - bool send_closed; /* transfer is done sending, we might have still - buffered data in stream->sendbuf to upload. */ -}; + if(!(k->keepon & KEEP_RECV_PAUSE)) + /* Unless paused - in a HTTP/2 connection we can basically always get a + frame so we should always be ready for one */ + bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); -#define H2_STREAM_CTX(d) ((struct stream_ctx *)(((d) && (d)->req.p.http)? \ - ((struct HTTP *)(d)->req.p.http)->h2_ctx \ - : NULL)) -#define H2_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h2_ctx -#define H2_STREAM_ID(d) (H2_STREAM_CTX(d)? \ - H2_STREAM_CTX(d)->id : -2) + /* we're (still uploading OR the HTTP/2 layer wants to send data) AND + there's a window to send data in */ + if((((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) || + nghttp2_session_want_write(c->h2)) && + (nghttp2_session_get_remote_window_size(c->h2) && + nghttp2_session_get_stream_remote_window_size(c->h2, + stream->stream_id))) + bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); + + return bitmap; +} /* - * Mark this transfer to get "drained". + * http2_stream_free() free HTTP2 stream related data */ -static void drain_stream(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct stream_ctx *stream) +static void http2_stream_free(struct HTTP *http) { - unsigned char bits; - - (void)cf; - bits = CURL_CSELECT_IN; - if(!stream->send_closed && - (stream->upload_left || stream->upload_blocked_len)) - bits |= CURL_CSELECT_OUT; - if(data->state.dselect_bits != bits) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] DRAIN dselect_bits=%x", - stream->id, bits)); - data->state.dselect_bits = bits; - Curl_expire(data, 0, EXPIRE_RUN_NOW); + if(http) { + Curl_dyn_free(&http->header_recvbuf); + for(; http->push_headers_used > 0; --http->push_headers_used) { + free(http->push_headers[http->push_headers_used - 1]); + } + free(http->push_headers); + http->push_headers = NULL; } } -static CURLcode http2_data_setup(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct stream_ctx **pstream) +/* + * Disconnects *a* connection used for HTTP/2. It might be an old one from the + * connection cache and not the "main" one. Don't touch the easy handle! + */ + +static CURLcode http2_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead_connection) { - struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream; + struct http_conn *c = &conn->proto.httpc; + (void)dead_connection; +#ifndef DEBUG_HTTP2 + (void)data; +#endif - (void)cf; - DEBUGASSERT(data); - if(!data->req.p.http) { - failf(data, "initialization failure, transfer not http initialized"); - return CURLE_FAILED_INIT; - } - stream = H2_STREAM_CTX(data); - if(stream) { - *pstream = stream; - return CURLE_OK; - } + H2BUGF(infof(data, "HTTP/2 DISCONNECT starts now")); - stream = calloc(1, sizeof(*stream)); - if(!stream) - return CURLE_OUT_OF_MEMORY; - - stream->id = -1; - Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp, - H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); - Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp, - H2_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); - Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST); - stream->resp_hds_len = 0; - stream->bodystarted = FALSE; - stream->status_code = -1; - stream->closed = FALSE; - stream->close_handled = FALSE; - stream->error = NGHTTP2_NO_ERROR; - stream->local_window_size = H2_STREAM_WINDOW_SIZE; - stream->upload_left = 0; + nghttp2_session_del(c->h2); + Curl_safefree(c->inbuf); + + H2BUGF(infof(data, "HTTP/2 DISCONNECT done")); - H2_STREAM_LCTX(data) = stream; - *pstream = stream; return CURLE_OK; } -static void http2_data_done(struct Curl_cfilter *cf, - struct Curl_easy *data, bool premature) +/* + * The server may send us data at any point (e.g. PING frames). Therefore, + * we cannot assume that an HTTP/2 socket is dead just because it is readable. + * + * Instead, if it is readable, run Curl_connalive() to peek at the socket + * and distinguish between closed and data. + */ +static bool http2_connisdead(struct Curl_easy *data, struct connectdata *conn) { - struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H2_STREAM_CTX(data); + int sval; + bool dead = TRUE; - DEBUGASSERT(ctx); - (void)premature; - if(!stream) - return; - - if(ctx->h2) { - if(!stream->closed && stream->id > 0) { - /* RST_STREAM */ - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] premature DATA_DONE, RST stream", - stream->id)); - if(!nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE, - stream->id, NGHTTP2_STREAM_CLOSED)) - (void)nghttp2_session_send(ctx->h2); - } - if(!Curl_bufq_is_empty(&stream->recvbuf)) { - /* Anything in the recvbuf is still being counted - * in stream and connection window flow control. Need - * to free that space or the connection window might get - * exhausted eventually. */ - nghttp2_session_consume(ctx->h2, stream->id, - Curl_bufq_len(&stream->recvbuf)); - /* give WINDOW_UPATE a chance to be sent, but ignore any error */ - (void)h2_progress_egress(cf, data); - } + if(conn->bits.close) + return TRUE; - /* -1 means unassigned and 0 means cleared */ - if(nghttp2_session_get_stream_user_data(ctx->h2, stream->id)) { - int rv = nghttp2_session_set_stream_user_data(ctx->h2, - stream->id, 0); - if(rv) { - infof(data, "http/2: failed to clear user_data for stream %u", - stream->id); - DEBUGASSERT(0); + sval = SOCKET_READABLE(conn->sock[FIRSTSOCKET], 0); + if(sval == 0) { + /* timeout */ + dead = FALSE; + } + else if(sval & CURL_CSELECT_ERR) { + /* socket is in an error state */ + dead = TRUE; + } + else if(sval & CURL_CSELECT_IN) { + /* readable with no error. could still be closed */ + 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 transfer, there shouldn't be any data here, + only "protocol frames" */ + CURLcode result; + struct http_conn *httpc = &conn->proto.httpc; + ssize_t nread = -1; + if(httpc->recv_underlying) + /* if called "too early", this pointer isn't setup yet! */ + nread = ((Curl_recv *)httpc->recv_underlying)( + data, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result); + if(nread != -1) { + H2BUGF(infof(data, + "%d bytes stray data read before trying h2 connection", + (int)nread)); + httpc->nread_inbuf = 0; + httpc->inbuflen = nread; + if(h2_process_pending_input(data, httpc, &result) < 0) + /* immediate error, considered dead */ + dead = TRUE; } + else + /* the read failed so let's say this is dead anyway */ + dead = TRUE; } } - Curl_bufq_free(&stream->sendbuf); - Curl_bufq_free(&stream->recvbuf); - Curl_dynhds_free(&stream->resp_trailers); - if(stream->push_headers) { - /* if they weren't used and then freed before */ - for(; stream->push_headers_used > 0; --stream->push_headers_used) { - free(stream->push_headers[stream->push_headers_used - 1]); - } - free(stream->push_headers); - stream->push_headers = NULL; - } - - free(stream); - H2_STREAM_LCTX(data) = NULL; + return dead; } -static int h2_client_new(struct Curl_cfilter *cf, - nghttp2_session_callbacks *cbs) -{ - struct cf_h2_ctx *ctx = cf->ctx; - nghttp2_option *o; - - int rc = nghttp2_option_new(&o); - if(rc) - return rc; - /* We handle window updates ourself to enforce buffer limits */ - nghttp2_option_set_no_auto_window_update(o, 1); -#if NGHTTP2_VERSION_NUM >= 0x013200 - /* with 1.50.0 */ - /* turn off RFC 9113 leading and trailing white spaces validation against - HTTP field value. */ - nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1); -#endif - rc = nghttp2_session_client_new2(&ctx->h2, cbs, cf, o); - nghttp2_option_del(o); - return rc; -} - -static ssize_t nw_in_reader(void *reader_ctx, - unsigned char *buf, size_t buflen, - CURLcode *err) -{ - struct Curl_cfilter *cf = reader_ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - - return Curl_conn_cf_recv(cf->next, data, (char *)buf, buflen, err); -} - -static ssize_t nw_out_writer(void *writer_ctx, - const unsigned char *buf, size_t buflen, - CURLcode *err) +/* + * Set the transfer that is currently using this HTTP/2 connection. + */ +static void set_transfer(struct http_conn *c, + struct Curl_easy *data) { - struct Curl_cfilter *cf = writer_ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - - return Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, err); + c->trnsfr = data; } -static ssize_t send_callback(nghttp2_session *h2, - const uint8_t *mem, size_t length, int flags, - void *userp); -static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, - void *userp); -static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - const uint8_t *mem, size_t len, void *userp); -static int on_stream_close(nghttp2_session *session, int32_t stream_id, - uint32_t error_code, void *userp); -static int on_begin_headers(nghttp2_session *session, - const nghttp2_frame *frame, void *userp); -static int on_header(nghttp2_session *session, const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - uint8_t flags, - void *userp); -static int error_callback(nghttp2_session *session, const char *msg, - size_t len, void *userp); - /* - * multi_connchanged() is called to tell that there is a connection in - * this multi handle that has changed state (multiplexing become possible, the - * number of allowed streams changed or similar), and a subsequent use of this - * multi handle should move CONNECT_PEND handles back to CONNECT to have them - * retry. + * Get the transfer that is currently using this HTTP/2 connection. */ -static void multi_connchanged(struct Curl_multi *multi) +static struct Curl_easy *get_transfer(struct http_conn *c) { - multi->recheckstate = TRUE; + DEBUGASSERT(c && c->trnsfr); + return c->trnsfr; } -/* - * Initialize the cfilter context - */ -static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool via_h1_upgrade) +static unsigned int http2_conncheck(struct Curl_easy *data, + struct connectdata *conn, + unsigned int checks_to_perform) { - struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream; - CURLcode result = CURLE_OUT_OF_MEMORY; + unsigned int ret_val = CONNRESULT_NONE; + struct http_conn *c = &conn->proto.httpc; int rc; - nghttp2_session_callbacks *cbs = NULL; - - DEBUGASSERT(!ctx->h2); - Curl_bufcp_init(&ctx->stream_bufcp, H2_CHUNK_SIZE, H2_STREAM_POOL_SPARES); - Curl_bufq_initp(&ctx->inbufq, &ctx->stream_bufcp, H2_NW_RECV_CHUNKS, 0); - Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0); - ctx->last_stream_id = 2147483647; - - rc = nghttp2_session_callbacks_new(&cbs); - if(rc) { - failf(data, "Couldn't initialize nghttp2 callbacks"); - goto out; - } - - nghttp2_session_callbacks_set_send_callback(cbs, send_callback); - nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv); - nghttp2_session_callbacks_set_on_data_chunk_recv_callback( - cbs, on_data_chunk_recv); - nghttp2_session_callbacks_set_on_stream_close_callback(cbs, on_stream_close); - nghttp2_session_callbacks_set_on_begin_headers_callback( - cbs, on_begin_headers); - nghttp2_session_callbacks_set_on_header_callback(cbs, on_header); - nghttp2_session_callbacks_set_error_callback(cbs, error_callback); - - /* The nghttp2 session is not yet setup, do it */ - rc = h2_client_new(cf, cbs); - if(rc) { - failf(data, "Couldn't initialize nghttp2"); - goto out; - } - ctx->max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS; - - if(via_h1_upgrade) { - /* HTTP/1.1 Upgrade issued. H2 Settings have already been submitted - * in the H1 request and we upgrade from there. This stream - * is opened implicitly as #1. */ - uint8_t binsettings[H2_BINSETTINGS_LEN]; - size_t binlen; /* length of the binsettings data */ - - binlen = populate_binsettings(binsettings, data); - - result = http2_data_setup(cf, data, &stream); - if(result) - goto out; - DEBUGASSERT(stream); - stream->id = 1; - /* queue SETTINGS frame (again) */ - rc = nghttp2_session_upgrade2(ctx->h2, binsettings, binlen, - data->state.httpreq == HTTPREQ_HEAD, - NULL); - if(rc) { - failf(data, "nghttp2_session_upgrade2() failed: %s(%d)", - nghttp2_strerror(rc), rc); - result = CURLE_HTTP2; - goto out; - } + bool send_frames = false; - rc = nghttp2_session_set_stream_user_data(ctx->h2, stream->id, - data); - if(rc) { - infof(data, "http/2: failed to set user_data for stream %u", - stream->id); - DEBUGASSERT(0); - } + if(checks_to_perform & CONNCHECK_ISDEAD) { + if(http2_connisdead(data, conn)) + ret_val |= CONNRESULT_DEAD; } - else { - nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN]; - int ivlen; - ivlen = populate_settings(iv, data); - rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, - iv, ivlen); - if(rc) { - failf(data, "nghttp2_submit_settings() failed: %s(%d)", - nghttp2_strerror(rc), rc); - result = CURLE_HTTP2; - goto out; + if(checks_to_perform & CONNCHECK_KEEPALIVE) { + struct curltime now = Curl_now(); + timediff_t elapsed = Curl_timediff(now, conn->keepalive); + + if(elapsed > data->set.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(data, "nghttp2_submit_ping() failed: %s(%d)", + nghttp2_strerror(rc), rc); + } + + conn->keepalive = now; } } - rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0, - HTTP2_HUGE_WINDOW_SIZE); - if(rc) { - failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", - nghttp2_strerror(rc), rc); - result = CURLE_HTTP2; - goto out; + if(send_frames) { + set_transfer(c, data); /* set the transfer */ + rc = nghttp2_session_send(c->h2); + if(rc) + failf(data, "nghttp2_session_send() failed: %s(%d)", + nghttp2_strerror(rc), rc); } - /* all set, traffic will be send on connect */ - result = CURLE_OK; - -out: - if(cbs) - nghttp2_session_callbacks_del(cbs); - return result; + return ret_val; } -/* - * Returns nonzero if current HTTP/2 session should be closed. - */ -static int should_close_session(struct cf_h2_ctx *ctx) +/* called from http_setup_conn */ +void Curl_http2_setup_req(struct Curl_easy *data) { - return ctx->drain_total == 0 && !nghttp2_session_want_read(ctx->h2) && - !nghttp2_session_want_write(ctx->h2); + struct HTTP *http = data->req.p.http; + http->bodystarted = FALSE; + http->status_code = -1; + http->pausedata = NULL; + http->pauselen = 0; + http->closed = FALSE; + http->close_handled = FALSE; + http->mem = NULL; + http->len = 0; + http->memlen = 0; + http->error = NGHTTP2_NO_ERROR; } -/* - * Processes pending input left in network input buffer. - * This function returns 0 if it succeeds, or -1 and error code will - * be assigned to *err. - */ -static int h2_process_pending_input(struct Curl_cfilter *cf, - struct Curl_easy *data, - CURLcode *err) +/* called from http_setup_conn */ +void Curl_http2_setup_conn(struct connectdata *conn) { - struct cf_h2_ctx *ctx = cf->ctx; - const unsigned char *buf; - size_t blen; - ssize_t rv; - - while(Curl_bufq_peek(&ctx->inbufq, &buf, &blen)) { - - rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)buf, blen); - if(rv < 0) { - failf(data, - "process_pending_input: nghttp2_session_mem_recv() returned " - "%zd:%s", rv, nghttp2_strerror((int)rv)); - *err = CURLE_RECV_ERROR; - return -1; - } - Curl_bufq_skip(&ctx->inbufq, (size_t)rv); - if(Curl_bufq_is_empty(&ctx->inbufq)) { - break; - } - else { - DEBUGF(LOG_CF(data, cf, "process_pending_input: %zu bytes left " - "in connection buffer", Curl_bufq_len(&ctx->inbufq))); - } - } - - if(nghttp2_session_check_request_allowed(ctx->h2) == 0) { - /* No more requests are allowed in the current session, so - the connection may not be reused. This is set when a - GOAWAY frame has been received or when the limit of stream - identifiers has been reached. */ - connclose(cf->conn, "http/2: No new requests allowed"); - } - - return 0; + conn->proto.httpc.settings.max_concurrent_streams = + DEFAULT_MAX_CONCURRENT_STREAMS; } /* - * The server may send us data at any point (e.g. PING frames). Therefore, - * we cannot assume that an HTTP/2 socket is dead just because it is readable. - * - * Check the lower filters first and, if successful, peek at the socket - * and distinguish between closed and data. + * HTTP2 handler interface. This isn't added to the general list of protocols + * but will be used at run-time when the protocol is dynamically switched from + * HTTP to HTTP2. */ -static bool http2_connisalive(struct Curl_cfilter *cf, struct Curl_easy *data, - bool *input_pending) -{ - struct cf_h2_ctx *ctx = cf->ctx; - bool alive = TRUE; - - *input_pending = FALSE; - if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) - return FALSE; - - if(*input_pending) { - /* This happens before we've sent off a request and the connection is - not in use by any other transfer, there shouldn't be any data here, - only "protocol frames" */ - CURLcode result; - ssize_t nread = -1; - - *input_pending = FALSE; - nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result); - if(nread != -1) { - DEBUGF(LOG_CF(data, cf, "%zd bytes stray data read before trying " - "h2 connection", nread)); - if(h2_process_pending_input(cf, data, &result) < 0) - /* immediate error, considered dead */ - alive = FALSE; - else { - alive = !should_close_session(ctx); - } - } - else if(result != CURLE_AGAIN) { - /* the read failed so let's say this is dead anyway */ - alive = FALSE; - } - } - - return alive; -} - -static CURLcode http2_send_ping(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_h2_ctx *ctx = cf->ctx; - int rc; - - rc = nghttp2_submit_ping(ctx->h2, 0, ZERO_NULL); - if(rc) { - failf(data, "nghttp2_submit_ping() failed: %s(%d)", - nghttp2_strerror(rc), rc); - return CURLE_HTTP2; - } +static const struct Curl_handler Curl_handler_http2 = { + "HTTP", /* scheme */ + ZERO_NULL, /* setup_connection */ + Curl_http, /* do_it */ + Curl_http_done, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + http2_getsock, /* proto_getsock */ + http2_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + http2_getsock, /* perform_getsock */ + http2_disconnect, /* disconnect */ + ZERO_NULL, /* readwrite */ + http2_conncheck, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_HTTP, /* defport */ + CURLPROTO_HTTP, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_STREAM /* flags */ +}; - rc = nghttp2_session_send(ctx->h2); - if(rc) { - failf(data, "nghttp2_session_send() failed: %s(%d)", - nghttp2_strerror(rc), rc); - return CURLE_SEND_ERROR; - } - return CURLE_OK; -} +static const struct Curl_handler Curl_handler_http2_ssl = { + "HTTPS", /* scheme */ + ZERO_NULL, /* setup_connection */ + Curl_http, /* do_it */ + Curl_http_done, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + http2_getsock, /* proto_getsock */ + http2_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + http2_getsock, /* perform_getsock */ + http2_disconnect, /* disconnect */ + ZERO_NULL, /* readwrite */ + http2_conncheck, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_HTTP, /* defport */ + CURLPROTO_HTTPS, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_SSL | PROTOPT_STREAM /* flags */ +}; /* * Store nghttp2 version info in this buffer. @@ -639,65 +369,44 @@ void Curl_http2_ver(char *p, size_t len) (void)msnprintf(p, len, "nghttp2/%s", h2->version_str); } -static CURLcode nw_out_flush(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_h2_ctx *ctx = cf->ctx; - ssize_t nwritten; - CURLcode result; - - (void)data; - if(Curl_bufq_is_empty(&ctx->outbufq)) - return CURLE_OK; - - nwritten = Curl_bufq_pass(&ctx->outbufq, nw_out_writer, cf, &result); - if(nwritten < 0) { - if(result == CURLE_AGAIN) { - DEBUGF(LOG_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN", - Curl_bufq_len(&ctx->outbufq))); - ctx->nw_out_blocked = 1; - } - return result; - } - DEBUGF(LOG_CF(data, cf, "nw send buffer flushed")); - return Curl_bufq_is_empty(&ctx->outbufq)? CURLE_OK: CURLE_AGAIN; -} - /* * The implementation of nghttp2_send_callback type. Here we write |data| with * size |length| to the network and return the number of bytes actually * written. See the documentation of nghttp2_send_callback for the details. */ static ssize_t send_callback(nghttp2_session *h2, - const uint8_t *buf, size_t blen, int flags, + const uint8_t *mem, size_t length, int flags, void *userp) { - struct Curl_cfilter *cf = userp; - struct cf_h2_ctx *ctx = cf->ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nwritten; + struct connectdata *conn = (struct connectdata *)userp; + struct http_conn *c = &conn->proto.httpc; + struct Curl_easy *data = get_transfer(c); + ssize_t written; CURLcode result = CURLE_OK; (void)h2; (void)flags; - DEBUGASSERT(data); - nwritten = Curl_bufq_write_pass(&ctx->outbufq, buf, blen, - nw_out_writer, cf, &result); - if(nwritten < 0) { - if(result == CURLE_AGAIN) { - ctx->nw_out_blocked = 1; - return NGHTTP2_ERR_WOULDBLOCK; - } + if(!c->send_underlying) + /* called before setup properly! */ + return NGHTTP2_ERR_CALLBACK_FAILURE; + + written = ((Curl_send*)c->send_underlying)(data, FIRSTSOCKET, + mem, length, &result); + + if(result == CURLE_AGAIN) { + return NGHTTP2_ERR_WOULDBLOCK; + } + + if(written == -1) { failf(data, "Failed sending HTTP2 data"); return NGHTTP2_ERR_CALLBACK_FAILURE; } - if(!nwritten) { - ctx->nw_out_blocked = 1; + if(!written) return NGHTTP2_ERR_WOULDBLOCK; - } - return nwritten; + + return written; } @@ -718,8 +427,8 @@ char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num) if(!h || !GOOD_EASY_HANDLE(h->data)) return NULL; else { - struct stream_ctx *stream = H2_STREAM_CTX(h->data); - if(stream && num < stream->push_headers_used) + struct HTTP *stream = h->data->req.p.http; + if(num < stream->push_headers_used) return stream->push_headers[num]; } return NULL; @@ -730,9 +439,6 @@ char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num) */ char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header) { - struct stream_ctx *stream; - size_t len; - size_t i; /* Verify that we got a good easy handle in the push header struct, mostly to detect rubbish input fast(er). Also empty header name is just a rubbish too. We have to allow ":" at the beginning of @@ -742,25 +448,45 @@ char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header) if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] || !strcmp(header, ":") || strchr(header + 1, ':')) return NULL; - - stream = H2_STREAM_CTX(h->data); - if(!stream) - return NULL; - - len = strlen(header); - for(i = 0; i<stream->push_headers_used; i++) { - if(!strncmp(header, stream->push_headers[i], len)) { - /* sub-match, make sure that it is followed by a colon */ - if(stream->push_headers[i][len] != ':') - continue; - return &stream->push_headers[i][len + 1]; + else { + struct HTTP *stream = h->data->req.p.http; + size_t len = strlen(header); + size_t i; + for(i = 0; i<stream->push_headers_used; i++) { + if(!strncmp(header, stream->push_headers[i], len)) { + /* sub-match, make sure that it is followed by a colon */ + if(stream->push_headers[i][len] != ':') + continue; + return &stream->push_headers[i][len + 1]; + } } } return NULL; } -static struct Curl_easy *h2_duphandle(struct Curl_cfilter *cf, - struct Curl_easy *data) +/* + * This specific transfer on this connection has been "drained". + */ +static void drained_transfer(struct Curl_easy *data, + struct http_conn *httpc) +{ + DEBUGASSERT(httpc->drain_total >= data->state.drain); + httpc->drain_total -= data->state.drain; + data->state.drain = 0; +} + +/* + * Mark this transfer to get "drained". + */ +static void drain_this(struct Curl_easy *data, + struct http_conn *httpc) +{ + data->state.drain++; + httpc->drain_total++; + DEBUGASSERT(httpc->drain_total >= data->state.drain); +} + +static struct Curl_easy *duphandle(struct Curl_easy *data) { struct Curl_easy *second = curl_easy_duphandle(data); if(second) { @@ -770,11 +496,10 @@ static struct Curl_easy *h2_duphandle(struct Curl_cfilter *cf, (void)Curl_close(&second); } else { - struct stream_ctx *second_stream; - second->req.p.http = http; - http2_data_setup(cf, second, &second_stream); - second->state.priority.weight = data->state.priority.weight; + Curl_dyn_init(&http->header_recvbuf, DYN_H2_HEADERS); + Curl_http2_setup_req(second); + second->state.stream_weight = data->state.stream_weight; } } return second; @@ -792,7 +517,7 @@ static int set_transfer_url(struct Curl_easy *data, if(!u) return 5; - v = curl_pushheader_byname(hp, HTTP_PSEUDO_SCHEME); + v = curl_pushheader_byname(hp, H2H3_PSEUDO_SCHEME); if(v) { uc = curl_url_set(u, CURLUPART_SCHEME, v, 0); if(uc) { @@ -801,16 +526,16 @@ static int set_transfer_url(struct Curl_easy *data, } } - v = curl_pushheader_byname(hp, HTTP_PSEUDO_AUTHORITY); + v = curl_pushheader_byname(hp, H2H3_PSEUDO_AUTHORITY); if(v) { - uc = Curl_url_set_authority(u, v, CURLU_DISALLOW_USER); + uc = curl_url_set(u, CURLUPART_HOST, v, 0); if(uc) { rc = 2; goto fail; } } - v = curl_pushheader_byname(hp, HTTP_PSEUDO_PATH); + v = curl_pushheader_byname(hp, H2H3_PSEUDO_PATH); if(v) { uc = curl_url_set(u, CURLUPART_PATH, v, 0); if(uc) { @@ -822,7 +547,7 @@ static int set_transfer_url(struct Curl_easy *data, uc = curl_url_get(u, CURLUPART_URL, &url, 0); if(uc) rc = 4; -fail: + fail: curl_url_cleanup(u); if(rc) return rc; @@ -834,34 +559,22 @@ fail: return 0; } -static void discard_newhandle(struct Curl_cfilter *cf, - struct Curl_easy *newhandle) -{ - if(!newhandle->req.p.http) { - http2_data_done(cf, newhandle, TRUE); - newhandle->req.p.http = NULL; - } - (void)Curl_close(&newhandle); -} - -static int push_promise(struct Curl_cfilter *cf, - struct Curl_easy *data, +static int push_promise(struct Curl_easy *data, + struct connectdata *conn, const nghttp2_push_promise *frame) { - struct cf_h2_ctx *ctx = cf->ctx; int rv; /* one of the CURL_PUSH_* defines */ - - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] PUSH_PROMISE received", - frame->promised_stream_id)); + H2BUGF(infof(data, "PUSH_PROMISE received, stream %u", + frame->promised_stream_id)); if(data->multi->push_cb) { - struct stream_ctx *stream; - struct stream_ctx *newstream; + struct HTTP *stream; + struct HTTP *newstream; struct curl_pushheaders heads; CURLMcode rc; - CURLcode result; + struct http_conn *httpc; size_t i; /* clone the parent */ - struct Curl_easy *newhandle = h2_duphandle(cf, data); + struct Curl_easy *newhandle = duphandle(data); if(!newhandle) { infof(data, "failed to duplicate handle"); rv = CURL_PUSH_DENY; /* FAIL HARD */ @@ -871,32 +584,23 @@ static int push_promise(struct Curl_cfilter *cf, heads.data = data; heads.frame = frame; /* ask the application */ - DEBUGF(LOG_CF(data, cf, "Got PUSH_PROMISE, ask application")); + H2BUGF(infof(data, "Got PUSH_PROMISE, ask application")); - stream = H2_STREAM_CTX(data); + stream = data->req.p.http; if(!stream) { failf(data, "Internal NULL stream"); - discard_newhandle(cf, newhandle); + (void)Curl_close(&newhandle); rv = CURL_PUSH_DENY; goto fail; } rv = set_transfer_url(newhandle, &heads); if(rv) { - discard_newhandle(cf, newhandle); + (void)Curl_close(&newhandle); rv = CURL_PUSH_DENY; goto fail; } - result = http2_data_setup(cf, newhandle, &newstream); - if(result) { - failf(data, "error setting up stream: %d", result); - discard_newhandle(cf, newhandle); - rv = CURL_PUSH_DENY; - goto fail; - } - DEBUGASSERT(stream); - Curl_set_in_callback(data, true); rv = data->multi->push_cb(data, newhandle, stream->push_headers_used, &heads, @@ -913,116 +617,132 @@ static int push_promise(struct Curl_cfilter *cf, if(rv) { DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT)); /* denied, kill off the new handle again */ - discard_newhandle(cf, newhandle); + http2_stream_free(newhandle->req.p.http); + newhandle->req.p.http = NULL; + (void)Curl_close(&newhandle); goto fail; } - newstream->id = frame->promised_stream_id; + newstream = newhandle->req.p.http; + newstream->stream_id = frame->promised_stream_id; newhandle->req.maxdownload = -1; newhandle->req.size = -1; /* approved, add to the multi handle and immediately switch to PERFORM state with the given connection !*/ - rc = Curl_multi_add_perform(data->multi, newhandle, cf->conn); + rc = Curl_multi_add_perform(data->multi, newhandle, conn); if(rc) { infof(data, "failed to add handle to multi"); - discard_newhandle(cf, newhandle); + http2_stream_free(newhandle->req.p.http); + newhandle->req.p.http = NULL; + Curl_close(&newhandle); rv = CURL_PUSH_DENY; goto fail; } - rv = nghttp2_session_set_stream_user_data(ctx->h2, - newstream->id, + httpc = &conn->proto.httpc; + rv = nghttp2_session_set_stream_user_data(httpc->h2, + frame->promised_stream_id, newhandle); if(rv) { infof(data, "failed to set user_data for stream %u", - newstream->id); + frame->promised_stream_id); DEBUGASSERT(0); rv = CURL_PUSH_DENY; goto fail; } + Curl_dyn_init(&newstream->header_recvbuf, DYN_H2_HEADERS); + Curl_dyn_init(&newstream->trailer_recvbuf, DYN_H2_TRAILERS); } else { - DEBUGF(LOG_CF(data, cf, "Got PUSH_PROMISE, ignore it")); + H2BUGF(infof(data, "Got PUSH_PROMISE, ignore it")); rv = CURL_PUSH_DENY; } -fail: + fail: return rv; } -static CURLcode recvbuf_write_hds(struct Curl_cfilter *cf, - struct Curl_easy *data, - const char *buf, size_t blen) +/* + * multi_connchanged() is called to tell that there is a connection in + * this multi handle that has changed state (multiplexing become possible, the + * number of allowed streams changed or similar), and a subsequent use of this + * multi handle should move CONNECT_PEND handles back to CONNECT to have them + * retry. + */ +static void multi_connchanged(struct Curl_multi *multi) { - struct stream_ctx *stream = H2_STREAM_CTX(data); - ssize_t nwritten; - CURLcode result; - - (void)cf; - nwritten = Curl_bufq_write(&stream->recvbuf, - (const unsigned char *)buf, blen, &result); - if(nwritten < 0) - return result; - stream->resp_hds_len += (size_t)nwritten; - DEBUGASSERT((size_t)nwritten == blen); - return CURLE_OK; + multi->recheckstate = TRUE; } -static CURLcode on_stream_frame(struct Curl_cfilter *cf, - struct Curl_easy *data, - const nghttp2_frame *frame) +static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, + void *userp) { - struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H2_STREAM_CTX(data); + struct connectdata *conn = (struct connectdata *)userp; + struct http_conn *httpc = &conn->proto.httpc; + struct Curl_easy *data_s = NULL; + struct HTTP *stream = NULL; + struct Curl_easy *data = get_transfer(httpc); + int rv; + size_t left, ncopy; int32_t stream_id = frame->hd.stream_id; CURLcode result; - size_t rbuflen; - int rv; + if(!stream_id) { + /* stream ID zero is for connection-oriented stuff */ + if(frame->hd.type == NGHTTP2_SETTINGS) { + uint32_t max_conn = httpc->settings.max_concurrent_streams; + H2BUGF(infof(data, "Got SETTINGS")); + httpc->settings.max_concurrent_streams = + nghttp2_session_get_remote_settings( + session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); + httpc->settings.enable_push = + nghttp2_session_get_remote_settings( + session, NGHTTP2_SETTINGS_ENABLE_PUSH); + H2BUGF(infof(data, "MAX_CONCURRENT_STREAMS == %d", + httpc->settings.max_concurrent_streams)); + H2BUGF(infof(data, "ENABLE_PUSH == %s", + httpc->settings.enable_push?"TRUE":"false")); + if(max_conn != httpc->settings.max_concurrent_streams) { + /* only signal change if the value actually changed */ + infof(data, + "Connection state changed (MAX_CONCURRENT_STREAMS == %u)!", + httpc->settings.max_concurrent_streams); + multi_connchanged(data->multi); + } + } + return 0; + } + data_s = nghttp2_session_get_stream_user_data(session, stream_id); + if(!data_s) { + H2BUGF(infof(data, + "No Curl_easy associated with stream: %u", + stream_id)); + return 0; + } + + stream = data_s->req.p.http; if(!stream) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] No proto pointer", stream_id)); - return CURLE_FAILED_INIT; + H2BUGF(infof(data_s, "No proto pointer for stream: %u", + stream_id)); + return NGHTTP2_ERR_CALLBACK_FAILURE; } + H2BUGF(infof(data_s, "on_frame_recv() header %x stream %u", + frame->hd.type, stream_id)); + switch(frame->hd.type) { case NGHTTP2_DATA: - rbuflen = Curl_bufq_len(&stream->recvbuf); - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[DATA len=%zu pad=%zu], " - "buffered=%zu, window=%d/%d", - stream_id, frame->hd.length, frame->data.padlen, rbuflen, - nghttp2_session_get_stream_effective_recv_data_length( - ctx->h2, stream->id), - nghttp2_session_get_stream_effective_local_window_size( - ctx->h2, stream->id))); - /* If !body started on this stream, then receiving DATA is illegal. */ + /* If body started on this stream, then receiving DATA is illegal. */ if(!stream->bodystarted) { - rv = nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE, + rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id, NGHTTP2_PROTOCOL_ERROR); if(nghttp2_is_fatal(rv)) { - return CURLE_RECV_ERROR; - } - } - if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - drain_stream(cf, data, stream); - } - else if(rbuflen > stream->local_window_size) { - int32_t wsize = nghttp2_session_get_stream_local_window_size( - ctx->h2, stream->id); - if(wsize > 0 && (uint32_t)wsize != stream->local_window_size) { - /* H2 flow control is not absolute, as the server might not have the - * same view, yet. When we recieve more than we want, we enforce - * the local window size again to make nghttp2 send WINDOW_UPATEs - * accordingly. */ - nghttp2_session_set_local_window_size(ctx->h2, - NGHTTP2_FLAG_NONE, - stream->id, - stream->local_window_size); + return NGHTTP2_ERR_CALLBACK_FAILURE; } } break; case NGHTTP2_HEADERS: - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[HEADERS]", stream_id)); if(stream->bodystarted) { /* Only valid HEADERS after body started is trailer HEADERS. We buffer them in on_header callback. */ @@ -1033,7 +753,7 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, stream->status_code. Fuzzing has proven this can still be reached without status code having been set. */ if(stream->status_code == -1) - return CURLE_RECV_ERROR; + return NGHTTP2_ERR_CALLBACK_FAILURE; /* Only final status code signals the end of header */ if(stream->status_code / 100 != 1) { @@ -1041,148 +761,69 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, stream->status_code = -1; } - result = recvbuf_write_hds(cf, data, STRCONST("\r\n")); + result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST("\r\n")); if(result) - return result; + return NGHTTP2_ERR_CALLBACK_FAILURE; + + left = Curl_dyn_len(&stream->header_recvbuf) - + stream->nread_header_recvbuf; + ncopy = CURLMIN(stream->len, left); + + memcpy(&stream->mem[stream->memlen], + Curl_dyn_ptr(&stream->header_recvbuf) + + stream->nread_header_recvbuf, + ncopy); + stream->nread_header_recvbuf += ncopy; + + DEBUGASSERT(stream->mem); + H2BUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p", + ncopy, stream_id, stream->mem)); + + stream->len -= ncopy; + stream->memlen += ncopy; - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] %zu header bytes", - stream_id, Curl_bufq_len(&stream->recvbuf))); - drain_stream(cf, data, stream); + drain_this(data_s, httpc); + /* if we receive data for another handle, wake that up */ + if(get_transfer(httpc) != data_s) + Curl_expire(data_s, 0, EXPIRE_RUN_NOW); break; case NGHTTP2_PUSH_PROMISE: - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[PUSH_PROMISE]", stream_id)); - rv = push_promise(cf, data, &frame->push_promise); + rv = push_promise(data_s, conn, &frame->push_promise); if(rv) { /* deny! */ + int h2; DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT)); - rv = nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE, + h2 = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->push_promise.promised_stream_id, NGHTTP2_CANCEL); - if(nghttp2_is_fatal(rv)) - return CURLE_SEND_ERROR; + if(nghttp2_is_fatal(h2)) + return NGHTTP2_ERR_CALLBACK_FAILURE; else if(rv == CURL_PUSH_ERROROUT) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] fail in PUSH_PROMISE received", - stream_id)); - return CURLE_RECV_ERROR; + DEBUGF(infof(data_s, "Fail the parent stream (too)")); + return NGHTTP2_ERR_CALLBACK_FAILURE; } } break; - case NGHTTP2_RST_STREAM: - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[RST]", stream_id)); - stream->closed = TRUE; - stream->reset = TRUE; - stream->send_closed = TRUE; - data->req.keepon &= ~KEEP_SEND_HOLD; - drain_stream(cf, data, stream); - break; - case NGHTTP2_WINDOW_UPDATE: - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[WINDOW_UPDATE]", stream_id)); - if((data->req.keepon & KEEP_SEND_HOLD) && - (data->req.keepon & KEEP_SEND)) { - data->req.keepon &= ~KEEP_SEND_HOLD; - drain_stream(cf, data, stream); - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] un-holding after win update", - stream_id)); - } - break; default: - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[%x]", - stream_id, frame->hd.type)); + H2BUGF(infof(data_s, "Got frame type %x for stream %u", + frame->hd.type, stream_id)); break; } - return CURLE_OK; -} - -static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, - void *userp) -{ - struct Curl_cfilter *cf = userp; - struct cf_h2_ctx *ctx = cf->ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf), *data_s; - int32_t stream_id = frame->hd.stream_id; - - DEBUGASSERT(data); - if(!stream_id) { - /* stream ID zero is for connection-oriented stuff */ - DEBUGASSERT(data); - switch(frame->hd.type) { - case NGHTTP2_SETTINGS: { - uint32_t max_conn = ctx->max_concurrent_streams; - DEBUGF(LOG_CF(data, cf, "FRAME[SETTINGS]")); - ctx->max_concurrent_streams = nghttp2_session_get_remote_settings( - session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); - ctx->enable_push = nghttp2_session_get_remote_settings( - session, NGHTTP2_SETTINGS_ENABLE_PUSH) != 0; - DEBUGF(LOG_CF(data, cf, "MAX_CONCURRENT_STREAMS == %d", - ctx->max_concurrent_streams)); - DEBUGF(LOG_CF(data, cf, "ENABLE_PUSH == %s", - ctx->enable_push ? "TRUE" : "false")); - if(data && max_conn != ctx->max_concurrent_streams) { - /* only signal change if the value actually changed */ - DEBUGF(LOG_CF(data, cf, "MAX_CONCURRENT_STREAMS now %u", - ctx->max_concurrent_streams)); - multi_connchanged(data->multi); - } - /* Since the initial stream window is 64K, a request might be on HOLD, - * due to exhaustion. The (initial) SETTINGS may announce a much larger - * window and *assume* that we treat this like a WINDOW_UPDATE. Some - * servers send an explicit WINDOW_UPDATE, but not all seem to do that. - * To be safe, we UNHOLD a stream in order not to stall. */ - if((data->req.keepon & KEEP_SEND_HOLD) && - (data->req.keepon & KEEP_SEND)) { - struct stream_ctx *stream = H2_STREAM_CTX(data); - data->req.keepon &= ~KEEP_SEND_HOLD; - if(stream) { - drain_stream(cf, data, stream); - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] un-holding after SETTINGS", - stream_id)); - } - } - break; - } - case NGHTTP2_GOAWAY: - ctx->goaway = TRUE; - ctx->goaway_error = frame->goaway.error_code; - ctx->last_stream_id = frame->goaway.last_stream_id; - if(data) { - DEBUGF(LOG_CF(data, cf, "FRAME[GOAWAY, error=%d, last_stream=%u]", - ctx->goaway_error, ctx->last_stream_id)); - infof(data, "received GOAWAY, error=%d, last_stream=%u", - ctx->goaway_error, ctx->last_stream_id); - multi_connchanged(data->multi); - } - break; - case NGHTTP2_WINDOW_UPDATE: - DEBUGF(LOG_CF(data, cf, "FRAME[WINDOW_UPDATE]")); - break; - default: - DEBUGF(LOG_CF(data, cf, "recv frame %x on 0", frame->hd.type)); - } - return 0; - } - - data_s = nghttp2_session_get_stream_user_data(session, stream_id); - if(!data_s) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] No Curl_easy associated", - stream_id)); - return 0; - } - - return on_stream_frame(cf, data_s, frame)? NGHTTP2_ERR_CALLBACK_FAILURE : 0; + return 0; } static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *mem, size_t len, void *userp) { - struct Curl_cfilter *cf = userp; - struct stream_ctx *stream; + struct HTTP *stream; struct Curl_easy *data_s; - ssize_t nwritten; - CURLcode result; + size_t nread; + struct connectdata *conn = (struct connectdata *)userp; + struct http_conn *httpc = &conn->proto.httpc; + (void)session; (void)flags; DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ - DEBUGASSERT(CF_DATA_CURRENT(cf)); /* get the stream from the hash based on Stream ID */ data_s = nghttp2_session_get_stream_user_data(session, stream_id); @@ -1190,93 +831,123 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, /* Receiving a Stream ID not in the hash should not happen - unless we have aborted a transfer artificially and there were more data in the pipeline. Silently ignore. */ - DEBUGF(LOG_CF(CF_DATA_CURRENT(cf), cf, "[h2sid=%d] Data for unknown", - stream_id)); - /* consumed explicitly as no one will read it */ - nghttp2_session_consume(session, stream_id, len); + H2BUGF(fprintf(stderr, "Data for stream %u but it doesn't exist\n", + stream_id)); return 0; } - stream = H2_STREAM_CTX(data_s); + stream = data_s->req.p.http; if(!stream) return NGHTTP2_ERR_CALLBACK_FAILURE; - nwritten = Curl_bufq_write(&stream->recvbuf, mem, len, &result); - if(nwritten < 0) { - if(result != CURLE_AGAIN) - return NGHTTP2_ERR_CALLBACK_FAILURE; + nread = CURLMIN(stream->len, len); + memcpy(&stream->mem[stream->memlen], mem, nread); - nwritten = 0; - } + stream->len -= nread; + stream->memlen += nread; + + drain_this(data_s, &conn->proto.httpc); /* if we receive data for another handle, wake that up */ - drain_stream(cf, data_s, stream); + if(get_transfer(httpc) != data_s) + Curl_expire(data_s, 0, EXPIRE_RUN_NOW); + + H2BUGF(infof(data_s, "%zu data received for stream %u " + "(%zu left in buffer %p, total %zu)", + nread, stream_id, + stream->len, stream->mem, + stream->memlen)); + + if(nread < len) { + stream->pausedata = mem + nread; + stream->pauselen = len - nread; + H2BUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer" + ", stream %u", + len - nread, stream_id)); + data_s->conn->proto.httpc.pause_stream_id = stream_id; + + return NGHTTP2_ERR_PAUSE; + } + + /* pause execution of nghttp2 if we received data for another handle + in order to process them first. */ + if(get_transfer(httpc) != data_s) { + data_s->conn->proto.httpc.pause_stream_id = stream_id; + + return NGHTTP2_ERR_PAUSE; + } - DEBUGASSERT((size_t)nwritten == len); return 0; } static int on_stream_close(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *userp) { - struct Curl_cfilter *cf = userp; struct Curl_easy *data_s; - struct stream_ctx *stream; + struct HTTP *stream; + struct connectdata *conn = (struct connectdata *)userp; int rv; (void)session; + (void)stream_id; - /* get the stream from the hash based on Stream ID, stream ID zero is for - connection-oriented stuff */ - data_s = stream_id? - nghttp2_session_get_stream_user_data(session, stream_id) : NULL; - if(!data_s) { - return 0; - } - stream = H2_STREAM_CTX(data_s); - DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] on_stream_close(), %s (err %d)", - stream_id, nghttp2_http2_strerror(error_code), error_code)); - if(!stream) - return NGHTTP2_ERR_CALLBACK_FAILURE; - - stream->closed = TRUE; - stream->error = error_code; - if(stream->error) - stream->reset = TRUE; - data_s->req.keepon &= ~KEEP_SEND_HOLD; + if(stream_id) { + struct http_conn *httpc; + /* get the stream from the hash based on Stream ID, stream ID zero is for + connection-oriented stuff */ + data_s = nghttp2_session_get_stream_user_data(session, stream_id); + if(!data_s) { + /* We could get stream ID not in the hash. For example, if we + decided to reject stream (e.g., PUSH_PROMISE). */ + return 0; + } + H2BUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u", + nghttp2_http2_strerror(error_code), error_code, stream_id)); + stream = data_s->req.p.http; + if(!stream) + return NGHTTP2_ERR_CALLBACK_FAILURE; - drain_stream(cf, data_s, stream); + stream->closed = TRUE; + httpc = &conn->proto.httpc; + drain_this(data_s, httpc); + Curl_expire(data_s, 0, EXPIRE_RUN_NOW); + stream->error = error_code; - /* remove `data_s` from the nghttp2 stream */ - rv = nghttp2_session_set_stream_user_data(session, stream_id, 0); - if(rv) { - infof(data_s, "http/2: failed to clear user_data for stream %u", - stream_id); - DEBUGASSERT(0); + /* remove the entry from the hash as the stream is now gone */ + rv = nghttp2_session_set_stream_user_data(session, stream_id, 0); + if(rv) { + infof(data_s, "http/2: failed to clear user_data for stream %u", + stream_id); + DEBUGASSERT(0); + } + if(stream_id == httpc->pause_stream_id) { + H2BUGF(infof(data_s, "Stopped the pause stream")); + httpc->pause_stream_id = 0; + } + H2BUGF(infof(data_s, "Removed stream %u hash", stream_id)); + stream->stream_id = 0; /* cleared */ } - DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] closed now", stream_id)); return 0; } static int on_begin_headers(nghttp2_session *session, const nghttp2_frame *frame, void *userp) { - struct Curl_cfilter *cf = userp; - struct stream_ctx *stream; + struct HTTP *stream; struct Curl_easy *data_s = NULL; + (void)userp; - (void)cf; data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); if(!data_s) { return 0; } - DEBUGF(LOG_CF(data_s, cf, "on_begin_headers() was called")); + H2BUGF(infof(data_s, "on_begin_headers() was called")); if(frame->hd.type != NGHTTP2_HEADERS) { return 0; } - stream = H2_STREAM_CTX(data_s); + stream = data_s->req.p.http; if(!stream || !stream->bodystarted) { return 0; } @@ -1284,6 +955,33 @@ static int on_begin_headers(nghttp2_session *session, return 0; } +/* Decode HTTP status code. Returns -1 if no valid status code was + decoded. */ +static int decode_status_code(const uint8_t *value, size_t len) +{ + int i; + int res; + + if(len != 3) { + return -1; + } + + res = 0; + + for(i = 0; i < 3; ++i) { + char c = value[i]; + + if(c < '0' || c > '9') { + return -1; + } + + res *= 10; + res += c - '0'; + } + + return res; +} + /* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, @@ -1291,10 +989,11 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, uint8_t flags, void *userp) { - struct Curl_cfilter *cf = userp; - struct stream_ctx *stream; + struct HTTP *stream; struct Curl_easy *data_s; int32_t stream_id = frame->hd.stream_id; + struct connectdata *conn = (struct connectdata *)userp; + struct http_conn *httpc = &conn->proto.httpc; CURLcode result; (void)flags; @@ -1307,7 +1006,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, internal error more than anything else! */ return NGHTTP2_ERR_CALLBACK_FAILURE; - stream = H2_STREAM_CTX(data_s); + stream = data_s->req.p.http; if(!stream) { failf(data_s, "Internal NULL stream"); return NGHTTP2_ERR_CALLBACK_FAILURE; @@ -1318,17 +1017,16 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(frame->hd.type == NGHTTP2_PUSH_PROMISE) { char *h; - if(!strcmp(HTTP_PSEUDO_AUTHORITY, (const char *)name)) { + if(!strcmp(H2H3_PSEUDO_AUTHORITY, (const char *)name)) { /* pseudo headers are lower case */ int rc = 0; - char *check = aprintf("%s:%d", cf->conn->host.name, - cf->conn->remote_port); + char *check = aprintf("%s:%d", conn->host.name, conn->remote_port); if(!check) /* no memory */ return NGHTTP2_ERR_CALLBACK_FAILURE; - if(!strcasecompare(check, (const char *)value) && - ((cf->conn->remote_port != cf->conn->given->defport) || - !strcasecompare(cf->conn->host.name, (const char *)value))) { + if(!Curl_strcasecompare(check, (const char *)value) && + ((conn->remote_port != conn->given->defport) || + !Curl_strcasecompare(conn->host.name, (const char *)value))) { /* This is push is not for the same authority that was asked for in * the URL. RFC 7540 section 8.2 says: "A client MUST treat a * PUSH_PROMISE for which the server is not authoritative as a stream @@ -1377,93 +1075,87 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(stream->bodystarted) { /* This is a trailer */ - DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] trailer: %.*s: %.*s", - stream->id, - (int)namelen, name, - (int)valuelen, value)); - result = Curl_dynhds_add(&stream->resp_trailers, - (const char *)name, namelen, - (const char *)value, valuelen); + H2BUGF(infof(data_s, "h2 trailer: %.*s: %.*s", namelen, name, valuelen, + value)); + result = Curl_dyn_addf(&stream->trailer_recvbuf, + "%.*s: %.*s\r\n", namelen, name, + valuelen, value); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; return 0; } - if(namelen == sizeof(HTTP_PSEUDO_STATUS) - 1 && - memcmp(HTTP_PSEUDO_STATUS, name, namelen) == 0) { - /* nghttp2 guarantees :status is received first and only once. */ + if(namelen == sizeof(H2H3_PSEUDO_STATUS) - 1 && + memcmp(H2H3_PSEUDO_STATUS, name, namelen) == 0) { + /* nghttp2 guarantees :status is received first and only once, and + value is 3 digits status code, and decode_status_code always + succeeds. */ char buffer[32]; - result = Curl_http_decode_status(&stream->status_code, - (const char *)value, valuelen); - if(result) - return NGHTTP2_ERR_CALLBACK_FAILURE; - msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%u\r", + stream->status_code = decode_status_code(value, valuelen); + DEBUGASSERT(stream->status_code != -1); + msnprintf(buffer, sizeof(buffer), H2H3_PSEUDO_STATUS ":%u\r", stream->status_code); result = Curl_headers_push(data_s, buffer, CURLH_PSEUDO); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; - result = recvbuf_write_hds(cf, data_s, STRCONST("HTTP/2 ")); + result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST("HTTP/2 ")); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; - result = recvbuf_write_hds(cf, data_s, (const char *)value, valuelen); + result = Curl_dyn_addn(&stream->header_recvbuf, value, valuelen); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; /* the space character after the status code is mandatory */ - result = recvbuf_write_hds(cf, data_s, STRCONST(" \r\n")); + result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST(" \r\n")); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; /* if we receive data for another handle, wake that up */ - if(CF_DATA_CURRENT(cf) != data_s) + if(get_transfer(httpc) != data_s) Curl_expire(data_s, 0, EXPIRE_RUN_NOW); - DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] status: HTTP/2 %03d", - stream->id, stream->status_code)); + H2BUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)", + stream->status_code, data_s)); return 0; } /* nghttp2 guarantees that namelen > 0, and :status was already received, and this is not pseudo-header field . */ - /* convert to an HTTP1-style header */ - result = recvbuf_write_hds(cf, data_s, (const char *)name, namelen); + /* convert to a HTTP1-style header */ + result = Curl_dyn_addn(&stream->header_recvbuf, name, namelen); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; - result = recvbuf_write_hds(cf, data_s, STRCONST(": ")); + result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST(": ")); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; - result = recvbuf_write_hds(cf, data_s, (const char *)value, valuelen); + result = Curl_dyn_addn(&stream->header_recvbuf, value, valuelen); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; - result = recvbuf_write_hds(cf, data_s, STRCONST("\r\n")); + result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST("\r\n")); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; /* if we receive data for another handle, wake that up */ - if(CF_DATA_CURRENT(cf) != data_s) + if(get_transfer(httpc) != data_s) Curl_expire(data_s, 0, EXPIRE_RUN_NOW); - DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] header: %.*s: %.*s", - stream->id, - (int)namelen, name, - (int)valuelen, value)); + H2BUGF(infof(data_s, "h2 header: %.*s: %.*s", namelen, name, valuelen, + value)); return 0; /* 0 is successful */ } -static ssize_t req_body_read_callback(nghttp2_session *session, - int32_t stream_id, - uint8_t *buf, size_t length, - uint32_t *data_flags, - nghttp2_data_source *source, - void *userp) +static ssize_t data_source_read_callback(nghttp2_session *session, + int32_t stream_id, + uint8_t *buf, size_t length, + uint32_t *data_flags, + nghttp2_data_source *source, + void *userp) { - struct Curl_cfilter *cf = userp; struct Curl_easy *data_s; - struct stream_ctx *stream = NULL; - CURLcode result; - ssize_t nread; + struct HTTP *stream = NULL; + size_t nread; (void)source; + (void)userp; - (void)cf; if(stream_id) { /* get the stream from the hash based on Stream ID, stream ID zero is for connection-oriented stuff */ @@ -1473,32 +1165,31 @@ static ssize_t req_body_read_callback(nghttp2_session *session, internal error more than anything else! */ return NGHTTP2_ERR_CALLBACK_FAILURE; - stream = H2_STREAM_CTX(data_s); + stream = data_s->req.p.http; if(!stream) return NGHTTP2_ERR_CALLBACK_FAILURE; } else return NGHTTP2_ERR_INVALID_ARGUMENT; - nread = Curl_bufq_read(&stream->sendbuf, buf, length, &result); - if(nread < 0) { - if(result != CURLE_AGAIN) - return NGHTTP2_ERR_CALLBACK_FAILURE; - nread = 0; + nread = CURLMIN(stream->upload_len, length); + if(nread > 0) { + memcpy(buf, stream->upload_mem, nread); + stream->upload_mem += nread; + stream->upload_len -= nread; + if(data_s->state.infilesize != -1) + stream->upload_left -= nread; } - if(nread > 0 && stream->upload_left != -1) - stream->upload_left -= nread; - - DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] req_body_read(len=%zu) left=%" - CURL_FORMAT_CURL_OFF_T " -> %zd, %d", - stream_id, length, stream->upload_left, nread, result)); - if(stream->upload_left == 0) *data_flags = NGHTTP2_DATA_FLAG_EOF; else if(nread == 0) return NGHTTP2_ERR_DEFERRED; + H2BUGF(infof(data_s, "data_source_read_callback: " + "returns %zu bytes stream %u", + nread, stream_id)); + return nread; } @@ -1516,25 +1207,176 @@ static int error_callback(nghttp2_session *session, } #endif +static void populate_settings(struct Curl_easy *data, + struct http_conn *httpc) +{ + nghttp2_settings_entry *iv = httpc->local_settings; + + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[0].value = Curl_multi_max_concurrent_streams(data->multi); + + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = HTTP2_HUGE_WINDOW_SIZE; + + iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; + iv[2].value = data->multi->push_cb != NULL; + + httpc->local_settings_num = 3; +} + +void Curl_http2_done(struct Curl_easy *data, bool premature) +{ + struct HTTP *http = data->req.p.http; + struct http_conn *httpc = &data->conn->proto.httpc; + + /* there might be allocated resources done before this got the 'h2' pointer + setup */ + Curl_dyn_free(&http->header_recvbuf); + Curl_dyn_free(&http->trailer_recvbuf); + if(http->push_headers) { + /* if they weren't used and then freed before */ + for(; http->push_headers_used > 0; --http->push_headers_used) { + free(http->push_headers[http->push_headers_used - 1]); + } + free(http->push_headers); + http->push_headers = NULL; + } + + if(!(data->conn->handler->protocol&PROTO_FAMILY_HTTP) || + !httpc->h2) /* not HTTP/2 ? */ + return; + + /* do this before the reset handling, as that might clear ->stream_id */ + if(http->stream_id == httpc->pause_stream_id) { + H2BUGF(infof(data, "DONE the pause stream (%u)", http->stream_id)); + httpc->pause_stream_id = 0; + } + if(premature || (!http->closed && http->stream_id)) { + /* RST_STREAM */ + set_transfer(httpc, data); /* set the transfer */ + H2BUGF(infof(data, "RST stream %u", http->stream_id)); + if(!nghttp2_submit_rst_stream(httpc->h2, NGHTTP2_FLAG_NONE, + http->stream_id, NGHTTP2_STREAM_CLOSED)) + (void)nghttp2_session_send(httpc->h2); + } + + if(data->state.drain) + drained_transfer(data, httpc); + + /* -1 means unassigned and 0 means cleared */ + if(http->stream_id > 0) { + int rv = nghttp2_session_set_stream_user_data(httpc->h2, + http->stream_id, 0); + if(rv) { + infof(data, "http/2: failed to clear user_data for stream %u", + http->stream_id); + DEBUGASSERT(0); + } + set_transfer(httpc, NULL); + http->stream_id = 0; + } +} + +static int client_new(struct connectdata *conn, + nghttp2_session_callbacks *callbacks) +{ +#if NGHTTP2_VERSION_NUM < 0x013200 + /* before 1.50.0 */ + return nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn); +#else + nghttp2_option *o; + int rc = nghttp2_option_new(&o); + if(rc) + return rc; + /* turn off RFC 9113 leading and trailing white spaces validation against + HTTP field value. */ + nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1); + rc = nghttp2_session_client_new2(&conn->proto.httpc.h2, callbacks, conn, + o); + nghttp2_option_del(o); + return rc; +#endif +} + /* - * Append headers to ask for an HTTP1.1 to HTTP2 upgrade. + * Initialize nghttp2 for a Curl connection + */ +static CURLcode http2_init(struct Curl_easy *data, struct connectdata *conn) +{ + if(!conn->proto.httpc.h2) { + int rc; + nghttp2_session_callbacks *callbacks; + + conn->proto.httpc.inbuf = malloc(H2_BUFSIZE); + if(!conn->proto.httpc.inbuf) + return CURLE_OUT_OF_MEMORY; + + rc = nghttp2_session_callbacks_new(&callbacks); + + if(rc) { + failf(data, "Couldn't initialize nghttp2 callbacks"); + return CURLE_OUT_OF_MEMORY; /* most likely at least */ + } + + /* nghttp2_send_callback */ + nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); + /* nghttp2_on_frame_recv_callback */ + nghttp2_session_callbacks_set_on_frame_recv_callback + (callbacks, on_frame_recv); + /* nghttp2_on_data_chunk_recv_callback */ + nghttp2_session_callbacks_set_on_data_chunk_recv_callback + (callbacks, on_data_chunk_recv); + /* nghttp2_on_stream_close_callback */ + nghttp2_session_callbacks_set_on_stream_close_callback + (callbacks, on_stream_close); + /* nghttp2_on_begin_headers_callback */ + nghttp2_session_callbacks_set_on_begin_headers_callback + (callbacks, on_begin_headers); + /* nghttp2_on_header_callback */ + nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header); + + nghttp2_session_callbacks_set_error_callback(callbacks, error_callback); + + /* The nghttp2 session is not yet setup, do it */ + rc = client_new(conn, callbacks); + + nghttp2_session_callbacks_del(callbacks); + + if(rc) { + failf(data, "Couldn't initialize nghttp2"); + return CURLE_OUT_OF_MEMORY; /* most likely at least */ + } + } + return CURLE_OK; +} + +/* + * Append headers to ask for a HTTP1.1 to HTTP2 upgrade. */ CURLcode Curl_http2_request_upgrade(struct dynbuf *req, struct Curl_easy *data) { CURLcode result; + ssize_t binlen; char *base64; size_t blen; + struct connectdata *conn = data->conn; struct SingleRequest *k = &data->req; - uint8_t binsettings[H2_BINSETTINGS_LEN]; - size_t binlen; /* length of the binsettings data */ + uint8_t *binsettings = conn->proto.httpc.binsettings; + struct http_conn *httpc = &conn->proto.httpc; - binlen = populate_binsettings(binsettings, data); + populate_settings(data, httpc); + + /* this returns number of bytes it wrote */ + binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN, + httpc->local_settings, + httpc->local_settings_num); if(binlen <= 0) { failf(data, "nghttp2 unexpectedly failed on pack_settings_payload"); Curl_dyn_free(req); return CURLE_FAILED_INIT; } + conn->proto.httpc.binlen = binlen; result = Curl_base64url_encode((const char *)binsettings, binlen, &base64, &blen); @@ -1555,119 +1397,208 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req, return result; } -static CURLcode http2_data_done_send(struct Curl_cfilter *cf, - struct Curl_easy *data) +/* + * Returns nonzero if current HTTP/2 session should be closed. + */ +static int should_close_session(struct http_conn *httpc) +{ + return httpc->drain_total == 0 && !nghttp2_session_want_read(httpc->h2) && + !nghttp2_session_want_write(httpc->h2); +} + +/* + * h2_process_pending_input() processes pending input left in + * httpc->inbuf. Then, call h2_session_send() to send pending data. + * This function returns 0 if it succeeds, or -1 and error code will + * be assigned to *err. + */ +static int h2_process_pending_input(struct Curl_easy *data, + struct http_conn *httpc, + CURLcode *err) +{ + ssize_t nread; + char *inbuf; + ssize_t rv; + + nread = httpc->inbuflen - httpc->nread_inbuf; + inbuf = httpc->inbuf + httpc->nread_inbuf; + + set_transfer(httpc, data); /* set the transfer */ + rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread); + if(rv < 0) { + failf(data, + "h2_process_pending_input: nghttp2_session_mem_recv() returned " + "%zd:%s", rv, nghttp2_strerror((int)rv)); + *err = CURLE_RECV_ERROR; + return -1; + } + + if(nread == rv) { + H2BUGF(infof(data, + "h2_process_pending_input: All data in connection buffer " + "processed")); + httpc->inbuflen = 0; + httpc->nread_inbuf = 0; + } + else { + httpc->nread_inbuf += rv; + H2BUGF(infof(data, + "h2_process_pending_input: %zu bytes left in connection " + "buffer", + httpc->inbuflen - httpc->nread_inbuf)); + } + + rv = h2_session_send(data, httpc->h2); + if(rv) { + *err = CURLE_SEND_ERROR; + return -1; + } + + if(nghttp2_session_check_request_allowed(httpc->h2) == 0) { + /* No more requests are allowed in the current session, so + the connection may not be reused. This is set when a + GOAWAY frame has been received or when the limit of stream + identifiers has been reached. */ + connclose(data->conn, "http/2: No new requests allowed"); + } + + if(should_close_session(httpc)) { + struct HTTP *stream = data->req.p.http; + H2BUGF(infof(data, + "h2_process_pending_input: nothing to do in this session")); + if(stream->error) + *err = CURLE_HTTP2; + else { + /* not an error per se, but should still close the connection */ + connclose(data->conn, "GOAWAY received"); + *err = CURLE_OK; + } + return -1; + } + return 0; +} + +/* + * Called from transfer.c:done_sending when we stop uploading. + */ +CURLcode Curl_http2_done_sending(struct Curl_easy *data, + struct connectdata *conn) { - struct cf_h2_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; - struct stream_ctx *stream = H2_STREAM_CTX(data); - if(!ctx || !ctx->h2 || !stream) - goto out; + if((conn->handler == &Curl_handler_http2_ssl) || + (conn->handler == &Curl_handler_http2)) { + /* make sure this is only attempted for HTTP/2 transfers */ + struct HTTP *stream = data->req.p.http; + struct http_conn *httpc = &conn->proto.httpc; + nghttp2_session *h2 = httpc->h2; - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] data done send", stream->id)); - if(!stream->send_closed) { - stream->send_closed = TRUE; if(stream->upload_left) { - /* we now know that everything that is buffered is all there is. */ - stream->upload_left = Curl_bufq_len(&stream->sendbuf); + /* If the stream still thinks there's data left to upload. */ + + stream->upload_left = 0; /* DONE! */ + /* resume sending here to trigger the callback to get called again so that it can signal EOF to nghttp2 */ - (void)nghttp2_session_resume_data(ctx->h2, stream->id); - drain_stream(cf, data, stream); + (void)nghttp2_session_resume_data(h2, stream->stream_id); + (void)h2_process_pending_input(data, httpc, &result); } - } -out: + /* If nghttp2 still has pending frames unsent */ + if(nghttp2_session_want_write(h2)) { + struct SingleRequest *k = &data->req; + int rv; + + H2BUGF(infof(data, "HTTP/2 still wants to send data (easy %p)", data)); + + /* and attempt to send the pending frames */ + rv = h2_session_send(data, h2); + if(rv) + result = CURLE_SEND_ERROR; + + if(nghttp2_session_want_write(h2)) { + /* re-set KEEP_SEND to make sure we are called again */ + k->keepon |= KEEP_SEND; + } + } + } return result; } -static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf, +static ssize_t http2_handle_stream_close(struct connectdata *conn, struct Curl_easy *data, - struct stream_ctx *stream, - CURLcode *err) + struct HTTP *stream, CURLcode *err) { - ssize_t rv = 0; + struct http_conn *httpc = &conn->proto.httpc; + if(httpc->pause_stream_id == stream->stream_id) { + httpc->pause_stream_id = 0; + } + + drained_transfer(data, httpc); + + if(httpc->pause_stream_id == 0) { + if(h2_process_pending_input(data, httpc, err) != 0) { + return -1; + } + } + + DEBUGASSERT(data->state.drain == 0); + + /* Reset to FALSE to prevent infinite loop in readwrite_data function. */ + stream->closed = FALSE; if(stream->error == NGHTTP2_REFUSED_STREAM) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] REFUSED_STREAM, try again on a new " - "connection", stream->id)); - connclose(cf->conn, "REFUSED_STREAM"); /* don't use this anymore */ + H2BUGF(infof(data, "REFUSED_STREAM (%u), try again on a new connection", + stream->stream_id)); + connclose(conn, "REFUSED_STREAM"); /* don't use this anymore */ data->state.refused_stream = TRUE; - *err = CURLE_SEND_ERROR; /* trigger Curl_retry_request() later */ + *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ return -1; } else if(stream->error != NGHTTP2_NO_ERROR) { failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)", - stream->id, nghttp2_http2_strerror(stream->error), + stream->stream_id, nghttp2_http2_strerror(stream->error), stream->error); *err = CURLE_HTTP2_STREAM; return -1; } - else if(stream->reset) { - failf(data, "HTTP/2 stream %u was reset", stream->id); - *err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR; - return -1; - } if(!stream->bodystarted) { failf(data, "HTTP/2 stream %u was closed cleanly, but before getting " " all response header fields, treated as error", - stream->id); + stream->stream_id); *err = CURLE_HTTP2_STREAM; return -1; } - if(Curl_dynhds_count(&stream->resp_trailers)) { - struct dynhds_entry *e; - struct dynbuf dbuf; - size_t i; + if(Curl_dyn_len(&stream->trailer_recvbuf)) { + char *trailp = Curl_dyn_ptr(&stream->trailer_recvbuf); + char *lf; - *err = CURLE_OK; - Curl_dyn_init(&dbuf, DYN_TRAILERS); - for(i = 0; i < Curl_dynhds_count(&stream->resp_trailers); ++i) { - e = Curl_dynhds_getn(&stream->resp_trailers, i); - if(!e) - break; - Curl_dyn_reset(&dbuf); - *err = Curl_dyn_addf(&dbuf, "%.*s: %.*s\x0d\x0a", - (int)e->namelen, e->name, - (int)e->valuelen, e->value); - if(*err) - break; - Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&dbuf), - Curl_dyn_len(&dbuf)); - *err = Curl_client_write(data, CLIENTWRITE_HEADER|CLIENTWRITE_TRAILER, - Curl_dyn_ptr(&dbuf), Curl_dyn_len(&dbuf)); - if(*err) + do { + size_t len = 0; + CURLcode result; + /* each trailer line ends with a newline */ + lf = strchr(trailp, '\n'); + if(!lf) break; - } - Curl_dyn_free(&dbuf); - if(*err) - goto out; + len = lf + 1 - trailp; + + Curl_debug(data, CURLINFO_HEADER_IN, trailp, len); + /* pass the trailers one by one to the callback */ + result = Curl_client_write(data, CLIENTWRITE_HEADER, trailp, len); + if(result) { + *err = result; + return -1; + } + trailp = ++lf; + } while(lf); } stream->close_handled = TRUE; - *err = CURLE_OK; - rv = 0; -out: - DEBUGF(LOG_CF(data, cf, "handle_stream_close -> %zd, %d", rv, *err)); - return rv; -} - -static int sweight_wanted(const struct Curl_easy *data) -{ - /* 0 weight is not set by user and we take the nghttp2 default one */ - return data->set.priority.weight? - data->set.priority.weight : NGHTTP2_DEFAULT_WEIGHT; -} - -static int sweight_in_effect(const struct Curl_easy *data) -{ - /* 0 weight is not set by user and we take the nghttp2 default one */ - return data->state.priority.weight? - data->state.priority.weight : NGHTTP2_DEFAULT_WEIGHT; + H2BUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close")); + return 0; } /* @@ -1679,303 +1610,367 @@ static int sweight_in_effect(const struct Curl_easy *data) static void h2_pri_spec(struct Curl_easy *data, nghttp2_priority_spec *pri_spec) { - struct Curl_data_priority *prio = &data->set.priority; - struct stream_ctx *depstream = H2_STREAM_CTX(prio->parent); - int32_t depstream_id = depstream? depstream->id:0; - nghttp2_priority_spec_init(pri_spec, depstream_id, - sweight_wanted(data), - data->set.priority.exclusive); - data->state.priority = *prio; + struct HTTP *depstream = (data->set.stream_depends_on? + data->set.stream_depends_on->req.p.http:NULL); + int32_t depstream_id = depstream? depstream->stream_id:0; + nghttp2_priority_spec_init(pri_spec, depstream_id, data->set.stream_weight, + data->set.stream_depends_e); + data->state.stream_weight = data->set.stream_weight; + data->state.stream_depends_e = data->set.stream_depends_e; + data->state.stream_depends_on = data->set.stream_depends_on; } /* - * Check if there's been an update in the priority / + * h2_session_send() checks if there's been an update in the priority / * dependency settings and if so it submits a PRIORITY frame with the updated * info. - * Flush any out data pending in the network buffer. */ -static CURLcode h2_progress_egress(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H2_STREAM_CTX(data); - int rv = 0; - - if(stream && stream->id > 0 && - ((sweight_wanted(data) != sweight_in_effect(data)) || - (data->set.priority.exclusive != data->state.priority.exclusive) || - (data->set.priority.parent != data->state.priority.parent)) ) { +static int h2_session_send(struct Curl_easy *data, + nghttp2_session *h2) +{ + struct HTTP *stream = data->req.p.http; + struct http_conn *httpc = &data->conn->proto.httpc; + set_transfer(httpc, data); + if((data->set.stream_weight != data->state.stream_weight) || + (data->set.stream_depends_e != data->state.stream_depends_e) || + (data->set.stream_depends_on != data->state.stream_depends_on) ) { /* send new weight and/or dependency */ nghttp2_priority_spec pri_spec; + int rv; h2_pri_spec(data, &pri_spec); - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] Queuing PRIORITY", - stream->id)); - DEBUGASSERT(stream->id != -1); - rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE, - stream->id, &pri_spec); + + H2BUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)", + stream->stream_id, data)); + DEBUGASSERT(stream->stream_id != -1); + rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id, + &pri_spec); if(rv) - goto out; + return rv; } - ctx->nw_out_blocked = 0; - while(!rv && !ctx->nw_out_blocked && nghttp2_session_want_write(ctx->h2)) - rv = nghttp2_session_send(ctx->h2); - -out: - if(nghttp2_is_fatal(rv)) { - DEBUGF(LOG_CF(data, cf, "nghttp2_session_send error (%s)%d", - nghttp2_strerror(rv), rv)); - return CURLE_SEND_ERROR; - } - return nw_out_flush(cf, data); + return nghttp2_session_send(h2); } -static ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) +static ssize_t http2_recv(struct Curl_easy *data, int sockindex, + char *mem, size_t len, CURLcode *err) { - struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H2_STREAM_CTX(data); - ssize_t nread = -1; + ssize_t nread; + struct connectdata *conn = data->conn; + struct http_conn *httpc = &conn->proto.httpc; + struct HTTP *stream = data->req.p.http; - *err = CURLE_AGAIN; - if(!Curl_bufq_is_empty(&stream->recvbuf)) { - nread = Curl_bufq_read(&stream->recvbuf, - (unsigned char *)buf, len, err); - DEBUGF(LOG_CF(data, cf, "recvbuf read(len=%zu) -> %zd, %d", - len, nread, *err)); - if(nread < 0) - goto out; - DEBUGASSERT(nread > 0); - } - - if(nread < 0) { - if(stream->closed) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] returning CLOSE", stream->id)); - nread = http2_handle_stream_close(cf, data, stream, err); - } - else if(stream->reset || - (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) || - (ctx->goaway && ctx->last_stream_id < stream->id)) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] returning ERR", stream->id)); - *err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR; - nread = -1; + (void)sockindex; /* we always do HTTP2 on sockindex 0 */ + + if(should_close_session(httpc)) { + H2BUGF(infof(data, + "http2_recv: nothing to do in this session")); + if(conn->bits.close) { + /* already marked for closure, return OK and we're done */ + *err = CURLE_OK; + return 0; } + *err = CURLE_HTTP2; + return -1; } - else if(nread == 0) { - *err = CURLE_AGAIN; - nread = -1; + + /* Nullify here because we call nghttp2_session_send() and they + might refer to the old buffer. */ + stream->upload_mem = NULL; + stream->upload_len = 0; + + /* + * At this point 'stream' is just in the Curl_easy the connection + * identifies as its owner at this time. + */ + + if(stream->bodystarted && + stream->nread_header_recvbuf < Curl_dyn_len(&stream->header_recvbuf)) { + /* If there is header data pending for this stream to return, do that */ + size_t left = + Curl_dyn_len(&stream->header_recvbuf) - stream->nread_header_recvbuf; + size_t ncopy = CURLMIN(len, left); + memcpy(mem, Curl_dyn_ptr(&stream->header_recvbuf) + + stream->nread_header_recvbuf, ncopy); + stream->nread_header_recvbuf += ncopy; + + H2BUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf", + (int)ncopy)); + return ncopy; + } + + H2BUGF(infof(data, "http2_recv: easy %p (stream %u) win %u/%u", + data, stream->stream_id, + nghttp2_session_get_local_window_size(httpc->h2), + nghttp2_session_get_stream_local_window_size(httpc->h2, + stream->stream_id) + )); + + if((data->state.drain) && stream->memlen) { + H2BUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u (%p => %p)", + stream->memlen, stream->stream_id, + stream->mem, mem)); + if(mem != stream->mem) { + /* if we didn't get the same buffer this time, we must move the data to + the beginning */ + memmove(mem, stream->mem, stream->memlen); + stream->len = len - stream->memlen; + stream->mem = mem; + } + if(httpc->pause_stream_id == stream->stream_id && !stream->pausedata) { + /* We have paused nghttp2, but we have no pause data (see + on_data_chunk_recv). */ + httpc->pause_stream_id = 0; + if(h2_process_pending_input(data, httpc, err) != 0) { + return -1; + } + } } + else if(stream->pausedata) { + DEBUGASSERT(httpc->pause_stream_id == stream->stream_id); + nread = CURLMIN(len, stream->pauselen); + memcpy(mem, stream->pausedata, nread); -out: - DEBUGF(LOG_CF(data, cf, "stream_recv(len=%zu) -> %zd, %d", - len, nread, *err)); - return nread; -} + stream->pausedata += nread; + stream->pauselen -= nread; -static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream; - CURLcode result = CURLE_OK; - ssize_t nread; + if(stream->pauselen == 0) { + H2BUGF(infof(data, "Unpaused by stream %u", stream->stream_id)); + DEBUGASSERT(httpc->pause_stream_id == stream->stream_id); + httpc->pause_stream_id = 0; - /* Process network input buffer fist */ - if(!Curl_bufq_is_empty(&ctx->inbufq)) { - DEBUGF(LOG_CF(data, cf, "Process %zu bytes in connection buffer", - Curl_bufq_len(&ctx->inbufq))); - if(h2_process_pending_input(cf, data, &result) < 0) - return result; - } - - /* Receive data from the "lower" filters, e.g. network until - * it is time to stop due to connection close or us not processing - * all network input */ - while(!ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) { - stream = H2_STREAM_CTX(data); - if(stream && (stream->closed || Curl_bufq_is_full(&stream->recvbuf))) { - /* We would like to abort here and stop processing, so that - * the transfer loop can handle the data/close here. However, - * this may leave data in underlying buffers that will not - * be consumed. */ - if(!cf->next || !cf->next->cft->has_data_pending(cf->next, data)) - break; - } + stream->pausedata = NULL; + stream->pauselen = 0; - nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result); - /* DEBUGF(LOG_CF(data, cf, "read %zu bytes nw data -> %zd, %d", - Curl_bufq_len(&ctx->inbufq), nread, result)); */ - if(nread < 0) { - if(result != CURLE_AGAIN) { - failf(data, "Failed receiving HTTP2 data: %d(%s)", result, - curl_easy_strerror(result)); - return result; + /* When NGHTTP2_ERR_PAUSE is returned from + data_source_read_callback, we might not process DATA frame + fully. Calling nghttp2_session_mem_recv() again will + continue to process DATA frame, but if there is no incoming + frames, then we have to call it again with 0-length data. + Without this, on_stream_close callback will not be called, + and stream could be hanged. */ + if(h2_process_pending_input(data, httpc, err) != 0) { + return -1; } - break; - } - else if(nread == 0) { - ctx->conn_closed = TRUE; - break; } - - if(h2_process_pending_input(cf, data, &result)) - return result; + H2BUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u", + nread, stream->stream_id)); + return nread; + } + else if(httpc->pause_stream_id) { + /* If a stream paused nghttp2_session_mem_recv previously, and has + not processed all data, it still refers to the buffer in + nghttp2_session. If we call nghttp2_session_mem_recv(), we may + overwrite that buffer. To avoid that situation, just return + here with CURLE_AGAIN. This could be busy loop since data in + socket is not read. But it seems that usually streams are + notified with its drain property, and socket is read again + quickly. */ + if(stream->closed) + /* closed overrides paused */ + return 0; + H2BUGF(infof(data, "stream %u is paused, pause id: %u", + stream->stream_id, httpc->pause_stream_id)); + *err = CURLE_AGAIN; + return -1; } + else { + /* remember where to store incoming data for this stream and how big the + buffer is */ + stream->mem = mem; + stream->len = len; + stream->memlen = 0; - if(ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) { - connclose(cf->conn, "GOAWAY received"); - } + if(httpc->inbuflen == 0) { + nread = ((Curl_recv *)httpc->recv_underlying)( + data, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, err); - return CURLE_OK; -} + if(nread == -1) { + if(*err != CURLE_AGAIN) + failf(data, "Failed receiving HTTP2 data"); + else if(stream->closed) + /* received when the stream was already closed! */ + return http2_handle_stream_close(conn, data, stream, err); -static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) -{ - struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H2_STREAM_CTX(data); - ssize_t nread = -1; - CURLcode result; - struct cf_call_data save; + return -1; + } - CF_DATA_SAVE(save, cf, data); + if(nread == 0) { + if(!stream->closed) { + /* This will happen when the server or proxy server is SIGKILLed + during data transfer. We should emit an error since our data + received may be incomplete. */ + failf(data, "HTTP/2 stream %u was not closed cleanly before" + " end of the underlying stream", + stream->stream_id); + *err = CURLE_HTTP2_STREAM; + return -1; + } - nread = stream_recv(cf, data, buf, len, err); - if(nread < 0 && *err != CURLE_AGAIN) - goto out; + H2BUGF(infof(data, "end of stream")); + *err = CURLE_OK; + return 0; + } - if(nread < 0) { - *err = h2_progress_ingress(cf, data); - if(*err) - goto out; + H2BUGF(infof(data, "nread=%zd", nread)); - nread = stream_recv(cf, data, buf, len, err); - } + httpc->inbuflen = nread; - if(nread > 0) { - size_t data_consumed = (size_t)nread; - /* Now that we transferred this to the upper layer, we report - * the actual amount of DATA consumed to the H2 session, so - * that it adjusts stream flow control */ - if(stream->resp_hds_len >= data_consumed) { - stream->resp_hds_len -= data_consumed; /* no DATA */ + DEBUGASSERT(httpc->nread_inbuf == 0); } else { - if(stream->resp_hds_len) { - data_consumed -= stream->resp_hds_len; - stream->resp_hds_len = 0; - } - if(data_consumed) { - nghttp2_session_consume(ctx->h2, stream->id, data_consumed); - } + nread = httpc->inbuflen - httpc->nread_inbuf; + (void)nread; /* silence warning, used in debug */ + H2BUGF(infof(data, "Use data left in connection buffer, nread=%zd", + nread)); } - if(stream->closed) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] closed stream, set drain", - stream->id)); - drain_stream(cf, data, stream); - } + if(h2_process_pending_input(data, httpc, err)) + return -1; } + if(stream->memlen) { + ssize_t retlen = stream->memlen; + H2BUGF(infof(data, "http2_recv: returns %zd for stream %u", + retlen, stream->stream_id)); + stream->memlen = 0; -out: - result = h2_progress_egress(cf, data); - if(result && result != CURLE_AGAIN) { - *err = result; - nread = -1; - } - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_recv(len=%zu) -> %zd %d, " - "buffered=%zu, window=%d/%d, connection %d/%d", - stream->id, len, nread, *err, - Curl_bufq_len(&stream->recvbuf), - nghttp2_session_get_stream_effective_recv_data_length( - ctx->h2, stream->id), - nghttp2_session_get_stream_effective_local_window_size( - ctx->h2, stream->id), - nghttp2_session_get_local_window_size(ctx->h2), - HTTP2_HUGE_WINDOW_SIZE)); - - CF_DATA_RESTORE(cf, save); - return nread; + if(httpc->pause_stream_id == stream->stream_id) { + /* data for this stream is returned now, but this stream caused a pause + already so we need it called again asap */ + H2BUGF(infof(data, "Data returned for PAUSED stream %u", + stream->stream_id)); + } + else if(!stream->closed) { + drained_transfer(data, httpc); + } + else + /* this stream is closed, trigger a another read ASAP to detect that */ + Curl_expire(data, 0, EXPIRE_RUN_NOW); + + return retlen; + } + if(stream->closed) + return http2_handle_stream_close(conn, data, stream, err); + *err = CURLE_AGAIN; + H2BUGF(infof(data, "http2_recv returns AGAIN for stream %u", + stream->stream_id)); + return -1; } -static ssize_t h2_submit(struct stream_ctx **pstream, - struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) +static ssize_t http2_send(struct Curl_easy *data, int sockindex, + const void *mem, size_t len, CURLcode *err) { - struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream = NULL; - struct h1_req_parser h1; - struct dynhds h2_headers; + /* + * Currently, we send request in this function, but this function is also + * used to send request body. It would be nice to add dedicated function for + * request. + */ + int rv; + struct connectdata *conn = data->conn; + struct http_conn *httpc = &conn->proto.httpc; + struct HTTP *stream = data->req.p.http; nghttp2_nv *nva = NULL; - const void *body = NULL; - size_t nheader, bodylen, i; + size_t nheader; nghttp2_data_provider data_prd; int32_t stream_id; + nghttp2_session *h2 = httpc->h2; nghttp2_priority_spec pri_spec; - ssize_t nwritten; + CURLcode result; + struct h2h3req *hreq; - Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); - Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); + (void)sockindex; - *err = http2_data_setup(cf, data, &stream); - if(*err) { - nwritten = -1; - goto out; - } + H2BUGF(infof(data, "http2_send len=%zu", len)); - nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err); - if(nwritten < 0) - goto out; - DEBUGASSERT(h1.done); - DEBUGASSERT(h1.req); + if(stream->stream_id != -1) { + if(stream->close_handled) { + infof(data, "stream %u closed", stream->stream_id); + *err = CURLE_HTTP2_STREAM; + return -1; + } + else if(stream->closed) { + return http2_handle_stream_close(conn, data, stream, err); + } + /* If stream_id != -1, we have dispatched request HEADERS, and now + are going to send or sending request body in DATA frame */ + stream->upload_mem = mem; + stream->upload_len = len; + rv = nghttp2_session_resume_data(h2, stream->stream_id); + if(nghttp2_is_fatal(rv)) { + *err = CURLE_SEND_ERROR; + return -1; + } + rv = h2_session_send(data, h2); + if(nghttp2_is_fatal(rv)) { + *err = CURLE_SEND_ERROR; + return -1; + } + len -= stream->upload_len; - *err = Curl_http_req_to_h2(&h2_headers, h1.req, data); - if(*err) { - nwritten = -1; - goto out; - } + /* Nullify here because we call nghttp2_session_send() and they + might refer to the old buffer. */ + stream->upload_mem = NULL; + stream->upload_len = 0; - nheader = Curl_dynhds_count(&h2_headers); - nva = malloc(sizeof(nghttp2_nv) * nheader); - if(!nva) { - *err = CURLE_OUT_OF_MEMORY; - nwritten = -1; - goto out; - } - - for(i = 0; i < nheader; ++i) { - struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); - nva[i].name = (unsigned char *)e->name; - nva[i].namelen = e->namelen; - nva[i].value = (unsigned char *)e->value; - nva[i].valuelen = e->valuelen; - nva[i].flags = NGHTTP2_NV_FLAG_NONE; - } + if(should_close_session(httpc)) { + H2BUGF(infof(data, "http2_send: nothing to do in this session")); + *err = CURLE_HTTP2; + return -1; + } -#define MAX_ACC 60000 /* <64KB to account for some overhead */ - { - size_t acc = 0; + if(stream->upload_left) { + /* we are sure that we have more data to send here. Calling the + following API will make nghttp2_session_want_write() return + nonzero if remote window allows it, which then libcurl checks + socket is writable or not. See http2_perform_getsock(). */ + nghttp2_session_resume_data(h2, stream->stream_id); + } - for(i = 0; i < nheader; ++i) { - acc += nva[i].namelen + nva[i].valuelen; +#ifdef DEBUG_HTTP2 + if(!len) { + infof(data, "http2_send: easy %p (stream %u) win %u/%u", + data, stream->stream_id, + nghttp2_session_get_remote_window_size(httpc->h2), + nghttp2_session_get_stream_remote_window_size(httpc->h2, + stream->stream_id) + ); - infof(data, "h2 [%.*s: %.*s]", - (int)nva[i].namelen, nva[i].name, - (int)nva[i].valuelen, nva[i].value); } + infof(data, "http2_send returns %zu for stream %u", len, + stream->stream_id); +#endif + return len; + } + + result = Curl_pseudo_headers(data, mem, len, &hreq); + if(result) { + *err = result; + return -1; + } + nheader = hreq->entries; - if(acc > MAX_ACC) { - infof(data, "http_request: Warning: The cumulative length of all " - "headers exceeds %d bytes and that could cause the " - "stream to be rejected.", MAX_ACC); + nva = malloc(sizeof(nghttp2_nv) * nheader); + if(!nva) { + Curl_pseudo_free(hreq); + *err = CURLE_OUT_OF_MEMORY; + return -1; + } + else { + unsigned int i; + for(i = 0; i < nheader; i++) { + nva[i].name = (unsigned char *)hreq->header[i].name; + nva[i].namelen = hreq->header[i].namelen; + nva[i].value = (unsigned char *)hreq->header[i].value; + nva[i].valuelen = hreq->header[i].valuelen; + nva[i].flags = NGHTTP2_NV_FLAG_NONE; } + Curl_pseudo_free(hreq); } h2_pri_spec(data, &pri_spec); - DEBUGF(LOG_CF(data, cf, "send request allowed %d", - nghttp2_session_check_request_allowed(ctx->h2))); + H2BUGF(infof(data, "http2_send request allowed %d (easy handle %p)", + nghttp2_session_check_request_allowed(h2), (void *)data)); switch(data->state.httpreq) { case HTTPREQ_POST: @@ -1986,345 +1981,220 @@ static ssize_t h2_submit(struct stream_ctx **pstream, stream->upload_left = data->state.infilesize; else /* data sending without specifying the data amount up front */ - stream->upload_left = -1; /* unknown */ + stream->upload_left = -1; /* unknown, but not zero */ - data_prd.read_callback = req_body_read_callback; + data_prd.read_callback = data_source_read_callback; data_prd.source.ptr = NULL; - stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader, + stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader, &data_prd, data); break; default: - stream->upload_left = 0; /* no request body */ - stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader, + stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader, NULL, data); } Curl_safefree(nva); if(stream_id < 0) { - DEBUGF(LOG_CF(data, cf, "send: nghttp2_submit_request error (%s)%u", - nghttp2_strerror(stream_id), stream_id)); + H2BUGF(infof(data, + "http2_send() nghttp2_submit_request error (%s)%u", + nghttp2_strerror(stream_id), stream_id)); *err = CURLE_SEND_ERROR; - nwritten = -1; - goto out; - } - - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_send(len=%zu) submit %s", - stream_id, len, data->state.url)); - infof(data, "Using Stream ID: %u", stream_id); - stream->id = stream_id; - stream->local_window_size = H2_STREAM_WINDOW_SIZE; - if(data->set.max_recv_speed) { - /* We are asked to only receive `max_recv_speed` bytes per second. - * Let's limit our stream window size around that, otherwise the server - * will send in large bursts only. We make the window 50% larger to - * allow for data in flight and avoid stalling. */ - curl_off_t n = (((data->set.max_recv_speed - 1) / H2_CHUNK_SIZE) + 1); - n += CURLMAX((n/2), 1); - if(n < (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) && - n < (UINT_MAX / H2_CHUNK_SIZE)) { - stream->local_window_size = (uint32_t)n * H2_CHUNK_SIZE; - } + return -1; } - body = (const char *)buf + nwritten; - bodylen = len - nwritten; + infof(data, "Using Stream ID: %u (easy handle %p)", + stream_id, (void *)data); + stream->stream_id = stream_id; - if(bodylen) { - /* We have request body to send in DATA frame */ - ssize_t n = Curl_bufq_write(&stream->sendbuf, body, bodylen, err); - if(n < 0) { - *err = CURLE_SEND_ERROR; - nwritten = -1; - goto out; - } - nwritten += n; + rv = h2_session_send(data, h2); + if(rv) { + H2BUGF(infof(data, + "http2_send() nghttp2_session_send error (%s)%d", + nghttp2_strerror(rv), rv)); + + *err = CURLE_SEND_ERROR; + return -1; } -out: - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] submit -> %zd, %d", - stream? stream->id : -1, nwritten, *err)); - *pstream = stream; - Curl_h1_req_parse_free(&h1); - Curl_dynhds_free(&h2_headers); - return nwritten; + if(should_close_session(httpc)) { + H2BUGF(infof(data, "http2_send: nothing to do in this session")); + *err = CURLE_HTTP2; + return -1; + } + + /* If whole HEADERS frame was sent off to the underlying socket, the nghttp2 + library calls data_source_read_callback. But only it found that no data + available, so it deferred the DATA transmission. Which means that + nghttp2_session_want_write() returns 0 on http2_perform_getsock(), which + results that no writable socket check is performed. To workaround this, + we issue nghttp2_session_resume_data() here to bring back DATA + transmission from deferred state. */ + nghttp2_session_resume_data(h2, stream->stream_id); + + return len; } -static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) +CURLcode Curl_http2_setup(struct Curl_easy *data, + struct connectdata *conn) { - struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H2_STREAM_CTX(data); - struct cf_call_data save; - int rv; - ssize_t nwritten; CURLcode result; - int blocked = 0; + struct http_conn *httpc = &conn->proto.httpc; + struct HTTP *stream = data->req.p.http; - CF_DATA_SAVE(save, cf, data); + DEBUGASSERT(data->state.buffer); - if(stream && stream->id != -1) { - if(stream->close_handled) { - infof(data, "stream %u closed", stream->id); - *err = CURLE_HTTP2_STREAM; - nwritten = -1; - goto out; - } - else if(stream->closed) { - nwritten = http2_handle_stream_close(cf, data, stream, err); - goto out; - } - else if(stream->upload_blocked_len) { - /* the data in `buf` has alread been submitted or added to the - * buffers, but have been EAGAINed on the last invocation. */ - DEBUGASSERT(len >= stream->upload_blocked_len); - if(len < stream->upload_blocked_len) { - /* Did we get called again with a smaller `len`? This should not - * happend. We are not prepared to handle that. */ - failf(data, "HTTP/2 send again with decreased length"); - *err = CURLE_HTTP2; - nwritten = -1; - goto out; - } - nwritten = (ssize_t)stream->upload_blocked_len; - stream->upload_blocked_len = 0; - } - else { - /* If stream_id != -1, we have dispatched request HEADERS and - * optionally request body, and now are going to send or sending - * more request body in DATA frame */ - nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err); - if(nwritten < 0) { - if(*err != CURLE_AGAIN) - goto out; - nwritten = 0; - } - } + stream->stream_id = -1; - if(!Curl_bufq_is_empty(&stream->sendbuf)) { - /* req body data is buffered, resume the potentially suspended stream */ - rv = nghttp2_session_resume_data(ctx->h2, stream->id); - if(nghttp2_is_fatal(rv)) { - *err = CURLE_SEND_ERROR; - nwritten = -1; - goto out; - } - } - } - else { - nwritten = h2_submit(&stream, cf, data, buf, len, err); - if(nwritten < 0) { - goto out; - } - DEBUGASSERT(stream); - } + Curl_dyn_init(&stream->header_recvbuf, DYN_H2_HEADERS); + Curl_dyn_init(&stream->trailer_recvbuf, DYN_H2_TRAILERS); - /* Call the nghttp2 send loop and flush to write ALL buffered data, - * headers and/or request body completely out to the network */ - result = h2_progress_egress(cf, data); - /* if the stream has been closed in egress handling (nghttp2 does that - * when it does not like the headers, for example */ - if(stream && stream->closed) { - nwritten = http2_handle_stream_close(cf, data, stream, err); - goto out; - } - else if(result == CURLE_AGAIN) { - blocked = 1; - } - else if(result) { - *err = result; - nwritten = -1; - goto out; - } - else if(stream && !Curl_bufq_is_empty(&stream->sendbuf)) { - /* although we wrote everything that nghttp2 wants to send now, - * there is data left in our stream send buffer unwritten. This may - * be due to the stream's HTTP/2 flow window being exhausted. */ - blocked = 1; - } - - if(stream && blocked) { - /* Unable to send all data, due to connection blocked or H2 window - * exhaustion. Data is left in our stream buffer, or nghttp2's internal - * frame buffer or our network out buffer. */ - size_t rwin = nghttp2_session_get_stream_remote_window_size(ctx->h2, - stream->id); - if(rwin == 0) { - /* H2 flow window exhaustion. We need to HOLD upload until we get - * a WINDOW_UPDATE from the server. */ - data->req.keepon |= KEEP_SEND_HOLD; - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] holding send as remote flow " - "window is exhausted", stream->id)); - } + stream->upload_left = 0; + stream->upload_mem = NULL; + stream->upload_len = 0; + stream->mem = data->state.buffer; + stream->len = data->set.buffer_size; - /* Whatever the cause, we need to return CURL_EAGAIN for this call. - * We have unwritten state that needs us being invoked again and EAGAIN - * is the only way to ensure that. */ - stream->upload_blocked_len = nwritten; - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_send(len=%zu) BLOCK: win %u/%zu " - "blocked_len=%zu", - stream->id, len, - nghttp2_session_get_remote_window_size(ctx->h2), rwin, - nwritten)); - *err = CURLE_AGAIN; - nwritten = -1; - goto out; - } - else if(should_close_session(ctx)) { - /* nghttp2 thinks this session is done. If the stream has not been - * closed, this is an error state for out transfer */ - if(stream->closed) { - nwritten = http2_handle_stream_close(cf, data, stream, err); - } - else { - DEBUGF(LOG_CF(data, cf, "send: nothing to do in this session")); - *err = CURLE_HTTP2; - nwritten = -1; - } - } + multi_connchanged(data->multi); + /* below this point only connection related inits are done, which only needs + to be done once per connection */ -out: - if(stream) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_send(len=%zu) -> %zd, %d, " - "upload_left=%" CURL_FORMAT_CURL_OFF_T ", " - "h2 windows %d-%d (stream-conn), " - "buffers %zu-%zu (stream-conn)", - stream->id, len, nwritten, *err, - (ssize_t)stream->upload_left, - nghttp2_session_get_stream_remote_window_size( - ctx->h2, stream->id), - nghttp2_session_get_remote_window_size(ctx->h2), - Curl_bufq_len(&stream->sendbuf), - Curl_bufq_len(&ctx->outbufq))); + if((conn->handler == &Curl_handler_http2_ssl) || + (conn->handler == &Curl_handler_http2)) + return CURLE_OK; /* already done */ + + if(conn->handler->flags & PROTOPT_SSL) + conn->handler = &Curl_handler_http2_ssl; + else + conn->handler = &Curl_handler_http2; + + result = http2_init(data, conn); + if(result) { + Curl_dyn_free(&stream->header_recvbuf); + return result; } - else { - DEBUGF(LOG_CF(data, cf, "cf_send(len=%zu) -> %zd, %d, " - "connection-window=%d, nw_send_buffer(%zu)", - len, nwritten, *err, - nghttp2_session_get_remote_window_size(ctx->h2), - Curl_bufq_len(&ctx->outbufq))); - } - CF_DATA_RESTORE(cf, save); - return nwritten; -} -static int cf_h2_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *sock) -{ - struct cf_h2_ctx *ctx = cf->ctx; - struct SingleRequest *k = &data->req; - struct stream_ctx *stream = H2_STREAM_CTX(data); - int bitmap = GETSOCK_BLANK; - struct cf_call_data save; + infof(data, "Using HTTP2, server supports multiplexing"); - CF_DATA_SAVE(save, cf, data); - sock[0] = Curl_conn_cf_get_socket(cf, data); + conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ + conn->httpversion = 20; + conn->bundle->multiuse = BUNDLE_MULTIPLEX; - if(!(k->keepon & (KEEP_RECV_PAUSE|KEEP_RECV_HOLD))) - /* Unless paused - in an HTTP/2 connection we can basically always get a - frame so we should always be ready for one */ - bitmap |= GETSOCK_READSOCK(0); + httpc->inbuflen = 0; + httpc->nread_inbuf = 0; - /* we're (still uploading OR the HTTP/2 layer wants to send data) AND - there's a window to send data in */ - if((((k->keepon & KEEP_SENDBITS) == KEEP_SEND) || - nghttp2_session_want_write(ctx->h2)) && - (nghttp2_session_get_remote_window_size(ctx->h2) && - nghttp2_session_get_stream_remote_window_size(ctx->h2, - stream->id))) - bitmap |= GETSOCK_WRITESOCK(0); - - CF_DATA_RESTORE(cf, save); - return bitmap; -} + httpc->pause_stream_id = 0; + httpc->drain_total = 0; + return CURLE_OK; +} -static CURLcode cf_h2_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking, bool *done) +CURLcode Curl_http2_switched(struct Curl_easy *data, + const char *mem, size_t nread) { - struct cf_h2_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - struct cf_call_data save; + CURLcode result; + struct connectdata *conn = data->conn; + struct http_conn *httpc = &conn->proto.httpc; + int rv; + struct HTTP *stream = data->req.p.http; - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } + result = Curl_http2_setup(data, conn); + if(result) + return result; - /* Connect the lower filters first */ - if(!cf->next->connected) { - result = Curl_conn_cf_connect(cf->next, data, blocking, done); - if(result || !*done) - return result; - } + httpc->recv_underlying = conn->recv[FIRSTSOCKET]; + httpc->send_underlying = conn->send[FIRSTSOCKET]; + conn->recv[FIRSTSOCKET] = http2_recv; + conn->send[FIRSTSOCKET] = http2_send; - *done = FALSE; + if(data->req.upgr101 == UPGR101_RECEIVED) { + /* stream 1 is opened implicitly on upgrade */ + stream->stream_id = 1; + /* queue SETTINGS frame (again) */ + rv = nghttp2_session_upgrade2(httpc->h2, httpc->binsettings, httpc->binlen, + data->state.httpreq == HTTPREQ_HEAD, NULL); + if(rv) { + failf(data, "nghttp2_session_upgrade2() failed: %s(%d)", + nghttp2_strerror(rv), rv); + return CURLE_HTTP2; + } - CF_DATA_SAVE(save, cf, data); - if(!ctx->h2) { - result = cf_h2_ctx_init(cf, data, FALSE); - if(result) - goto out; + rv = nghttp2_session_set_stream_user_data(httpc->h2, + stream->stream_id, + data); + if(rv) { + infof(data, "http/2: failed to set user_data for stream %u", + stream->stream_id); + DEBUGASSERT(0); + } } + else { + populate_settings(data, httpc); - result = h2_progress_ingress(cf, data); - if(result) - goto out; + /* stream ID is unknown at this point */ + stream->stream_id = -1; + rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE, + httpc->local_settings, + httpc->local_settings_num); + if(rv) { + failf(data, "nghttp2_submit_settings() failed: %s(%d)", + nghttp2_strerror(rv), rv); + return CURLE_HTTP2; + } + } - result = h2_progress_egress(cf, data); - if(result) - goto out; + rv = nghttp2_session_set_local_window_size(httpc->h2, NGHTTP2_FLAG_NONE, 0, + HTTP2_HUGE_WINDOW_SIZE); + if(rv) { + failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", + nghttp2_strerror(rv), rv); + return CURLE_HTTP2; + } - *done = TRUE; - cf->connected = TRUE; - result = CURLE_OK; + /* we are going to copy mem to httpc->inbuf. This is required since + mem is part of buffer pointed by stream->mem, and callbacks + called by nghttp2_session_mem_recv() will write stream specific + data into stream->mem, overwriting data already there. */ + if(H2_BUFSIZE < nread) { + failf(data, "connection buffer size is too small to store data following " + "HTTP Upgrade response header: buflen=%d, datalen=%zu", + H2_BUFSIZE, nread); + return CURLE_HTTP2; + } -out: - CF_DATA_RESTORE(cf, save); - return result; -} + infof(data, "Copying HTTP/2 data in stream buffer to connection buffer" + " after upgrade: len=%zu", + nread); -static void cf_h2_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_h2_ctx *ctx = cf->ctx; + if(nread) + memcpy(httpc->inbuf, mem, nread); - if(ctx) { - struct cf_call_data save; + httpc->inbuflen = nread; - CF_DATA_SAVE(save, cf, data); - cf_h2_ctx_clear(ctx); - CF_DATA_RESTORE(cf, save); - } -} + DEBUGASSERT(httpc->nread_inbuf == 0); -static void cf_h2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_h2_ctx *ctx = cf->ctx; + if(-1 == h2_process_pending_input(data, httpc, &result)) + return CURLE_HTTP2; - (void)data; - if(ctx) { - cf_h2_ctx_free(ctx); - cf->ctx = NULL; - } + return CURLE_OK; } -static CURLcode http2_data_pause(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool pause) +CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause) { -#ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE - struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H2_STREAM_CTX(data); - DEBUGASSERT(data); - if(ctx && ctx->h2 && stream) { - uint32_t window = pause? 0 : stream->local_window_size; - - int rv = nghttp2_session_set_local_window_size(ctx->h2, + DEBUGASSERT(data->conn); + /* if it isn't HTTP/2, we're done */ + if(!(data->conn->handler->protocol & PROTO_FAMILY_HTTP) || + !data->conn->proto.httpc.h2) + return CURLE_OK; +#ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE + else { + struct HTTP *stream = data->req.p.http; + struct http_conn *httpc = &data->conn->proto.httpc; + uint32_t window = !pause * HTTP2_HUGE_WINDOW_SIZE; + int rv = nghttp2_session_set_local_window_size(httpc->h2, NGHTTP2_FLAG_NONE, - stream->id, + stream->stream_id, window); if(rv) { failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", @@ -2332,32 +2202,22 @@ static CURLcode http2_data_pause(struct Curl_cfilter *cf, return CURLE_HTTP2; } - if(!pause) - drain_stream(cf, data, stream); - - /* attempt to send the window update */ - (void)h2_progress_egress(cf, data); + /* make sure the window update gets sent */ + rv = h2_session_send(data, httpc->h2); + if(rv) + return CURLE_SEND_ERROR; - if(!pause) { - /* Unpausing a h2 transfer, requires it to be run again. The server - * may send new DATA on us increasing the flow window, and it may - * not. We may have already buffered and exhausted the new window - * by operating on things in flight during the handling of other - * transfers. */ - drain_stream(cf, data, stream); - Curl_expire(data, 0, EXPIRE_RUN_NOW); - } DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u", - window, stream->id)); + window, stream->stream_id)); #ifdef DEBUGBUILD { /* read out the stream local window again */ uint32_t window2 = - nghttp2_session_get_stream_local_window_size(ctx->h2, - stream->id); + nghttp2_session_get_stream_local_window_size(httpc->h2, + stream->stream_id); DEBUGF(infof(data, "HTTP/2 window size is now %u for stream %u", - window2, stream->id)); + window2, stream->stream_id)); } #endif } @@ -2365,340 +2225,94 @@ static CURLcode http2_data_pause(struct Curl_cfilter *cf, return CURLE_OK; } -static CURLcode cf_h2_cntrl(struct Curl_cfilter *cf, - struct Curl_easy *data, - int event, int arg1, void *arg2) -{ - CURLcode result = CURLE_OK; - struct cf_call_data save; - - (void)arg2; - - CF_DATA_SAVE(save, cf, data); - switch(event) { - case CF_CTRL_DATA_SETUP: - break; - case CF_CTRL_DATA_PAUSE: - result = http2_data_pause(cf, data, (arg1 != 0)); - break; - case CF_CTRL_DATA_DONE_SEND: { - result = http2_data_done_send(cf, data); - break; - } - case CF_CTRL_DATA_DONE: { - http2_data_done(cf, data, arg1 != 0); - break; - } - default: - break; - } - CF_DATA_RESTORE(cf, save); - return result; -} - -static bool cf_h2_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data) +CURLcode Curl_http2_add_child(struct Curl_easy *parent, + struct Curl_easy *child, + bool exclusive) { - struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H2_STREAM_CTX(data); - - if(ctx && (!Curl_bufq_is_empty(&ctx->inbufq) - || (stream && !Curl_bufq_is_empty(&stream->sendbuf)) - || (stream && !Curl_bufq_is_empty(&stream->recvbuf)))) - return TRUE; - return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE; -} + if(parent) { + struct Curl_http2_dep **tail; + struct Curl_http2_dep *dep = calloc(1, sizeof(struct Curl_http2_dep)); + if(!dep) + return CURLE_OUT_OF_MEMORY; + dep->data = child; -static bool cf_h2_is_alive(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *input_pending) -{ - struct cf_h2_ctx *ctx = cf->ctx; - CURLcode result; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - result = (ctx && ctx->h2 && http2_connisalive(cf, data, input_pending)); - DEBUGF(LOG_CF(data, cf, "conn alive -> %d, input_pending=%d", - result, *input_pending)); - CF_DATA_RESTORE(cf, save); - return result; -} - -static CURLcode cf_h2_keep_alive(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - CURLcode result; - struct cf_call_data save; + if(parent->set.stream_dependents && exclusive) { + struct Curl_http2_dep *node = parent->set.stream_dependents; + while(node) { + node->data->set.stream_depends_on = child; + node = node->next; + } - CF_DATA_SAVE(save, cf, data); - result = http2_send_ping(cf, data); - CF_DATA_RESTORE(cf, save); - return result; -} + tail = &child->set.stream_dependents; + while(*tail) + tail = &(*tail)->next; -static CURLcode cf_h2_query(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query, int *pres1, void *pres2) -{ - struct cf_h2_ctx *ctx = cf->ctx; - struct cf_call_data save; - size_t effective_max; - - switch(query) { - case CF_QUERY_MAX_CONCURRENT: - DEBUGASSERT(pres1); - - CF_DATA_SAVE(save, cf, data); - if(nghttp2_session_check_request_allowed(ctx->h2) == 0) { - /* the limit is what we have in use right now */ - effective_max = CONN_INUSE(cf->conn); - } - else { - effective_max = ctx->max_concurrent_streams; + DEBUGASSERT(!*tail); + *tail = parent->set.stream_dependents; + parent->set.stream_dependents = 0; } - *pres1 = (effective_max > INT_MAX)? INT_MAX : (int)effective_max; - CF_DATA_RESTORE(cf, save); - return CURLE_OK; - default: - break; - } - return cf->next? - cf->next->cft->query(cf->next, data, query, pres1, pres2) : - CURLE_UNKNOWN_OPTION; -} - -struct Curl_cftype Curl_cft_nghttp2 = { - "HTTP/2", - CF_TYPE_MULTIPLEX, - CURL_LOG_DEFAULT, - cf_h2_destroy, - cf_h2_connect, - cf_h2_close, - Curl_cf_def_get_host, - cf_h2_get_select_socks, - cf_h2_data_pending, - cf_h2_send, - cf_h2_recv, - cf_h2_cntrl, - cf_h2_is_alive, - cf_h2_keep_alive, - cf_h2_query, -}; - -static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - int sockindex) -{ - struct Curl_cfilter *cf = NULL; - struct cf_h2_ctx *ctx; - CURLcode result = CURLE_OUT_OF_MEMORY; - - DEBUGASSERT(data->conn); - ctx = calloc(sizeof(*ctx), 1); - if(!ctx) - goto out; - - result = Curl_cf_create(&cf, &Curl_cft_nghttp2, ctx); - if(result) - goto out; - Curl_conn_cf_add(data, conn, sockindex, cf); - result = CURLE_OK; - -out: - if(result) - cf_h2_ctx_free(ctx); - *pcf = result? NULL : cf; - return result; -} - -static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct Curl_cfilter *cf_h2 = NULL; - struct cf_h2_ctx *ctx; - CURLcode result = CURLE_OUT_OF_MEMORY; - - (void)data; - ctx = calloc(sizeof(*ctx), 1); - if(!ctx) - goto out; - - result = Curl_cf_create(&cf_h2, &Curl_cft_nghttp2, ctx); - if(result) - goto out; - - Curl_conn_cf_insert_after(cf, cf_h2); - result = CURLE_OK; - -out: - if(result) - cf_h2_ctx_free(ctx); - return result; -} - -static bool Curl_cf_is_http2(struct Curl_cfilter *cf, - const struct Curl_easy *data) -{ - (void)data; - for(; cf; cf = cf->next) { - if(cf->cft == &Curl_cft_nghttp2) - return TRUE; - if(cf->cft->flags & CF_TYPE_IP_CONNECT) - return FALSE; - } - return FALSE; -} - -bool Curl_conn_is_http2(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex) -{ - return conn? Curl_cf_is_http2(conn->cfilter[sockindex], data) : FALSE; -} - -bool Curl_http2_may_switch(struct Curl_easy *data, - struct connectdata *conn, - int sockindex) -{ - (void)sockindex; - if(!Curl_conn_is_http2(data, conn, sockindex) && - data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) { -#ifndef CURL_DISABLE_PROXY - if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { - /* We don't support HTTP/2 proxies yet. Also it's debatable - whether or not this setting should apply to HTTP/2 proxies. */ - infof(data, "Ignoring HTTP/2 prior knowledge due to proxy"); - return FALSE; + tail = &parent->set.stream_dependents; + while(*tail) { + (*tail)->data->set.stream_depends_e = FALSE; + tail = &(*tail)->next; } -#endif - return TRUE; - } - return FALSE; -} - -CURLcode Curl_http2_switch(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - struct Curl_cfilter *cf; - CURLcode result; - - DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex)); - DEBUGF(infof(data, "switching to HTTP/2")); - - result = http2_cfilter_add(&cf, data, conn, sockindex); - if(result) - return result; - - result = cf_h2_ctx_init(cf, data, FALSE); - if(result) - return result; - conn->httpversion = 20; /* we know we're on HTTP/2 now */ - conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - conn->bundle->multiuse = BUNDLE_MULTIPLEX; - multi_connchanged(data->multi); - - if(cf->next) { - bool done; - return Curl_conn_cf_connect(cf, data, FALSE, &done); + DEBUGASSERT(!*tail); + *tail = dep; } - return CURLE_OK; -} - -CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct Curl_cfilter *cf_h2; - CURLcode result; - - DEBUGASSERT(!Curl_cf_is_http2(cf, data)); - - result = http2_cfilter_insert_after(cf, data); - if(result) - return result; - - cf_h2 = cf->next; - result = cf_h2_ctx_init(cf_h2, data, FALSE); - if(result) - return result; - cf->conn->httpversion = 20; /* we know we're on HTTP/2 now */ - cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; - multi_connchanged(data->multi); - - if(cf_h2->next) { - bool done; - return Curl_conn_cf_connect(cf_h2, data, FALSE, &done); - } + child->set.stream_depends_on = parent; + child->set.stream_depends_e = exclusive; return CURLE_OK; } -CURLcode Curl_http2_upgrade(struct Curl_easy *data, - struct connectdata *conn, int sockindex, - const char *mem, size_t nread) +void Curl_http2_remove_child(struct Curl_easy *parent, struct Curl_easy *child) { - struct Curl_cfilter *cf; - struct cf_h2_ctx *ctx; - CURLcode result; - - DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex)); - DEBUGF(infof(data, "upgrading to HTTP/2")); - DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED); - - result = http2_cfilter_add(&cf, data, conn, sockindex); - if(result) - return result; + struct Curl_http2_dep *last = 0; + struct Curl_http2_dep *data = parent->set.stream_dependents; + DEBUGASSERT(child->set.stream_depends_on == parent); - DEBUGASSERT(cf->cft == &Curl_cft_nghttp2); - ctx = cf->ctx; + while(data && data->data != child) { + last = data; + data = data->next; + } - result = cf_h2_ctx_init(cf, data, TRUE); - if(result) - return result; + DEBUGASSERT(data); - if(nread > 0) { - /* Remaining data from the protocol switch reply is already using - * the switched protocol, ie. HTTP/2. We add that to the network - * inbufq. */ - ssize_t copied; - - copied = Curl_bufq_write(&ctx->inbufq, - (const unsigned char *)mem, nread, &result); - if(copied < 0) { - failf(data, "error on copying HTTP Upgrade response: %d", result); - return CURLE_RECV_ERROR; + if(data) { + if(last) { + last->next = data->next; } - if((size_t)copied < nread) { - failf(data, "connection buffer size could not take all data " - "from HTTP Upgrade response header: copied=%zd, datalen=%zu", - copied, nread); - return CURLE_HTTP2; + else { + parent->set.stream_dependents = data->next; } - infof(data, "Copied HTTP/2 data in stream buffer to connection buffer" - " after upgrade: len=%zu", nread); + free(data); } - conn->httpversion = 20; /* we know we're on HTTP/2 now */ - conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - conn->bundle->multiuse = BUNDLE_MULTIPLEX; - multi_connchanged(data->multi); + child->set.stream_depends_on = 0; + child->set.stream_depends_e = FALSE; +} - if(cf->next) { - bool done; - return Curl_conn_cf_connect(cf, data, FALSE, &done); +void Curl_http2_cleanup_dependencies(struct Curl_easy *data) +{ + while(data->set.stream_dependents) { + struct Curl_easy *tmp = data->set.stream_dependents->data; + Curl_http2_remove_child(data, tmp); + if(data->set.stream_depends_on) + Curl_http2_add_child(data->set.stream_depends_on, tmp, FALSE); } - return CURLE_OK; + + if(data->set.stream_depends_on) + Curl_http2_remove_child(data->set.stream_depends_on, data); } -/* Only call this function for a transfer that already got an HTTP/2 +/* Only call this function for a transfer that already got a HTTP/2 CURLE_HTTP2_STREAM error! */ bool Curl_h2_http_1_1_error(struct Curl_easy *data) { - struct stream_ctx *stream = H2_STREAM_CTX(data); - return (stream && stream->error == NGHTTP2_HTTP_1_1_REQUIRED); + struct HTTP *stream = data->req.p.http; + return (stream->error == NGHTTP2_HTTP_1_1_REQUIRED); } #else /* !USE_NGHTTP2 */ diff --git a/contrib/libs/curl/lib/http2.h b/contrib/libs/curl/lib/http2.h index 80e183480a..f0390596ce 100644 --- a/contrib/libs/curl/lib/http2.h +++ b/contrib/libs/curl/lib/http2.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -38,39 +38,46 @@ */ void Curl_http2_ver(char *p, size_t len); +const char *Curl_http2_strerror(uint32_t err); + +CURLcode Curl_http2_init(struct connectdata *conn); +void Curl_http2_init_state(struct UrlState *state); +void Curl_http2_init_userset(struct UserDefined *set); CURLcode Curl_http2_request_upgrade(struct dynbuf *req, struct Curl_easy *data); +CURLcode Curl_http2_setup(struct Curl_easy *data, struct connectdata *conn); +CURLcode Curl_http2_switched(struct Curl_easy *data, + const char *ptr, size_t nread); +/* called from http_setup_conn */ +void Curl_http2_setup_conn(struct connectdata *conn); +void Curl_http2_setup_req(struct Curl_easy *data); +void Curl_http2_done(struct Curl_easy *data, bool premature); +CURLcode Curl_http2_done_sending(struct Curl_easy *data, + struct connectdata *conn); +CURLcode Curl_http2_add_child(struct Curl_easy *parent, + struct Curl_easy *child, + bool exclusive); +void Curl_http2_remove_child(struct Curl_easy *parent, + struct Curl_easy *child); +void Curl_http2_cleanup_dependencies(struct Curl_easy *data); +CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause); /* returns true if the HTTP/2 stream error was HTTP_1_1_REQUIRED */ bool Curl_h2_http_1_1_error(struct Curl_easy *data); - -bool Curl_conn_is_http2(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex); -bool Curl_http2_may_switch(struct Curl_easy *data, - struct connectdata *conn, - int sockindex); - -CURLcode Curl_http2_switch(struct Curl_easy *data, - struct connectdata *conn, int sockindex); - -CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data); - -CURLcode Curl_http2_upgrade(struct Curl_easy *data, - struct connectdata *conn, int sockindex, - const char *ptr, size_t nread); - -extern struct Curl_cftype Curl_cft_nghttp2; - #else /* USE_NGHTTP2 */ - -#define Curl_cf_is_http2(a,b) FALSE -#define Curl_conn_is_http2(a,b,c) FALSE -#define Curl_http2_may_switch(a,b,c) FALSE - #define Curl_http2_request_upgrade(x,y) CURLE_UNSUPPORTED_PROTOCOL -#define Curl_http2_switch(a,b,c) CURLE_UNSUPPORTED_PROTOCOL -#define Curl_http2_upgrade(a,b,c,d,e) CURLE_UNSUPPORTED_PROTOCOL +#define Curl_http2_setup(x,y) CURLE_UNSUPPORTED_PROTOCOL +#define Curl_http2_switched(x,y,z) CURLE_UNSUPPORTED_PROTOCOL +#define Curl_http2_setup_conn(x) Curl_nop_stmt +#define Curl_http2_setup_req(x) +#define Curl_http2_init_state(x) +#define Curl_http2_init_userset(x) +#define Curl_http2_done(x,y) +#define Curl_http2_done_sending(x,y) +#define Curl_http2_add_child(x, y, z) +#define Curl_http2_remove_child(x, y) +#define Curl_http2_cleanup_dependencies(x) +#define Curl_http2_stream_pause(x, y) #define Curl_h2_http_1_1_error(x) 0 #endif diff --git a/contrib/libs/curl/lib/http_aws_sigv4.c b/contrib/libs/curl/lib/http_aws_sigv4.c index 806016253f..c9382918ed 100644 --- a/contrib/libs/curl/lib/http_aws_sigv4.c +++ b/contrib/libs/curl/lib/http_aws_sigv4.c @@ -24,7 +24,7 @@ #include "curl_setup.h" -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_AWS) #include "urldata.h" #include "strcase.h" @@ -34,6 +34,7 @@ #include "transfer.h" #include "parsedate.h" #include "sendf.h" +#include "escape.h" #include <time.h> @@ -44,16 +45,16 @@ #include "slist.h" -#define HMAC_SHA256(k, kl, d, dl, o) \ - do { \ - ret = Curl_hmacit(Curl_HMAC_SHA256, \ - (unsigned char *)k, \ - kl, \ - (unsigned char *)d, \ - dl, o); \ - if(ret) { \ - goto fail; \ - } \ +#define HMAC_SHA256(k, kl, d, dl, o) \ + do { \ + result = Curl_hmacit(Curl_HMAC_SHA256, \ + (unsigned char *)k, \ + kl, \ + (unsigned char *)d, \ + dl, o); \ + if(result) { \ + goto fail; \ + } \ } while(0) #define TIMESTAMP_SIZE 17 @@ -63,11 +64,8 @@ static void sha256_to_hex(char *dst, unsigned char *sha) { - int i; - - for(i = 0; i < SHA256_DIGEST_LENGTH; ++i) { - msnprintf(dst + (i * 2), SHA256_HEX_LENGTH - (i * 2), "%02x", sha[i]); - } + Curl_hexencode(sha, SHA256_DIGEST_LENGTH, + (unsigned char *)dst, SHA256_HEX_LENGTH); } static char *find_date_hdr(struct Curl_easy *data, const char *sig_hdr) @@ -199,10 +197,41 @@ static CURLcode make_headers(struct Curl_easy *data, head = tmp_head; } + /* copy user headers to our header list. the logic is based on how http.c + handles user headers. + + user headers in format 'name:' with no value are used to signal that an + internal header of that name should be removed. those user headers are not + added to this list. + + user headers in format 'name;' with no value are used to signal that a + header of that name with no value should be sent. those user headers are + added to this list but in the format that they will be sent, ie the + semi-colon is changed to a colon for format 'name:'. + + user headers with a value of whitespace only, or without a colon or + semi-colon, are not added to this list. + */ for(l = data->set.headers; l; l = l->next) { - tmp_head = curl_slist_append(head, l->data); - if(!tmp_head) + char *dupdata, *ptr; + char *sep = strchr(l->data, ':'); + if(!sep) + sep = strchr(l->data, ';'); + if(!sep || (*sep == ':' && !*(sep + 1))) + continue; + for(ptr = sep + 1; ISSPACE(*ptr); ++ptr) + ; + if(!*ptr && ptr != sep + 1) /* a value of whitespace only */ + continue; + dupdata = strdup(l->data); + if(!dupdata) goto fail; + dupdata[sep - l->data] = ':'; + tmp_head = Curl_slist_append_nodup(head, dupdata); + if(!tmp_head) { + free(dupdata); + goto fail; + } head = tmp_head; } @@ -214,23 +243,31 @@ static CURLcode make_headers(struct Curl_easy *data, if(!tmp_head) goto fail; head = tmp_head; - *date_header = curl_maprintf("%s: %s", date_hdr_key, timestamp); + *date_header = curl_maprintf("%s: %s\r\n", date_hdr_key, timestamp); } else { char *value; - - *date_header = strdup(*date_header); - if(!*date_header) - goto fail; - + char *endp; value = strchr(*date_header, ':'); - if(!value) + if(!value) { + *date_header = NULL; goto fail; + } ++value; while(ISBLANK(*value)) ++value; - strncpy(timestamp, value, TIMESTAMP_SIZE - 1); - timestamp[TIMESTAMP_SIZE - 1] = 0; + endp = value; + while(*endp && ISALNUM(*endp)) + ++endp; + /* 16 bytes => "19700101T000000Z" */ + if((endp - value) == TIMESTAMP_SIZE - 1) { + memcpy(timestamp, value, TIMESTAMP_SIZE - 1); + timestamp[TIMESTAMP_SIZE - 1] = 0; + } + else + /* bad timestamp length */ + timestamp[0] = 0; + *date_header = NULL; } /* alpha-sort in a case sensitive manner */ @@ -370,9 +407,126 @@ fail: return ret; } +struct pair { + const char *p; + size_t len; +}; + +static int compare_func(const void *a, const void *b) +{ + const struct pair *aa = a; + const struct pair *bb = b; + /* If one element is empty, the other is always sorted higher */ + if(aa->len == 0) + return -1; + if(bb->len == 0) + return 1; + return strncmp(aa->p, bb->p, aa->len < bb->len ? aa->len : bb->len); +} + +#define MAX_QUERYPAIRS 64 + +static CURLcode canon_query(struct Curl_easy *data, + const char *query, struct dynbuf *dq) +{ + CURLcode result = CURLE_OK; + int entry = 0; + int i; + const char *p = query; + struct pair array[MAX_QUERYPAIRS]; + struct pair *ap = &array[0]; + if(!query) + return result; + + /* sort the name=value pairs first */ + do { + char *amp; + entry++; + ap->p = p; + amp = strchr(p, '&'); + if(amp) + ap->len = amp - p; /* excluding the ampersand */ + else { + ap->len = strlen(p); + break; + } + ap++; + p = amp + 1; + } while(entry < MAX_QUERYPAIRS); + if(entry == MAX_QUERYPAIRS) { + /* too many query pairs for us */ + failf(data, "aws-sigv4: too many query pairs in URL"); + return CURLE_URL_MALFORMAT; + } + + qsort(&array[0], entry, sizeof(struct pair), compare_func); + + ap = &array[0]; + for(i = 0; !result && (i < entry); i++, ap++) { + size_t len; + const char *q = ap->p; + bool found_equals = false; + if(!ap->len) + continue; + for(len = ap->len; len && !result; q++, len--) { + if(ISALNUM(*q)) + result = Curl_dyn_addn(dq, q, 1); + else { + switch(*q) { + case '-': + case '.': + case '_': + case '~': + /* allowed as-is */ + result = Curl_dyn_addn(dq, q, 1); + break; + case '=': + /* allowed as-is */ + result = Curl_dyn_addn(dq, q, 1); + found_equals = true; + break; + case '%': + /* uppercase the following if hexadecimal */ + if(ISXDIGIT(q[1]) && ISXDIGIT(q[2])) { + char tmp[3]="%"; + tmp[1] = Curl_raw_toupper(q[1]); + tmp[2] = Curl_raw_toupper(q[2]); + result = Curl_dyn_addn(dq, tmp, 3); + q += 2; + len -= 2; + } + else + /* '%' without a following two-digit hex, encode it */ + result = Curl_dyn_addn(dq, "%25", 3); + break; + default: { + /* URL encode */ + const char hex[] = "0123456789ABCDEF"; + char out[3]={'%'}; + out[1] = hex[((unsigned char)*q)>>4]; + out[2] = hex[*q & 0xf]; + result = Curl_dyn_addn(dq, out, 3); + break; + } + } + } + } + if(!result && !found_equals) { + /* queries without value still need an equals */ + result = Curl_dyn_addn(dq, "=", 1); + } + if(!result && i < entry - 1) { + /* insert ampersands between query pairs */ + result = Curl_dyn_addn(dq, "&", 1); + } + } + return result; +} + + CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) { - CURLcode ret = CURLE_OUT_OF_MEMORY; + CURLcode result = CURLE_OUT_OF_MEMORY; struct connectdata *conn = data->conn; size_t len; const char *arg; @@ -388,6 +542,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) char date[9]; struct dynbuf canonical_headers; struct dynbuf signed_headers; + struct dynbuf canonical_query; char *date_header = NULL; Curl_HttpReq httpreq; const char *method = NULL; @@ -416,6 +571,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) /* we init those buffers here, so goto fail will free initialized dynbuf */ Curl_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER); + Curl_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER); Curl_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER); /* @@ -431,15 +587,15 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) /* provider1[:provider2[:region[:service]]] No string can be longer than N bytes of non-whitespace - */ + */ (void)sscanf(arg, "%" MAX_SIGV4_LEN_TXT "[^:]" ":%" MAX_SIGV4_LEN_TXT "[^:]" ":%" MAX_SIGV4_LEN_TXT "[^:]" ":%" MAX_SIGV4_LEN_TXT "s", provider0, provider1, region, service); if(!provider0[0]) { - failf(data, "first provider can't be empty"); - ret = CURLE_BAD_FUNCTION_ARGUMENT; + failf(data, "first aws-sigv4 provider can't be empty"); + result = CURLE_BAD_FUNCTION_ARGUMENT; goto fail; } else if(!provider1[0]) @@ -448,35 +604,38 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) if(!service[0]) { char *hostdot = strchr(hostname, '.'); if(!hostdot) { - failf(data, "service missing in parameters and hostname"); - ret = CURLE_URL_MALFORMAT; + failf(data, "aws-sigv4: service missing in parameters and hostname"); + result = CURLE_URL_MALFORMAT; goto fail; } len = hostdot - hostname; if(len > MAX_SIGV4_LEN) { - failf(data, "service too long in hostname"); - ret = CURLE_URL_MALFORMAT; + failf(data, "aws-sigv4: service too long in hostname"); + result = CURLE_URL_MALFORMAT; goto fail; } - strncpy(service, hostname, len); + memcpy(service, hostname, len); service[len] = '\0'; + infof(data, "aws_sigv4: picked service %s from host", service); + if(!region[0]) { const char *reg = hostdot + 1; const char *hostreg = strchr(reg, '.'); if(!hostreg) { - failf(data, "region missing in parameters and hostname"); - ret = CURLE_URL_MALFORMAT; + failf(data, "aws-sigv4: region missing in parameters and hostname"); + result = CURLE_URL_MALFORMAT; goto fail; } len = hostreg - reg; if(len > MAX_SIGV4_LEN) { - failf(data, "region too long in hostname"); - ret = CURLE_URL_MALFORMAT; + failf(data, "aws-sigv4: region too long in hostname"); + result = CURLE_URL_MALFORMAT; goto fail; } - strncpy(region, reg, len); + memcpy(region, reg, len); region[len] = '\0'; + infof(data, "aws_sigv4: picked region %s from host", region); } } @@ -491,11 +650,11 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) if(!payload_hash) { if(sign_as_s3) - ret = calc_s3_payload_hash(data, httpreq, provider1, sha_hash, - sha_hex, content_sha256_hdr); + result = calc_s3_payload_hash(data, httpreq, provider1, sha_hash, + sha_hex, content_sha256_hdr); else - ret = calc_payload_hash(data, sha_hash, sha_hex); - if(ret) + result = calc_payload_hash(data, sha_hash, sha_hex); + if(result) goto fail; payload_hash = sha_hex; @@ -514,21 +673,20 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) #else time(&clock); #endif - ret = Curl_gmtime(clock, &tm); - if(ret) { + result = Curl_gmtime(clock, &tm); + if(result) { goto fail; } if(!strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", &tm)) { - ret = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; goto fail; } - ret = make_headers(data, hostname, timestamp, provider1, - &date_header, content_sha256_hdr, - &canonical_headers, &signed_headers); - if(ret) + result = make_headers(data, hostname, timestamp, provider1, + &date_header, content_sha256_hdr, + &canonical_headers, &signed_headers); + if(result) goto fail; - ret = CURLE_OUT_OF_MEMORY; if(*content_sha256_hdr) { /* make_headers() needed this without the \r\n for canonicalization */ @@ -540,6 +698,11 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) memcpy(date, timestamp, sizeof(date)); date[sizeof(date) - 1] = 0; + result = canon_query(data, data->state.up.query, &canonical_query); + if(result) + goto fail; + result = CURLE_OUT_OF_MEMORY; + canonical_request = curl_maprintf("%s\n" /* HTTPRequestMethod */ "%s\n" /* CanonicalURI */ @@ -549,13 +712,16 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) "%.*s", /* HashedRequestPayload in hex */ method, data->state.up.path, - data->state.up.query ? data->state.up.query : "", + Curl_dyn_ptr(&canonical_query) ? + Curl_dyn_ptr(&canonical_query) : "", Curl_dyn_ptr(&canonical_headers), Curl_dyn_ptr(&signed_headers), (int)payload_hash_len, payload_hash); if(!canonical_request) goto fail; + DEBUGF(infof(data, "Canonical request: %s", canonical_request)); + /* provider 0 lowercase */ Curl_strntolower(provider0, provider0, strlen(provider0)); request_type = curl_maprintf("%s4_request", provider0); @@ -612,14 +778,19 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) "Credential=%s/%s, " "SignedHeaders=%s, " "Signature=%s\r\n" - "%s\r\n" + /* + * date_header is added here, only if it wasn't + * user-specified (using CURLOPT_HTTPHEADER). + * date_header includes \r\n + */ + "%s" "%s", /* optional sha256 header includes \r\n */ provider0, user, credential_scope, Curl_dyn_ptr(&signed_headers), sha_hex, - date_header, + date_header ? date_header : "", content_sha256_hdr); if(!auth_headers) { goto fail; @@ -628,9 +799,10 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) Curl_safefree(data->state.aptr.userpwd); data->state.aptr.userpwd = auth_headers; data->state.authhost.done = TRUE; - ret = CURLE_OK; + result = CURLE_OK; fail: + Curl_dyn_free(&canonical_query); Curl_dyn_free(&canonical_headers); Curl_dyn_free(&signed_headers); free(canonical_request); @@ -639,7 +811,7 @@ fail: free(str_to_sign); free(secret); free(date_header); - return ret; + return result; } -#endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) */ +#endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_AWS) */ diff --git a/contrib/libs/curl/lib/http_aws_sigv4.h b/contrib/libs/curl/lib/http_aws_sigv4.h index 57cc5706eb..85755e937b 100644 --- a/contrib/libs/curl/lib/http_aws_sigv4.h +++ b/contrib/libs/curl/lib/http_aws_sigv4.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/http_chunks.c b/contrib/libs/curl/lib/http_chunks.c index bda00d3833..0b836851ac 100644 --- a/contrib/libs/curl/lib/http_chunks.c +++ b/contrib/libs/curl/lib/http_chunks.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -113,7 +113,7 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, *wrote = 0; /* nothing's written yet */ /* the original data is written to the client, but we go on with the - chunk read process, to properly calculate the content length */ + chunk read process, to properly calculate the content length*/ if(data->set.http_te_skip && !k->ignorebody) { result = Curl_client_write(data, CLIENTWRITE_BODY, datap, datalen); if(result) { diff --git a/contrib/libs/curl/lib/http_chunks.h b/contrib/libs/curl/lib/http_chunks.h index ed50713162..2cf5507c21 100644 --- a/contrib/libs/curl/lib/http_chunks.h +++ b/contrib/libs/curl/lib/http_chunks.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/http_digest.c b/contrib/libs/curl/lib/http_digest.c index 8daad99e32..a71c6b7cfb 100644 --- a/contrib/libs/curl/lib/http_digest.c +++ b/contrib/libs/curl/lib/http_digest.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -81,7 +81,7 @@ CURLcode Curl_output_digest(struct Curl_easy *data, bool have_chlg; /* Point to the address of the pointer that holds the string to send to the - server, which is for a plain host or for an HTTP proxy */ + server, which is for a plain host or for a HTTP proxy */ char **allocuserpwd; /* Point to the name and password for this */ diff --git a/contrib/libs/curl/lib/http_digest.h b/contrib/libs/curl/lib/http_digest.h index 7d5cfc1bfd..eea90b7439 100644 --- a/contrib/libs/curl/lib/http_digest.h +++ b/contrib/libs/curl/lib/http_digest.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/http_negotiate.c b/contrib/libs/curl/lib/http_negotiate.c index 153e3d4ab8..5909f85b0d 100644 --- a/contrib/libs/curl/lib/http_negotiate.c +++ b/contrib/libs/curl/lib/http_negotiate.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/http_negotiate.h b/contrib/libs/curl/lib/http_negotiate.h index 76d8356132..6e2096c697 100644 --- a/contrib/libs/curl/lib/http_negotiate.h +++ b/contrib/libs/curl/lib/http_negotiate.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/http_ntlm.c b/contrib/libs/curl/lib/http_ntlm.c index b845ddf37f..5a6a977905 100644 --- a/contrib/libs/curl/lib/http_ntlm.c +++ b/contrib/libs/curl/lib/http_ntlm.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -134,7 +134,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) struct bufref ntlmmsg; /* point to the address of the pointer that holds the string to send to the - server, which is for a plain host or for an HTTP proxy */ + server, which is for a plain host or for a HTTP proxy */ char **allocuserpwd; /* point to the username, password, service and host */ diff --git a/contrib/libs/curl/lib/http_ntlm.h b/contrib/libs/curl/lib/http_ntlm.h index f37572baec..cec63b82c3 100644 --- a/contrib/libs/curl/lib/http_ntlm.h +++ b/contrib/libs/curl/lib/http_ntlm.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/http_proxy.c b/contrib/libs/curl/lib/http_proxy.c index 551578ef4f..d1933df0e3 100644 --- a/contrib/libs/curl/lib/http_proxy.c +++ b/contrib/libs/curl/lib/http_proxy.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -26,7 +26,7 @@ #include "http_proxy.h" -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY) +#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) #include <curl/curl.h> #ifdef USE_HYPER @@ -37,9 +37,6 @@ #include "url.h" #include "select.h" #include "progress.h" -#include "cfilters.h" -#include "cf-h1-proxy.h" -#include "cf-h2-proxy.h" #include "connect.h" #include "curlx.h" #include "vtls/vtls.h" @@ -51,180 +48,1030 @@ #include "curl_memory.h" #include "memdebug.h" +/* + * Perform SSL initialization for HTTPS proxy. Sets + * proxy_ssl_connected connection bit when complete. Can be + * called multiple times. + */ +static CURLcode https_proxy_connect(struct Curl_easy *data, int sockindex) +{ +#ifdef USE_SSL + struct connectdata *conn = data->conn; + CURLcode result = CURLE_OK; + DEBUGASSERT(conn->http_proxy.proxytype == CURLPROXY_HTTPS); + if(!conn->bits.proxy_ssl_connected[sockindex]) { + /* perform SSL initialization for this socket */ + result = + Curl_ssl_connect_nonblocking(data, conn, TRUE, sockindex, + &conn->bits.proxy_ssl_connected[sockindex]); + if(result) + /* a failed connection is marked for closure to prevent (bad) re-use or + similar */ + connclose(conn, "TLS handshake failed"); + } + return result; +#else + (void) data; + (void) sockindex; + return CURLE_NOT_BUILT_IN; +#endif +} + +CURLcode Curl_proxy_connect(struct Curl_easy *data, int sockindex) +{ + struct connectdata *conn = data->conn; + if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) { + const CURLcode result = https_proxy_connect(data, sockindex); + if(result) + return result; + if(!conn->bits.proxy_ssl_connected[sockindex]) + return result; /* wait for HTTPS proxy SSL initialization to complete */ + } + + if(conn->bits.tunnel_proxy && conn->bits.httpproxy) { +#ifndef CURL_DISABLE_PROXY + /* for [protocol] tunneled through HTTP proxy */ + const char *hostname; + int remote_port; + CURLcode result; -struct cf_proxy_ctx { - /* the protocol specific sub-filter we install during connect */ - struct Curl_cfilter *cf_protocol; -}; + /* We want "seamless" operations through HTTP proxy tunnel */ + + /* for the secondary socket (FTP), use the "connect to host" + * but ignore the "connect to port" (use the secondary port) + */ -static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking, bool *done) + if(conn->bits.conn_to_host) + hostname = conn->conn_to_host.name; + else if(sockindex == SECONDARYSOCKET) + hostname = conn->secondaryhostname; + else + hostname = conn->host.name; + + if(sockindex == SECONDARYSOCKET) + remote_port = conn->secondary_port; + else if(conn->bits.conn_to_port) + remote_port = conn->conn_to_port; + else + remote_port = conn->remote_port; + + result = Curl_proxyCONNECT(data, sockindex, hostname, remote_port); + if(CURLE_OK != result) + return result; + Curl_safefree(data->state.aptr.proxyuserpwd); +#else + return CURLE_NOT_BUILT_IN; +#endif + } + /* no HTTP tunnel proxy, just return */ + return CURLE_OK; +} + +bool Curl_connect_complete(struct connectdata *conn) { - struct cf_proxy_ctx *ctx = cf->ctx; - CURLcode result; + return !conn->connect_state || + (conn->connect_state->tunnel_state >= TUNNEL_COMPLETE); +} + +bool Curl_connect_ongoing(struct connectdata *conn) +{ + return conn->connect_state && + (conn->connect_state->tunnel_state <= TUNNEL_COMPLETE); +} + +/* when we've sent a CONNECT to a proxy, we should rather either wait for the + socket to become readable to be able to get the response headers or if + we're still sending the request, wait for write. */ +int Curl_connect_getsock(struct connectdata *conn) +{ + struct HTTP *http; + DEBUGASSERT(conn); + DEBUGASSERT(conn->connect_state); + http = &conn->connect_state->http_proxy; + + if(http->sending == HTTPSEND_REQUEST) + return GETSOCK_WRITESOCK(0); + + return GETSOCK_READSOCK(0); +} - if(cf->connected) { - *done = TRUE; - return CURLE_OK; +static CURLcode connect_init(struct Curl_easy *data, bool reinit) +{ + struct http_connect_state *s; + struct connectdata *conn = data->conn; + if(conn->handler->flags & PROTOPT_NOTCPPROXY) { + failf(data, "%s cannot be done over CONNECT", conn->handler->scheme); + return CURLE_UNSUPPORTED_PROTOCOL; } + if(!reinit) { + CURLcode result; + DEBUGASSERT(!conn->connect_state); + /* we might need the upload buffer for streaming a partial request */ + result = Curl_get_upload_buffer(data); + if(result) + return result; - DEBUGF(LOG_CF(data, cf, "connect")); -connect_sub: - result = cf->next->cft->do_connect(cf->next, data, blocking, done); - if(result || !*done) - return result; - - *done = FALSE; - if(!ctx->cf_protocol) { - struct Curl_cfilter *cf_protocol = NULL; - int alpn = Curl_conn_cf_is_ssl(cf->next)? - cf->conn->proxy_alpn : CURL_HTTP_VERSION_1_1; - - /* First time call after the subchain connected */ - switch(alpn) { - case CURL_HTTP_VERSION_NONE: - case CURL_HTTP_VERSION_1_0: - case CURL_HTTP_VERSION_1_1: - DEBUGF(LOG_CF(data, cf, "installing subfilter for HTTP/1.1")); - infof(data, "CONNECT tunnel: HTTP/1.%d negotiated", - (alpn == CURL_HTTP_VERSION_1_0)? 0 : 1); - result = Curl_cf_h1_proxy_insert_after(cf, data); - if(result) - goto out; - cf_protocol = cf->next; - break; -#ifdef USE_NGHTTP2 - case CURL_HTTP_VERSION_2: - DEBUGF(LOG_CF(data, cf, "installing subfilter for HTTP/2")); - infof(data, "CONNECT tunnel: HTTP/2 negotiated"); - result = Curl_cf_h2_proxy_insert_after(cf, data); - if(result) - goto out; - cf_protocol = cf->next; - break; -#endif - default: - DEBUGF(LOG_CF(data, cf, "installing subfilter for default HTTP/1.1")); - infof(data, "CONNECT tunnel: unsupported ALPN(%d) negotiated", alpn); - result = CURLE_COULDNT_CONNECT; - goto out; - } + s = calloc(1, sizeof(struct http_connect_state)); + if(!s) + return CURLE_OUT_OF_MEMORY; + infof(data, "allocate connect buffer"); + conn->connect_state = s; + Curl_dyn_init(&s->rcvbuf, DYN_PROXY_CONNECT_HEADERS); - ctx->cf_protocol = cf_protocol; - /* after we installed the filter "below" us, we call connect - * on out sub-chain again. + /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the + * member conn->proto.http; we want [protocol] through HTTP and we have + * to change the member temporarily for connecting to the HTTP + * proxy. After Curl_proxyCONNECT we have to set back the member to the + * original pointer + * + * This function might be called several times in the multi interface case + * if the proxy's CONNECT response is not instant. */ - goto connect_sub; + s->prot_save = data->req.p.http; + data->req.p.http = &s->http_proxy; + connkeep(conn, "HTTP proxy CONNECT"); } else { - /* subchain connected and we had already installed the protocol filter. - * This means the protocol tunnel is established, we are done. - */ - DEBUGASSERT(ctx->cf_protocol); - result = CURLE_OK; + DEBUGASSERT(conn->connect_state); + s = conn->connect_state; + Curl_dyn_reset(&s->rcvbuf); } + s->tunnel_state = TUNNEL_INIT; + s->keepon = KEEPON_CONNECT; + s->cl = 0; + s->close_connection = FALSE; + return CURLE_OK; +} -out: - if(!result) { - cf->connected = TRUE; - *done = TRUE; +void Curl_connect_done(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + struct http_connect_state *s = conn->connect_state; + if(s && (s->tunnel_state != TUNNEL_EXIT)) { + s->tunnel_state = TUNNEL_EXIT; + Curl_dyn_free(&s->rcvbuf); + Curl_dyn_free(&s->req); + + /* restore the protocol pointer */ + data->req.p.http = s->prot_save; + data->info.httpcode = 0; /* clear it as it might've been used for the + proxy */ + data->req.ignorebody = FALSE; +#ifdef USE_HYPER + data->state.hconnect = FALSE; +#endif + infof(data, "CONNECT phase completed"); } - return result; } -void Curl_cf_http_proxy_get_host(struct Curl_cfilter *cf, - struct Curl_easy *data, - const char **phost, - const char **pdisplay_host, - int *pport) +static CURLcode CONNECT_host(struct Curl_easy *data, + struct connectdata *conn, + const char *hostname, + int remote_port, + char **connecthostp, + char **hostp) { - (void)data; - if(!cf->connected) { - *phost = cf->conn->http_proxy.host.name; - *pdisplay_host = cf->conn->http_proxy.host.dispname; - *pport = (int)cf->conn->http_proxy.port; - } - else { - cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport); + char *hostheader; /* for CONNECT */ + char *host = NULL; /* Host: */ + bool ipv6_ip = conn->bits.ipv6_ip; + + /* the hostname may be different */ + if(hostname != conn->host.name) + ipv6_ip = (strchr(hostname, ':') != NULL); + hostheader = /* host:port with IPv6 support */ + aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"", + remote_port); + if(!hostheader) + return CURLE_OUT_OF_MEMORY; + + if(!Curl_checkProxyheaders(data, conn, STRCONST("Host"))) { + host = aprintf("Host: %s\r\n", hostheader); + if(!host) { + free(hostheader); + return CURLE_OUT_OF_MEMORY; + } } + *connecthostp = hostheader; + *hostp = host; + return CURLE_OK; } -static void http_proxy_cf_destroy(struct Curl_cfilter *cf, - struct Curl_easy *data) +#ifndef USE_HYPER +static CURLcode CONNECT(struct Curl_easy *data, + int sockindex, + const char *hostname, + int remote_port) { - struct cf_proxy_ctx *ctx = cf->ctx; + int subversion = 0; + struct SingleRequest *k = &data->req; + CURLcode result; + struct connectdata *conn = data->conn; + curl_socket_t tunnelsocket = conn->sock[sockindex]; + struct http_connect_state *s = conn->connect_state; + struct HTTP *http = data->req.p.http; + char *linep; + size_t perline; - (void)data; - DEBUGF(LOG_CF(data, cf, "destroy")); - free(ctx); -} +#define SELECT_OK 0 +#define SELECT_ERROR 1 + + if(Curl_connect_complete(conn)) + return CURLE_OK; /* CONNECT is already completed */ + + conn->bits.proxy_connect_closed = FALSE; + + do { + timediff_t check; + if(TUNNEL_INIT == s->tunnel_state) { + /* BEGIN CONNECT PHASE */ + struct dynbuf *req = &s->req; + char *hostheader = NULL; + char *host = NULL; + + infof(data, "Establish HTTP proxy tunnel to %s:%d", + hostname, remote_port); + + /* This only happens if we've looped here due to authentication + reasons, and we don't really use the newly cloned URL here + then. Just free() it. */ + Curl_safefree(data->req.newurl); + + /* initialize send-buffer */ + Curl_dyn_init(req, DYN_HTTP_REQUEST); + + result = CONNECT_host(data, conn, + hostname, remote_port, &hostheader, &host); + if(result) + return result; + + /* Setup the proxy-authorization header, if any */ + result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET, + hostheader, TRUE); + + if(!result) { + const char *httpv = + (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? "1.0" : "1.1"; + + result = + Curl_dyn_addf(req, + "CONNECT %s HTTP/%s\r\n" + "%s" /* Host: */ + "%s", /* Proxy-Authorization */ + hostheader, + httpv, + host?host:"", + data->state.aptr.proxyuserpwd? + data->state.aptr.proxyuserpwd:""); + + if(!result && !Curl_checkProxyheaders(data, + conn, STRCONST("User-Agent")) && + data->set.str[STRING_USERAGENT]) + result = Curl_dyn_addf(req, "User-Agent: %s\r\n", + data->set.str[STRING_USERAGENT]); + + if(!result && !Curl_checkProxyheaders(data, conn, + STRCONST("Proxy-Connection"))) + result = Curl_dyn_addn(req, + STRCONST("Proxy-Connection: Keep-Alive\r\n")); + + if(!result) + result = Curl_add_custom_headers(data, TRUE, req); + + if(!result) + /* CRLF terminate the request */ + result = Curl_dyn_addn(req, STRCONST("\r\n")); + + if(!result) { + /* Send the connect request to the proxy */ + result = Curl_buffer_send(req, data, &data->info.request_size, 0, + sockindex); + s->headerlines = 0; + } + if(result) + failf(data, "Failed sending CONNECT to proxy"); + } + free(host); + free(hostheader); + if(result) + return result; + + s->tunnel_state = TUNNEL_CONNECT; + } /* END CONNECT PHASE */ + + check = Curl_timeleft(data, NULL, TRUE); + if(check <= 0) { + failf(data, "Proxy CONNECT aborted due to timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + + if(!Curl_conn_data_pending(conn, sockindex) && !http->sending) + /* return so we'll be called again polling-style */ + return CURLE_OK; + + /* at this point, the tunnel_connecting phase is over. */ + + if(http->sending == HTTPSEND_REQUEST) { + if(!s->nsend) { + size_t fillcount; + k->upload_fromhere = data->state.ulbuf; + result = Curl_fillreadbuffer(data, data->set.upload_buffer_size, + &fillcount); + if(result) + return result; + s->nsend = fillcount; + } + if(s->nsend) { + ssize_t bytes_written; + /* write to socket (send away data) */ + result = Curl_write(data, + conn->writesockfd, /* socket to send to */ + k->upload_fromhere, /* buffer pointer */ + s->nsend, /* buffer size */ + &bytes_written); /* actually sent */ + + if(!result) + /* send to debug callback! */ + Curl_debug(data, CURLINFO_HEADER_OUT, + k->upload_fromhere, bytes_written); + + s->nsend -= bytes_written; + k->upload_fromhere += bytes_written; + return result; + } + http->sending = HTTPSEND_NADA; + /* if nothing left to send, continue */ + } + { /* READING RESPONSE PHASE */ + int error = SELECT_OK; + + while(s->keepon) { + ssize_t gotbytes; + char byte; + + /* Read one byte at a time to avoid a race condition. Wait at most one + second before looping to ensure continuous pgrsUpdates. */ + result = Curl_read(data, tunnelsocket, &byte, 1, &gotbytes); + if(result == CURLE_AGAIN) + /* socket buffer drained, return */ + return CURLE_OK; + + if(Curl_pgrsUpdate(data)) + return CURLE_ABORTED_BY_CALLBACK; + + if(result) { + s->keepon = KEEPON_DONE; + break; + } + else if(gotbytes <= 0) { + if(data->set.proxyauth && data->state.authproxy.avail && + data->state.aptr.proxyuserpwd) { + /* proxy auth was requested and there was proxy auth available, + then deem this as "mere" proxy disconnect */ + conn->bits.proxy_connect_closed = TRUE; + infof(data, "Proxy CONNECT connection closed"); + } + else { + error = SELECT_ERROR; + failf(data, "Proxy CONNECT aborted"); + } + s->keepon = KEEPON_DONE; + break; + } + + if(s->keepon == KEEPON_IGNORE) { + /* This means we are currently ignoring a response-body */ + + if(s->cl) { + /* A Content-Length based body: simply count down the counter + and make sure to break out of the loop when we're done! */ + s->cl--; + if(s->cl <= 0) { + s->keepon = KEEPON_DONE; + s->tunnel_state = TUNNEL_COMPLETE; + break; + } + } + else { + /* chunked-encoded body, so we need to do the chunked dance + properly to know when the end of the body is reached */ + CHUNKcode r; + CURLcode extra; + ssize_t tookcareof = 0; + + /* now parse the chunked piece of data so that we can + properly tell when the stream ends */ + r = Curl_httpchunk_read(data, &byte, 1, &tookcareof, &extra); + if(r == CHUNKE_STOP) { + /* we're done reading chunks! */ + infof(data, "chunk reading DONE"); + s->keepon = KEEPON_DONE; + /* we did the full CONNECT treatment, go COMPLETE */ + s->tunnel_state = TUNNEL_COMPLETE; + } + } + continue; + } + + if(Curl_dyn_addn(&s->rcvbuf, &byte, 1)) { + failf(data, "CONNECT response too large"); + return CURLE_RECV_ERROR; + } + + /* if this is not the end of a header line then continue */ + if(byte != 0x0a) + continue; + + s->headerlines++; + linep = Curl_dyn_ptr(&s->rcvbuf); + perline = Curl_dyn_len(&s->rcvbuf); /* amount of bytes in this line */ + + /* output debug if that is requested */ + Curl_debug(data, CURLINFO_HEADER_IN, linep, perline); + + if(!data->set.suppress_connect_headers) { + /* send the header to the callback */ + int writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT | + (data->set.include_header ? CLIENTWRITE_BODY : 0) | + (s->headerlines == 1 ? CLIENTWRITE_STATUS : 0); + + result = Curl_client_write(data, writetype, linep, perline); + if(result) + return result; + } + + data->info.header_size += (long)perline; + + /* Newlines are CRLF, so the CR is ignored as the line isn't + really terminated until the LF comes. Treat a following CR + as end-of-headers as well.*/ + + if(('\r' == linep[0]) || + ('\n' == linep[0])) { + /* end of response-headers from the proxy */ + + if((407 == k->httpcode) && !data->state.authproblem) { + /* If we get a 407 response code with content length + when we have no auth problem, we must ignore the + whole response-body */ + s->keepon = KEEPON_IGNORE; + + if(s->cl) { + infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T + " bytes of response-body", s->cl); + } + else if(s->chunked_encoding) { + CHUNKcode r; + CURLcode extra; + + infof(data, "Ignore chunked response-body"); + + /* We set ignorebody true here since the chunked decoder + function will acknowledge that. Pay attention so that this is + cleared again when this function returns! */ + k->ignorebody = TRUE; + + if(linep[1] == '\n') + /* this can only be a LF if the letter at index 0 was a CR */ + linep++; + + /* now parse the chunked piece of data so that we can properly + tell when the stream ends */ + r = Curl_httpchunk_read(data, linep + 1, 1, &gotbytes, + &extra); + if(r == CHUNKE_STOP) { + /* we're done reading chunks! */ + infof(data, "chunk reading DONE"); + s->keepon = KEEPON_DONE; + /* we did the full CONNECT treatment, go to COMPLETE */ + s->tunnel_state = TUNNEL_COMPLETE; + } + } + else { + /* without content-length or chunked encoding, we + can't keep the connection alive since the close is + the end signal so we bail out at once instead */ + s->keepon = KEEPON_DONE; + } + } + else + s->keepon = KEEPON_DONE; + + if(s->keepon == KEEPON_DONE && !s->cl) + /* we did the full CONNECT treatment, go to COMPLETE */ + s->tunnel_state = TUNNEL_COMPLETE; + + DEBUGASSERT(s->keepon == KEEPON_IGNORE || s->keepon == KEEPON_DONE); + continue; + } + + if((checkprefix("WWW-Authenticate:", linep) && + (401 == k->httpcode)) || + (checkprefix("Proxy-authenticate:", linep) && + (407 == k->httpcode))) { + + bool proxy = (k->httpcode == 407) ? TRUE : FALSE; + char *auth = Curl_copy_header_value(linep); + if(!auth) + return CURLE_OUT_OF_MEMORY; + + result = Curl_http_input_auth(data, proxy, auth); + + free(auth); -static void http_proxy_cf_close(struct Curl_cfilter *cf, - struct Curl_easy *data) + if(result) + return result; + } + else if(checkprefix("Content-Length:", linep)) { + if(k->httpcode/100 == 2) { + /* A client MUST ignore any Content-Length or Transfer-Encoding + header fields received in a successful response to CONNECT. + "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */ + infof(data, "Ignoring Content-Length in CONNECT %03d response", + k->httpcode); + } + else { + (void)curlx_strtoofft(linep + + strlen("Content-Length:"), NULL, 10, &s->cl); + } + } + else if(Curl_compareheader(linep, + STRCONST("Connection:"), STRCONST("close"))) + s->close_connection = TRUE; + else if(checkprefix("Transfer-Encoding:", linep)) { + if(k->httpcode/100 == 2) { + /* A client MUST ignore any Content-Length or Transfer-Encoding + header fields received in a successful response to CONNECT. + "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */ + infof(data, "Ignoring Transfer-Encoding in " + "CONNECT %03d response", k->httpcode); + } + else if(Curl_compareheader(linep, + STRCONST("Transfer-Encoding:"), + STRCONST("chunked"))) { + infof(data, "CONNECT responded chunked"); + s->chunked_encoding = TRUE; + /* init our chunky engine */ + Curl_httpchunk_init(data); + } + } + else if(Curl_compareheader(linep, + STRCONST("Proxy-Connection:"), + STRCONST("close"))) + s->close_connection = TRUE; + else if(2 == sscanf(linep, "HTTP/1.%d %d", + &subversion, + &k->httpcode)) { + /* store the HTTP code from the proxy */ + data->info.httpproxycode = k->httpcode; + } + + Curl_dyn_reset(&s->rcvbuf); + } /* while there's buffer left and loop is requested */ + + if(Curl_pgrsUpdate(data)) + return CURLE_ABORTED_BY_CALLBACK; + + if(error) + return CURLE_RECV_ERROR; + + if(data->info.httpproxycode/100 != 2) { + /* Deal with the possibly already received authenticate + headers. 'newurl' is set to a new URL if we must loop. */ + result = Curl_http_auth_act(data); + if(result) + return result; + + if(conn->bits.close) + /* the connection has been marked for closure, most likely in the + Curl_http_auth_act() function and thus we can kill it at once + below */ + s->close_connection = TRUE; + } + + if(s->close_connection && data->req.newurl) { + /* Connection closed by server. Don't use it anymore */ + Curl_closesocket(data, conn, conn->sock[sockindex]); + conn->sock[sockindex] = CURL_SOCKET_BAD; + break; + } + } /* END READING RESPONSE PHASE */ + + /* If we are supposed to continue and request a new URL, which basically + * means the HTTP authentication is still going on so if the tunnel + * is complete we start over in INIT state */ + if(data->req.newurl && (TUNNEL_COMPLETE == s->tunnel_state)) { + connect_init(data, TRUE); /* reinit */ + } + + } while(data->req.newurl); + + if(data->info.httpproxycode/100 != 2) { + if(s->close_connection && data->req.newurl) { + conn->bits.proxy_connect_closed = TRUE; + infof(data, "Connect me again please"); + Curl_connect_done(data); + } + else { + free(data->req.newurl); + data->req.newurl = NULL; + /* failure, close this connection to avoid re-use */ + streamclose(conn, "proxy CONNECT failure"); + } + + /* to back to init state */ + s->tunnel_state = TUNNEL_INIT; + + if(conn->bits.proxy_connect_closed) + /* this is not an error, just part of the connection negotiation */ + return CURLE_OK; + Curl_dyn_free(&s->rcvbuf); + failf(data, "Received HTTP code %d from proxy after CONNECT", + data->req.httpcode); + return CURLE_RECV_ERROR; + } + + s->tunnel_state = TUNNEL_COMPLETE; + + /* If a proxy-authorization header was used for the proxy, then we should + make sure that it isn't accidentally used for the document request + after we've connected. So let's free and clear it here. */ + Curl_safefree(data->state.aptr.proxyuserpwd); + data->state.aptr.proxyuserpwd = NULL; + + data->state.authproxy.done = TRUE; + data->state.authproxy.multipass = FALSE; + + infof(data, "Proxy replied %d to CONNECT request", + data->info.httpproxycode); + data->req.ignorebody = FALSE; /* put it (back) to non-ignore state */ + conn->bits.rewindaftersend = FALSE; /* make sure this isn't set for the + document request */ + Curl_dyn_free(&s->rcvbuf); + return CURLE_OK; +} +#else +/* The Hyper version of CONNECT */ +static CURLcode CONNECT(struct Curl_easy *data, + int sockindex, + const char *hostname, + int remote_port) { - struct cf_proxy_ctx *ctx = cf->ctx; - - DEBUGF(LOG_CF(data, cf, "close")); - cf->connected = FALSE; - if(ctx->cf_protocol) { - struct Curl_cfilter *f; - /* if someone already removed it, we assume he also - * took care of destroying it. */ - for(f = cf->next; f; f = f->next) { - if(f == ctx->cf_protocol) { - /* still in our sub-chain */ - Curl_conn_cf_discard_sub(cf, ctx->cf_protocol, data, FALSE); + struct connectdata *conn = data->conn; + struct hyptransfer *h = &data->hyp; + curl_socket_t tunnelsocket = conn->sock[sockindex]; + struct http_connect_state *s = conn->connect_state; + CURLcode result = CURLE_OUT_OF_MEMORY; + hyper_io *io = NULL; + hyper_request *req = NULL; + hyper_headers *headers = NULL; + hyper_clientconn_options *options = NULL; + hyper_task *handshake = NULL; + hyper_task *task = NULL; /* for the handshake */ + hyper_task *sendtask = NULL; /* for the send */ + hyper_clientconn *client = NULL; + hyper_error *hypererr = NULL; + char *hostheader = NULL; /* for CONNECT */ + char *host = NULL; /* Host: */ + + if(Curl_connect_complete(conn)) + return CURLE_OK; /* CONNECT is already completed */ + + conn->bits.proxy_connect_closed = FALSE; + + do { + switch(s->tunnel_state) { + case TUNNEL_INIT: + /* BEGIN CONNECT PHASE */ + io = hyper_io_new(); + if(!io) { + failf(data, "Couldn't create hyper IO"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + /* tell Hyper how to read/write network data */ + hyper_io_set_userdata(io, data); + hyper_io_set_read(io, Curl_hyper_recv); + hyper_io_set_write(io, Curl_hyper_send); + conn->sockfd = tunnelsocket; + + data->state.hconnect = TRUE; + + /* create an executor to poll futures */ + if(!h->exec) { + h->exec = hyper_executor_new(); + if(!h->exec) { + failf(data, "Couldn't create hyper executor"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + } + + options = hyper_clientconn_options_new(); + hyper_clientconn_options_set_preserve_header_case(options, 1); + hyper_clientconn_options_set_preserve_header_order(options, 1); + + if(!options) { + failf(data, "Couldn't create hyper client options"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + + hyper_clientconn_options_exec(options, h->exec); + + /* "Both the `io` and the `options` are consumed in this function + call" */ + handshake = hyper_clientconn_handshake(io, options); + if(!handshake) { + failf(data, "Couldn't create hyper client handshake"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + io = NULL; + options = NULL; + + if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) { + failf(data, "Couldn't hyper_executor_push the handshake"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + handshake = NULL; /* ownership passed on */ + + task = hyper_executor_poll(h->exec); + if(!task) { + failf(data, "Couldn't hyper_executor_poll the handshake"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + + client = hyper_task_value(task); + hyper_task_free(task); + req = hyper_request_new(); + if(!req) { + failf(data, "Couldn't hyper_request_new"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + if(hyper_request_set_method(req, (uint8_t *)"CONNECT", + strlen("CONNECT"))) { + failf(data, "error setting method"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + + infof(data, "Establish HTTP proxy tunnel to %s:%d", + hostname, remote_port); + + /* This only happens if we've looped here due to authentication + reasons, and we don't really use the newly cloned URL here + then. Just free() it. */ + Curl_safefree(data->req.newurl); + + result = CONNECT_host(data, conn, hostname, remote_port, + &hostheader, &host); + if(result) + goto error; + + if(hyper_request_set_uri(req, (uint8_t *)hostheader, + strlen(hostheader))) { + failf(data, "error setting path"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + if(data->set.verbose) { + char *se = aprintf("CONNECT %s HTTP/1.1\r\n", hostheader); + if(!se) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + Curl_debug(data, CURLINFO_HEADER_OUT, se, strlen(se)); + free(se); + } + /* Setup the proxy-authorization header, if any */ + result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET, + hostheader, TRUE); + if(result) + goto error; + Curl_safefree(hostheader); + + /* default is 1.1 */ + if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) && + (HYPERE_OK != hyper_request_set_version(req, + HYPER_HTTP_VERSION_1_0))) { + failf(data, "error setting HTTP version"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + + headers = hyper_request_headers(req); + if(!headers) { + failf(data, "hyper_request_headers"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + if(host) { + result = Curl_hyper_header(data, headers, host); + if(result) + goto error; + Curl_safefree(host); + } + + if(data->state.aptr.proxyuserpwd) { + result = Curl_hyper_header(data, headers, + data->state.aptr.proxyuserpwd); + if(result) + goto error; + } + + if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) && + data->set.str[STRING_USERAGENT]) { + struct dynbuf ua; + Curl_dyn_init(&ua, DYN_HTTP_REQUEST); + result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n", + data->set.str[STRING_USERAGENT]); + if(result) + goto error; + result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua)); + if(result) + goto error; + Curl_dyn_free(&ua); + } + + if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) { + result = Curl_hyper_header(data, headers, + "Proxy-Connection: Keep-Alive"); + if(result) + goto error; + } + + result = Curl_add_custom_headers(data, TRUE, headers); + if(result) + goto error; + + sendtask = hyper_clientconn_send(client, req); + if(!sendtask) { + failf(data, "hyper_clientconn_send"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + + if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) { + failf(data, "Couldn't hyper_executor_push the send"); + result = CURLE_OUT_OF_MEMORY; + goto error; + } + + hyper_clientconn_free(client); + + do { + task = hyper_executor_poll(h->exec); + if(task) { + bool error = hyper_task_type(task) == HYPER_TASK_ERROR; + if(error) + hypererr = hyper_task_value(task); + hyper_task_free(task); + if(error) { + /* this could probably use a better error code? */ + result = CURLE_OUT_OF_MEMORY; + goto error; + } + } + } while(task); + s->tunnel_state = TUNNEL_CONNECT; + /* FALLTHROUGH */ + case TUNNEL_CONNECT: { + int didwhat; + bool done = FALSE; + result = Curl_hyper_stream(data, conn, &didwhat, &done, + CURL_CSELECT_IN | CURL_CSELECT_OUT); + if(result) + goto error; + if(!done) break; + s->tunnel_state = TUNNEL_COMPLETE; + if(h->exec) { + hyper_executor_free(h->exec); + h->exec = NULL; + } + if(h->read_waker) { + hyper_waker_free(h->read_waker); + h->read_waker = NULL; + } + if(h->write_waker) { + hyper_waker_free(h->write_waker); + h->write_waker = NULL; } } - ctx->cf_protocol = NULL; + break; + + default: + break; + } + + if(conn->bits.close && data->req.newurl) { + /* Connection closed by server. Don't use it anymore */ + Curl_closesocket(data, conn, conn->sock[sockindex]); + conn->sock[sockindex] = CURL_SOCKET_BAD; + break; + } + + /* If we are supposed to continue and request a new URL, which basically + * means the HTTP authentication is still going on so if the tunnel + * is complete we start over in INIT state */ + if(data->req.newurl && (TUNNEL_COMPLETE == s->tunnel_state)) { + infof(data, "CONNECT request done, loop to make another"); + connect_init(data, TRUE); /* reinit */ + } + } while(data->req.newurl); + + result = CURLE_OK; + if(s->tunnel_state == TUNNEL_COMPLETE) { + if(data->info.httpproxycode/100 != 2) { + if(conn->bits.close && data->req.newurl) { + conn->bits.proxy_connect_closed = TRUE; + infof(data, "Connect me again please"); + Curl_connect_done(data); + } + else { + free(data->req.newurl); + data->req.newurl = NULL; + /* failure, close this connection to avoid re-use */ + streamclose(conn, "proxy CONNECT failure"); + Curl_closesocket(data, conn, conn->sock[sockindex]); + conn->sock[sockindex] = CURL_SOCKET_BAD; + } + + /* to back to init state */ + s->tunnel_state = TUNNEL_INIT; + + if(!conn->bits.proxy_connect_closed) { + failf(data, "Received HTTP code %d from proxy after CONNECT", + data->req.httpcode); + result = CURLE_RECV_ERROR; + } + } + } + error: + free(host); + free(hostheader); + if(io) + hyper_io_free(io); + + if(options) + hyper_clientconn_options_free(options); + + if(handshake) + hyper_task_free(handshake); + + if(hypererr) { + uint8_t errbuf[256]; + size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf)); + failf(data, "Hyper: %.*s", (int)errlen, errbuf); + hyper_error_free(hypererr); + } + return result; +} +#endif + +void Curl_connect_free(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + struct http_connect_state *s = conn->connect_state; + if(s) { + free(s); + conn->connect_state = NULL; } - if(cf->next) - cf->next->cft->do_close(cf->next, data); } +/* + * Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This + * function will issue the necessary commands to get a seamless tunnel through + * this proxy. After that, the socket can be used just as a normal socket. + */ -struct Curl_cftype Curl_cft_http_proxy = { - "HTTP-PROXY", - CF_TYPE_IP_CONNECT, - 0, - http_proxy_cf_destroy, - http_proxy_cf_connect, - http_proxy_cf_close, - Curl_cf_http_proxy_get_host, - Curl_cf_def_get_select_socks, - Curl_cf_def_data_pending, - Curl_cf_def_send, - Curl_cf_def_recv, - Curl_cf_def_cntrl, - Curl_cf_def_conn_is_alive, - Curl_cf_def_conn_keep_alive, - Curl_cf_def_query, -}; - -CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data) +CURLcode Curl_proxyCONNECT(struct Curl_easy *data, + int sockindex, + const char *hostname, + int remote_port) { - struct Curl_cfilter *cf; - struct cf_proxy_ctx *ctx = NULL; CURLcode result; - - (void)data; - ctx = calloc(1, sizeof(*ctx)); - if(!ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; + struct connectdata *conn = data->conn; + if(!conn->connect_state) { + result = connect_init(data, FALSE); + if(result) + return result; } - result = Curl_cf_create(&cf, &Curl_cft_http_proxy, ctx); - if(result) - goto out; - ctx = NULL; - Curl_conn_cf_insert_after(cf_at, cf); - -out: - free(ctx); + result = CONNECT(data, sockindex, hostname, remote_port); + + if(result || Curl_connect_complete(conn)) + Curl_connect_done(data); + return result; } -#endif /* ! CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */ +#else +void Curl_connect_free(struct Curl_easy *data) +{ + (void)data; +} + +#endif /* CURL_DISABLE_PROXY */ diff --git a/contrib/libs/curl/lib/http_proxy.h b/contrib/libs/curl/lib/http_proxy.h index a1a03720bd..1e650ee571 100644 --- a/contrib/libs/curl/lib/http_proxy.h +++ b/contrib/libs/curl/lib/http_proxy.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -25,28 +25,57 @@ ***************************************************************************/ #include "curl_setup.h" +#include "urldata.h" #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) - -#include "urldata.h" +/* ftp can use this as well */ +CURLcode Curl_proxyCONNECT(struct Curl_easy *data, + int tunnelsocket, + const char *hostname, int remote_port); /* Default proxy timeout in milliseconds */ #define PROXY_TIMEOUT (3600*1000) -void Curl_cf_http_proxy_get_host(struct Curl_cfilter *cf, - struct Curl_easy *data, - const char **phost, - const char **pdisplay_host, - int *pport); +CURLcode Curl_proxy_connect(struct Curl_easy *data, int sockindex); -CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data); +bool Curl_connect_complete(struct connectdata *conn); +bool Curl_connect_ongoing(struct connectdata *conn); +int Curl_connect_getsock(struct connectdata *conn); +void Curl_connect_done(struct Curl_easy *data); -extern struct Curl_cftype Curl_cft_http_proxy; +#else +#define Curl_proxyCONNECT(x,y,z,w) CURLE_NOT_BUILT_IN +#define Curl_proxy_connect(x,y) CURLE_OK +#define Curl_connect_complete(x) CURLE_OK +#define Curl_connect_ongoing(x) FALSE +#define Curl_connect_getsock(x) 0 +#define Curl_connect_done(x) +#endif -#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ +void Curl_connect_free(struct Curl_easy *data); -#define IS_HTTPS_PROXY(t) (((t) == CURLPROXY_HTTPS) || \ - ((t) == CURLPROXY_HTTPS2)) +/* struct for HTTP CONNECT state data */ +struct http_connect_state { + struct HTTP http_proxy; + struct HTTP *prot_save; + struct dynbuf rcvbuf; + struct dynbuf req; + size_t nsend; + size_t headerlines; + enum keeponval { + KEEPON_DONE, + KEEPON_CONNECT, + KEEPON_IGNORE + } keepon; + curl_off_t cl; /* size of content to read and ignore */ + enum { + TUNNEL_INIT, /* init/default/no tunnel state */ + TUNNEL_CONNECT, /* CONNECT has been sent off */ + TUNNEL_COMPLETE, /* CONNECT response received completely */ + TUNNEL_EXIT + } tunnel_state; + BIT(chunked_encoding); + BIT(close_connection); +}; #endif /* HEADER_CURL_HTTP_PROXY_H */ diff --git a/contrib/libs/curl/lib/idn.c b/contrib/libs/curl/lib/idn.c deleted file mode 100644 index ff69f3c7c7..0000000000 --- a/contrib/libs/curl/lib/idn.c +++ /dev/null @@ -1,202 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - - /* - * IDN conversions - */ - -#include "curl_setup.h" -#include "urldata.h" -#include "idn.h" -#include "sendf.h" -#include "curl_multibyte.h" -#include "warnless.h" - -#ifdef USE_LIBIDN2 -#error #include <idn2.h> - -#if defined(WIN32) && defined(UNICODE) -#define IDN2_LOOKUP(name, host, flags) \ - idn2_lookup_u8((const uint8_t *)name, (uint8_t **)host, flags) -#else -#define IDN2_LOOKUP(name, host, flags) \ - idn2_lookup_ul((const char *)name, (char **)host, flags) -#endif -#endif /* USE_LIBIDN2 */ - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#ifdef USE_WIN32_IDN -/* using Windows kernel32 and normaliz libraries. */ - -#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x600 -WINBASEAPI int WINAPI IdnToAscii(DWORD dwFlags, - const WCHAR *lpUnicodeCharStr, - int cchUnicodeChar, - WCHAR *lpASCIICharStr, - int cchASCIIChar); -WINBASEAPI int WINAPI IdnToUnicode(DWORD dwFlags, - const WCHAR *lpASCIICharStr, - int cchASCIIChar, - WCHAR *lpUnicodeCharStr, - int cchUnicodeChar); -#endif - -#define IDN_MAX_LENGTH 255 - -bool Curl_win32_idn_to_ascii(const char *in, char **out) -{ - bool success = FALSE; - - wchar_t *in_w = curlx_convert_UTF8_to_wchar(in); - if(in_w) { - wchar_t punycode[IDN_MAX_LENGTH]; - int chars = IdnToAscii(0, in_w, -1, punycode, IDN_MAX_LENGTH); - curlx_unicodefree(in_w); - if(chars) { - char *mstr = curlx_convert_wchar_to_UTF8(punycode); - if(mstr) { - *out = strdup(mstr); - curlx_unicodefree(mstr); - if(*out) - success = TRUE; - } - } - } - - return success; -} - -#endif /* USE_WIN32_IDN */ - -/* - * Helpers for IDNA conversions. - */ -bool Curl_is_ASCII_name(const char *hostname) -{ - /* get an UNSIGNED local version of the pointer */ - const unsigned char *ch = (const unsigned char *)hostname; - - if(!hostname) /* bad input, consider it ASCII! */ - return TRUE; - - while(*ch) { - if(*ch++ & 0x80) - return FALSE; - } - return TRUE; -} - -#ifdef USE_IDN -/* - * Curl_idn_decode() returns an allocated IDN decoded string if it was - * possible. NULL on error. - */ -static char *idn_decode(const char *input) -{ - char *decoded = NULL; -#ifdef USE_LIBIDN2 - if(idn2_check_version(IDN2_VERSION)) { - int flags = IDN2_NFC_INPUT -#if IDN2_VERSION_NUMBER >= 0x00140000 - /* IDN2_NFC_INPUT: Normalize input string using normalization form C. - IDN2_NONTRANSITIONAL: Perform Unicode TR46 non-transitional - processing. */ - | IDN2_NONTRANSITIONAL -#endif - ; - int rc = IDN2_LOOKUP(input, &decoded, flags); - if(rc != IDN2_OK) - /* fallback to TR46 Transitional mode for better IDNA2003 - compatibility */ - rc = IDN2_LOOKUP(input, &decoded, IDN2_TRANSITIONAL); - if(rc != IDN2_OK) - decoded = NULL; - } -#elif defined(USE_WIN32_IDN) - if(!Curl_win32_idn_to_ascii(input, &decoded)) - decoded = NULL; -#endif - return decoded; -} - -char *Curl_idn_decode(const char *input) -{ - char *d = idn_decode(input); -#ifdef USE_LIBIDN2 - if(d) { - char *c = strdup(d); - idn2_free(d); - d = c; - } -#endif - return d; -} - -/* - * Frees data allocated by idnconvert_hostname() - */ -void Curl_free_idnconverted_hostname(struct hostname *host) -{ - if(host->encalloc) { - /* must be freed with idn2_free() if allocated by libidn */ - Curl_idn_free(host->encalloc); - host->encalloc = NULL; - } -} - -#endif /* USE_IDN */ - -/* - * Perform any necessary IDN conversion of hostname - */ -CURLcode Curl_idnconvert_hostname(struct hostname *host) -{ - /* set the name we use to display the host name */ - host->dispname = host->name; - -#ifdef USE_IDN - /* Check name for non-ASCII and convert hostname if we can */ - if(!Curl_is_ASCII_name(host->name)) { - char *decoded = idn_decode(host->name); - if(decoded) { - if(!*decoded) { - /* zero length is a bad host name */ - Curl_idn_free(decoded); - return CURLE_URL_MALFORMAT; - } - /* successful */ - host->encalloc = decoded; - /* change the name pointer to point to the encoded hostname */ - host->name = host->encalloc; - } - else - return CURLE_URL_MALFORMAT; - } -#endif - return CURLE_OK; -} diff --git a/contrib/libs/curl/lib/idn.h b/contrib/libs/curl/lib/idn.h deleted file mode 100644 index 6c0bbb7109..0000000000 --- a/contrib/libs/curl/lib/idn.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef HEADER_CURL_IDN_H -#define HEADER_CURL_IDN_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#ifdef USE_WIN32_IDN -bool Curl_win32_idn_to_ascii(const char *in, char **out); -#endif /* USE_WIN32_IDN */ -bool Curl_is_ASCII_name(const char *hostname); -CURLcode Curl_idnconvert_hostname(struct hostname *host); -#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) -#define USE_IDN -void Curl_free_idnconverted_hostname(struct hostname *host); -char *Curl_idn_decode(const char *input); -#ifdef USE_LIBIDN2 -#define Curl_idn_free(x) idn2_free(x) -#else -#define Curl_idn_free(x) free(x) -#endif - -#else -#define Curl_free_idnconverted_hostname(x) -#define Curl_idn_decode(x) NULL -#endif -#endif /* HEADER_CURL_IDN_H */ diff --git a/contrib/libs/curl/lib/idn_win32.c b/contrib/libs/curl/lib/idn_win32.c new file mode 100644 index 0000000000..2433d927ee --- /dev/null +++ b/contrib/libs/curl/lib/idn_win32.c @@ -0,0 +1,121 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + + /* + * IDN conversions using Windows kernel32 and normaliz libraries. + */ + +#include "curl_setup.h" + +#ifdef USE_WIN32_IDN + +#include "curl_multibyte.h" +#include "curl_memory.h" +#include "warnless.h" + + /* The last #include file should be: */ +#include "memdebug.h" + +#ifdef WANT_IDN_PROTOTYPES +# if defined(_SAL_VERSION) +WINNORMALIZEAPI int WINAPI +IdnToAscii(_In_ DWORD dwFlags, + _In_reads_(cchUnicodeChar) LPCWSTR lpUnicodeCharStr, + _In_ int cchUnicodeChar, + _Out_writes_opt_(cchASCIIChar) LPWSTR lpASCIICharStr, + _In_ int cchASCIIChar); +WINNORMALIZEAPI int WINAPI +IdnToUnicode(_In_ DWORD dwFlags, + _In_reads_(cchASCIIChar) LPCWSTR lpASCIICharStr, + _In_ int cchASCIIChar, + _Out_writes_opt_(cchUnicodeChar) LPWSTR lpUnicodeCharStr, + _In_ int cchUnicodeChar); +# else +WINBASEAPI int WINAPI IdnToAscii(DWORD dwFlags, + const WCHAR *lpUnicodeCharStr, + int cchUnicodeChar, + WCHAR *lpASCIICharStr, + int cchASCIIChar); +WINBASEAPI int WINAPI IdnToUnicode(DWORD dwFlags, + const WCHAR *lpASCIICharStr, + int cchASCIIChar, + WCHAR *lpUnicodeCharStr, + int cchUnicodeChar); +# endif +#endif + +#define IDN_MAX_LENGTH 255 + +bool Curl_win32_idn_to_ascii(const char *in, char **out); +bool Curl_win32_ascii_to_idn(const char *in, char **out); + +bool Curl_win32_idn_to_ascii(const char *in, char **out) +{ + bool success = FALSE; + + wchar_t *in_w = curlx_convert_UTF8_to_wchar(in); + if(in_w) { + wchar_t punycode[IDN_MAX_LENGTH]; + int chars = IdnToAscii(0, in_w, -1, punycode, IDN_MAX_LENGTH); + curlx_unicodefree(in_w); + if(chars) { + char *mstr = curlx_convert_wchar_to_UTF8(punycode); + if(mstr) { + *out = strdup(mstr); + curlx_unicodefree(mstr); + if(*out) + success = TRUE; + } + } + } + + return success; +} + +bool Curl_win32_ascii_to_idn(const char *in, char **out) +{ + bool success = FALSE; + + wchar_t *in_w = curlx_convert_UTF8_to_wchar(in); + if(in_w) { + size_t in_len = wcslen(in_w) + 1; + wchar_t unicode[IDN_MAX_LENGTH]; + int chars = IdnToUnicode(0, in_w, curlx_uztosi(in_len), + unicode, IDN_MAX_LENGTH); + curlx_unicodefree(in_w); + if(chars) { + char *mstr = curlx_convert_wchar_to_UTF8(unicode); + if(mstr) { + *out = strdup(mstr); + curlx_unicodefree(mstr); + if(*out) + success = TRUE; + } + } + } + + return success; +} + +#endif /* USE_WIN32_IDN */ diff --git a/contrib/libs/curl/lib/if2ip.c b/contrib/libs/curl/lib/if2ip.c index 6bf0ce16f7..c29194878f 100644 --- a/contrib/libs/curl/lib/if2ip.c +++ b/contrib/libs/curl/lib/if2ip.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/if2ip.h b/contrib/libs/curl/lib/if2ip.h index 1f973505c0..5d15459e98 100644 --- a/contrib/libs/curl/lib/if2ip.h +++ b/contrib/libs/curl/lib/if2ip.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2020, 2022, 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 diff --git a/contrib/libs/curl/lib/imap.c b/contrib/libs/curl/lib/imap.c index 045fe24fd5..ffa08bf7a4 100644 --- a/contrib/libs/curl/lib/imap.c +++ b/contrib/libs/curl/lib/imap.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -56,6 +56,11 @@ #include <inet.h> #endif +#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) +#undef in_addr_t +#define in_addr_t unsigned long +#endif + #include <curl/curl.h> #include "urldata.h" #include "sendf.h" @@ -70,11 +75,11 @@ #include "strtoofft.h" #include "strcase.h" #include "vtls/vtls.h" -#include "cfilters.h" #include "connect.h" #include "select.h" #include "multiif.h" #include "url.h" +#include "strcase.h" #include "bufref.h" #include "curl_sasl.h" #include "warnless.h" @@ -385,11 +390,11 @@ static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out) /*********************************************************************** * - * imap_state() + * state() * * This is the ONLY way to change IMAP state! */ -static void imap_state(struct Curl_easy *data, imapstate newstate) +static void state(struct Curl_easy *data, imapstate newstate) { struct imap_conn *imapc = &data->conn->proto.imapc; #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) @@ -441,7 +446,7 @@ static CURLcode imap_perform_capability(struct Curl_easy *data, result = imap_sendf(data, "CAPABILITY"); if(!result) - imap_state(data, IMAP_CAPABILITY); + state(data, IMAP_CAPABILITY); return result; } @@ -458,7 +463,7 @@ static CURLcode imap_perform_starttls(struct Curl_easy *data) CURLcode result = imap_sendf(data, "STARTTLS"); if(!result) - imap_state(data, IMAP_STARTTLS); + state(data, IMAP_STARTTLS); return result; } @@ -474,27 +479,19 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data, { /* Start the SSL connection */ struct imap_conn *imapc = &conn->proto.imapc; - CURLcode result; - bool ssldone = FALSE; - - if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); - if(result) - goto out; - } + CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FALSE, + FIRSTSOCKET, &imapc->ssldone); - result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); if(!result) { - imapc->ssldone = ssldone; if(imapc->state != IMAP_UPGRADETLS) - imap_state(data, IMAP_UPGRADETLS); + state(data, IMAP_UPGRADETLS); if(imapc->ssldone) { imap_to_imaps(conn); result = imap_perform_capability(data, conn); } } -out: + return result; } @@ -514,7 +511,7 @@ static CURLcode imap_perform_login(struct Curl_easy *data, /* Check we have a username and password to authenticate with and end the connect phase if we don't */ if(!data->state.aptr.user) { - imap_state(data, IMAP_STOP); + state(data, IMAP_STOP); return result; } @@ -531,7 +528,7 @@ static CURLcode imap_perform_login(struct Curl_easy *data, free(passwd); if(!result) - imap_state(data, IMAP_LOGIN); + state(data, IMAP_LOGIN); return result; } @@ -615,7 +612,7 @@ static CURLcode imap_perform_authentication(struct Curl_easy *data, with and end the connect phase if we don't */ if(imapc->preauth || !Curl_sasl_can_authenticate(&imapc->sasl, data)) { - imap_state(data, IMAP_STOP); + state(data, IMAP_STOP); return result; } @@ -624,7 +621,7 @@ static CURLcode imap_perform_authentication(struct Curl_easy *data, if(!result) { if(progress == SASL_INPROGRESS) - imap_state(data, IMAP_AUTHENTICATE); + state(data, IMAP_AUTHENTICATE); else if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT)) /* Perform clear text authentication */ result = imap_perform_login(data, conn); @@ -667,7 +664,7 @@ static CURLcode imap_perform_list(struct Curl_easy *data) } if(!result) - imap_state(data, IMAP_LIST); + state(data, IMAP_LIST); return result; } @@ -707,7 +704,7 @@ static CURLcode imap_perform_select(struct Curl_easy *data) free(mailbox); if(!result) - imap_state(data, IMAP_SELECT); + state(data, IMAP_SELECT); return result; } @@ -749,7 +746,7 @@ static CURLcode imap_perform_fetch(struct Curl_easy *data) return CURLE_URL_MALFORMAT; } if(!result) - imap_state(data, IMAP_FETCH); + state(data, IMAP_FETCH); return result; } @@ -779,7 +776,7 @@ static CURLcode imap_perform_append(struct Curl_easy *data) /* Add external headers and mime version. */ curl_mime_headers(&data->set.mimepost, data->set.headers, 0); - result = Curl_mime_prepare_headers(data, &data->set.mimepost, NULL, + result = Curl_mime_prepare_headers(&data->set.mimepost, NULL, NULL, MIMESTRATEGY_MAIL); if(!result) @@ -820,7 +817,7 @@ static CURLcode imap_perform_append(struct Curl_easy *data) free(mailbox); if(!result) - imap_state(data, IMAP_APPEND); + state(data, IMAP_APPEND); return result; } @@ -846,7 +843,7 @@ static CURLcode imap_perform_search(struct Curl_easy *data) result = imap_sendf(data, "SEARCH %s", imap->query); if(!result) - imap_state(data, IMAP_SEARCH); + state(data, IMAP_SEARCH); return result; } @@ -863,7 +860,7 @@ static CURLcode imap_perform_logout(struct Curl_easy *data) CURLcode result = imap_sendf(data, "LOGOUT"); if(!result) - imap_state(data, IMAP_LOGOUT); + state(data, IMAP_LOGOUT); return result; } @@ -954,7 +951,7 @@ static CURLcode imap_state_capability_resp(struct Curl_easy *data, line += wordlen; } } - else if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) { + else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) { /* PREAUTH is not compatible with STARTTLS. */ if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) { /* Switch to TLS connection now */ @@ -1017,7 +1014,7 @@ static CURLcode imap_state_auth_resp(struct Curl_easy *data, if(!result) switch(progress) { case SASL_DONE: - imap_state(data, IMAP_STOP); /* Authenticated */ + state(data, IMAP_STOP); /* Authenticated */ break; case SASL_IDLE: /* No mechanism left after cancellation */ if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT)) @@ -1049,7 +1046,7 @@ static CURLcode imap_state_login_resp(struct Curl_easy *data, } else /* End of connect phase */ - imap_state(data, IMAP_STOP); + state(data, IMAP_STOP); return result; } @@ -1075,7 +1072,7 @@ static CURLcode imap_state_listsearch_resp(struct Curl_easy *data, result = CURLE_QUOTE_ERROR; else /* End of DO phase */ - imap_state(data, IMAP_STOP); + state(data, IMAP_STOP); return result; } @@ -1143,7 +1140,7 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data, if(imapcode != '*') { Curl_pgrsSetDownloadSize(data, -1); - imap_state(data, IMAP_STOP); + state(data, IMAP_STOP); return CURLE_REMOTE_FILE_NOT_FOUND; } @@ -1178,7 +1175,7 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data, if(!chunk) { /* no size, we're done with the data */ - imap_state(data, IMAP_STOP); + state(data, IMAP_STOP); return CURLE_OK; } result = Curl_client_write(data, CLIENTWRITE_BODY, pp->cache, chunk); @@ -1224,7 +1221,7 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data, } /* End of DO phase */ - imap_state(data, IMAP_STOP); + state(data, IMAP_STOP); return result; } @@ -1242,7 +1239,7 @@ static CURLcode imap_state_fetch_final_resp(struct Curl_easy *data, result = CURLE_WEIRD_SERVER_REPLY; else /* End of DONE phase */ - imap_state(data, IMAP_STOP); + state(data, IMAP_STOP); return result; } @@ -1265,7 +1262,7 @@ static CURLcode imap_state_append_resp(struct Curl_easy *data, int imapcode, Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); /* End of DO phase */ - imap_state(data, IMAP_STOP); + state(data, IMAP_STOP); } return result; @@ -1284,7 +1281,7 @@ static CURLcode imap_state_append_final_resp(struct Curl_easy *data, result = CURLE_UPLOAD_FAILED; else /* End of DONE phase */ - imap_state(data, IMAP_STOP); + state(data, IMAP_STOP); return result; } @@ -1372,7 +1369,7 @@ static CURLcode imap_statemachine(struct Curl_easy *data, /* fallthrough, just stop! */ default: /* internal error */ - imap_state(data, IMAP_STOP); + state(data, IMAP_STOP); break; } } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp)); @@ -1388,10 +1385,9 @@ static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done) struct imap_conn *imapc = &conn->proto.imapc; if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) { - bool ssldone = FALSE; - result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); - imapc->ssldone = ssldone; - if(result || !ssldone) + result = Curl_ssl_connect_nonblocking(data, conn, FALSE, + FIRSTSOCKET, &imapc->ssldone); + if(result || !imapc->ssldone) return result; } @@ -1475,7 +1471,7 @@ static CURLcode imap_connect(struct Curl_easy *data, bool *done) return result; /* Start off waiting for the server greeting response */ - imap_state(data, IMAP_SERVERGREET); + state(data, IMAP_SERVERGREET); /* Start off with an response id of '*' */ strcpy(imapc->resptag, "*"); @@ -1511,17 +1507,17 @@ static CURLcode imap_done(struct Curl_easy *data, CURLcode status, result = status; /* use the already set error code */ } else if(!data->set.connect_only && !imap->custom && - (imap->uid || imap->mindex || data->state.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->state.upload && data->set.mimepost.kind == MIMEKIND_NONE) - imap_state(data, IMAP_FETCH_FINAL); + if(!data->set.upload && data->set.mimepost.kind == MIMEKIND_NONE) + state(data, IMAP_FETCH_FINAL); else { /* End the APPEND command first by sending an empty line */ result = Curl_pp_sendf(data, &conn->proto.imapc.pp, "%s", ""); if(!result) - imap_state(data, IMAP_APPEND_FINAL); + state(data, IMAP_APPEND_FINAL); } /* Run the state-machine */ @@ -1565,7 +1561,7 @@ static CURLcode imap_perform(struct Curl_easy *data, bool *connected, DEBUGF(infof(data, "DO phase starts")); - if(data->req.no_body) { + if(data->set.opt_no_body) { /* Requested no body means no transfer */ imap->transfer = PPTRANSFER_INFO; } @@ -1581,7 +1577,7 @@ static CURLcode imap_perform(struct Curl_easy *data, bool *connected, selected = TRUE; /* Start the first command in the DO phase */ - if(data->state.upload || data->set.mimepost.kind != MIMEKIND_NONE) + if(data->set.upload || data->set.mimepost.kind != MIMEKIND_NONE) /* APPEND can be executed directly */ result = imap_perform_append(data); else if(imap->custom && (selected || !imap->mailbox)) @@ -1607,7 +1603,7 @@ static CURLcode imap_perform(struct Curl_easy *data, bool *connected, /* Run the state-machine */ result = imap_multi_statemach(data, dophase_done); - *connected = Curl_conn_is_connected(conn, FIRSTSOCKET); + *connected = conn->bits.tcpconnect[FIRSTSOCKET]; if(*dophase_done) DEBUGF(infof(data, "DO phase is complete")); @@ -1777,8 +1773,8 @@ static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...) /* Calculate the tag based on the connection ID and command ID */ msnprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d", - 'A' + curlx_sltosi((long)(data->conn->connection_id % 26)), - ++imapc->cmdid); + 'A' + curlx_sltosi(data->conn->connection_id % 26), + (++imapc->cmdid)%1000); /* start with a blank buffer */ Curl_dyn_reset(&imapc->dyn); @@ -1925,53 +1921,39 @@ static CURLcode imap_parse_url_options(struct connectdata *conn) CURLcode result = CURLE_OK; struct imap_conn *imapc = &conn->proto.imapc; const char *ptr = conn->options; - bool prefer_login = false; while(!result && ptr && *ptr) { const char *key = ptr; const char *value; while(*ptr && *ptr != '=') - ptr++; + ptr++; value = ptr + 1; while(*ptr && *ptr != ';') ptr++; - if(strncasecompare(key, "AUTH=+LOGIN", 11)) { - /* User prefers plaintext LOGIN over any SASL, including SASL LOGIN */ - prefer_login = true; - imapc->sasl.prefmech = SASL_AUTH_NONE; - } - else if(strncasecompare(key, "AUTH=", 5)) { - prefer_login = false; + if(strncasecompare(key, "AUTH=", 5)) result = Curl_sasl_parse_url_auth_option(&imapc->sasl, value, ptr - value); - } - else { - prefer_login = false; + else result = CURLE_URL_MALFORMAT; - } if(*ptr == ';') ptr++; } - if(prefer_login) - imapc->preftype = IMAP_TYPE_CLEARTEXT; - else { - switch(imapc->sasl.prefmech) { - case SASL_AUTH_NONE: - imapc->preftype = IMAP_TYPE_NONE; - break; - case SASL_AUTH_DEFAULT: - imapc->preftype = IMAP_TYPE_ANY; - break; - default: - imapc->preftype = IMAP_TYPE_SASL; - break; - } + switch(imapc->sasl.prefmech) { + case SASL_AUTH_NONE: + imapc->preftype = IMAP_TYPE_NONE; + break; + case SASL_AUTH_DEFAULT: + imapc->preftype = IMAP_TYPE_ANY; + break; + default: + imapc->preftype = IMAP_TYPE_SASL; + break; } return result; diff --git a/contrib/libs/curl/lib/imap.h b/contrib/libs/curl/lib/imap.h index 784ee97e55..43cc1e98fc 100644 --- a/contrib/libs/curl/lib/imap.h +++ b/contrib/libs/curl/lib/imap.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2009 - 2022, 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 @@ -72,19 +72,19 @@ struct IMAP { struct */ struct imap_conn { struct pingpong pp; + imapstate state; /* Always use imap.c:state() to change state! */ + bool ssldone; /* Is connect() over SSL done? */ + bool preauth; /* Is this connection PREAUTH? */ struct SASL sasl; /* SASL-related parameters */ - struct dynbuf dyn; /* for the IMAP commands */ + unsigned int preftype; /* Preferred authentication type */ + unsigned int cmdid; /* Last used command ID */ + char resptag[5]; /* Response tag to wait for */ + bool tls_supported; /* StartTLS capability supported by server */ + bool login_disabled; /* LOGIN command disabled by server */ + bool ir_supported; /* Initial response supported by server */ char *mailbox; /* The last selected mailbox */ char *mailbox_uidvalidity; /* UIDVALIDITY parsed from select response */ - imapstate state; /* Always use imap.c:state() to change state! */ - char resptag[5]; /* Response tag to wait for */ - unsigned char preftype; /* Preferred authentication type */ - unsigned char cmdid; /* Last used command ID */ - BIT(ssldone); /* Is connect() over SSL done? */ - BIT(preauth); /* Is this connection PREAUTH? */ - BIT(tls_supported); /* StartTLS capability supported by server */ - BIT(login_disabled); /* LOGIN command disabled by server */ - BIT(ir_supported); /* Initial response supported by server */ + struct dynbuf dyn; /* for the IMAP commands */ }; extern const struct Curl_handler Curl_handler_imap; @@ -96,6 +96,6 @@ extern const struct Curl_handler Curl_handler_imaps; /* Authentication type values */ #define IMAP_TYPE_NONE 0 -#define IMAP_TYPE_ANY (IMAP_TYPE_CLEARTEXT|IMAP_TYPE_SASL) +#define IMAP_TYPE_ANY ~0U #endif /* HEADER_CURL_IMAP_H */ diff --git a/contrib/libs/curl/lib/inet_ntop.c b/contrib/libs/curl/lib/inet_ntop.c index fa9077376b..024f8da36d 100644 --- a/contrib/libs/curl/lib/inet_ntop.c +++ b/contrib/libs/curl/lib/inet_ntop.c @@ -42,15 +42,6 @@ #define INT16SZ 2 /* - * If ENABLE_IPV6 is disabled, we still want to parse IPv6 addresses, so make - * sure we have _some_ value for AF_INET6 without polluting our fake value - * everywhere. - */ -#if !defined(ENABLE_IPV6) && !defined(AF_INET6) -#define AF_INET6 (AF_INET + 1) -#endif - -/* * Format an IPv4 address, more or less like inet_ntop(). * * Returns `dst' (as a const) @@ -81,6 +72,7 @@ static char *inet_ntop4 (const unsigned char *src, char *dst, size_t size) return dst; } +#ifdef ENABLE_IPV6 /* * Convert IPv6 binary address into presentation (printable) format. */ @@ -164,7 +156,7 @@ static char *inet_ntop6 (const unsigned char *src, char *dst, size_t size) /* Was it a trailing run of 0x00's? */ if(best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) - *tp++ = ':'; + *tp++ = ':'; *tp++ = '\0'; /* Check for overflow, copy, and we're done. @@ -176,6 +168,7 @@ static char *inet_ntop6 (const unsigned char *src, char *dst, size_t size) strcpy(dst, tmp); return dst; } +#endif /* ENABLE_IPV6 */ /* * Convert a network format address to presentation format. @@ -194,8 +187,10 @@ char *Curl_inet_ntop(int af, const void *src, char *buf, size_t size) switch(af) { case AF_INET: return inet_ntop4((const unsigned char *)src, buf, size); +#ifdef ENABLE_IPV6 case AF_INET6: return inet_ntop6((const unsigned char *)src, buf, size); +#endif default: errno = EAFNOSUPPORT; return NULL; diff --git a/contrib/libs/curl/lib/inet_ntop.h b/contrib/libs/curl/lib/inet_ntop.h index 7c3ead4341..18fbd8ba3a 100644 --- a/contrib/libs/curl/lib/inet_ntop.h +++ b/contrib/libs/curl/lib/inet_ntop.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/inet_pton.c b/contrib/libs/curl/lib/inet_pton.c index 7d3c698795..47fb77834d 100644 --- a/contrib/libs/curl/lib/inet_pton.c +++ b/contrib/libs/curl/lib/inet_pton.c @@ -1,6 +1,6 @@ /* This is from the BIND 4.9.4 release, modified to compile by itself */ -/* Copyright (c) Internet Software Consortium. +/* Copyright (c) 2003 - 2022 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -39,21 +39,14 @@ #define INT16SZ 2 /* - * If ENABLE_IPV6 is disabled, we still want to parse IPv6 addresses, so make - * sure we have _some_ value for AF_INET6 without polluting our fake value - * everywhere. - */ -#if !defined(ENABLE_IPV6) && !defined(AF_INET6) -#define AF_INET6 (AF_INET + 1) -#endif - -/* * WARNING: Don't even consider trying to compile this on a system where * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. */ static int inet_pton4(const char *src, unsigned char *dst); +#ifdef ENABLE_IPV6 static int inet_pton6(const char *src, unsigned char *dst); +#endif /* int * inet_pton(af, src, dst) @@ -77,8 +70,10 @@ Curl_inet_pton(int af, const char *src, void *dst) switch(af) { case AF_INET: return (inet_pton4(src, (unsigned char *)dst)); +#ifdef ENABLE_IPV6 case AF_INET6: return (inet_pton6(src, (unsigned char *)dst)); +#endif default: errno = EAFNOSUPPORT; return (-1); @@ -140,6 +135,7 @@ inet_pton4(const char *src, unsigned char *dst) return (1); } +#ifdef ENABLE_IPV6 /* int * inet_pton6(src, dst) * convert presentation level address to network order binary form. @@ -238,5 +234,6 @@ inet_pton6(const char *src, unsigned char *dst) memcpy(dst, tmp, IN6ADDRSZ); return (1); } +#endif /* ENABLE_IPV6 */ #endif /* HAVE_INET_PTON */ diff --git a/contrib/libs/curl/lib/inet_pton.h b/contrib/libs/curl/lib/inet_pton.h index 82fde7e2eb..92ae93ea1f 100644 --- a/contrib/libs/curl/lib/inet_pton.h +++ b/contrib/libs/curl/lib/inet_pton.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/krb5.c b/contrib/libs/curl/lib/krb5.c index a0a64706c9..1e61f29ccb 100644 --- a/contrib/libs/curl/lib/krb5.c +++ b/contrib/libs/curl/lib/krb5.c @@ -2,7 +2,7 @@ * * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). - * Copyright (C) Daniel Stenberg + * Copyright (c) 2004 - 2022 Daniel Stenberg * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -41,13 +41,8 @@ #ifdef HAVE_NETDB_H #include <netdb.h> #endif -#ifdef HAVE_ARPA_INET_H -#include <arpa/inet.h> -#endif #include "urldata.h" -#include "cfilters.h" -#include "cf-socket.h" #include "curl_base64.h" #include "ftp.h" #error #include "curl_gssapi.h" @@ -209,8 +204,8 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn) gss_ctx_id_t *context = app_data; struct gss_channel_bindings_struct chan; size_t base64_sz = 0; - struct sockaddr_in *remote_addr = - (struct sockaddr_in *)(void *)&conn->remote_addr->sa_addr; + struct sockaddr_in **remote_addr = + (struct sockaddr_in **)&conn->ip_addr->ai_addr; char *stringp; if(getsockname(conn->sock[FIRSTSOCKET], @@ -222,7 +217,7 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn) chan.initiator_address.value = &conn->local_addr.sin_addr.s_addr; chan.acceptor_addrtype = GSS_C_AF_INET; chan.acceptor_address.length = l - 4; - chan.acceptor_address.value = &remote_addr->sin_addr.s_addr; + chan.acceptor_address.value = &(*remote_addr)->sin_addr.s_addr; chan.application_data.length = 0; chan.application_data.value = NULL; @@ -261,7 +256,7 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn) } /* We pass NULL as |output_name_type| to avoid a leak. */ gss_display_name(&min, gssname, &output_buffer, NULL); - infof(data, "Trying against %s", (char *)output_buffer.value); + infof(data, "Trying against %s", output_buffer.value); gssresp = GSS_C_NO_BUFFER; *context = GSS_C_NO_CONTEXT; @@ -456,15 +451,15 @@ static int ftp_send_command(struct Curl_easy *data, const char *message, ...) /* Read |len| from the socket |fd| and store it in |to|. Return a CURLcode saying whether an error occurred or CURLE_OK if |len| was read. */ static CURLcode -socket_read(struct Curl_easy *data, int sockindex, void *to, size_t len) +socket_read(curl_socket_t fd, void *to, size_t len) { char *to_p = to; CURLcode result; ssize_t nread = 0; while(len > 0) { - nread = Curl_conn_recv(data, sockindex, to_p, len, &result); - if(nread > 0) { + result = Curl_read_plain(fd, to_p, len, &nread); + if(!result) { len -= nread; to_p += nread; } @@ -482,7 +477,7 @@ socket_read(struct Curl_easy *data, int sockindex, void *to, size_t len) CURLcode saying whether an error occurred or CURLE_OK if |len| was written. */ static CURLcode -socket_write(struct Curl_easy *data, int sockindex, const void *to, +socket_write(struct Curl_easy *data, curl_socket_t fd, const void *to, size_t len) { const char *to_p = to; @@ -490,8 +485,8 @@ socket_write(struct Curl_easy *data, int sockindex, const void *to, ssize_t written; while(len > 0) { - written = Curl_conn_send(data, sockindex, to_p, len, &result); - if(written > 0) { + result = Curl_write_plain(data, fd, to_p, len, &written); + if(!result) { len -= written; to_p += written; } @@ -504,15 +499,15 @@ socket_write(struct Curl_easy *data, int sockindex, const void *to, return CURLE_OK; } -static CURLcode read_data(struct Curl_easy *data, int sockindex, +static CURLcode read_data(struct connectdata *conn, + curl_socket_t fd, struct krb5buffer *buf) { - struct connectdata *conn = data->conn; int len; CURLcode result; int nread; - result = socket_read(data, sockindex, &len, sizeof(len)); + result = socket_read(fd, &len, sizeof(len)); if(result) return result; @@ -527,7 +522,7 @@ static CURLcode read_data(struct Curl_easy *data, int sockindex, if(!len || !buf->data) return CURLE_OUT_OF_MEMORY; - result = socket_read(data, sockindex, buf->data, len); + result = socket_read(fd, buf->data, len); if(result) return result; nread = conn->mech->decode(conn->app_data, buf->data, len, @@ -556,12 +551,13 @@ static ssize_t sec_recv(struct Curl_easy *data, int sockindex, size_t bytes_read; size_t total_read = 0; struct connectdata *conn = data->conn; + curl_socket_t fd = conn->sock[sockindex]; *err = CURLE_OK; /* Handle clear text response. */ if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR) - return Curl_conn_recv(data, sockindex, buffer, len, err); + return sread(fd, buffer, len); if(conn->in_buffer.eof_flag) { conn->in_buffer.eof_flag = 0; @@ -574,7 +570,7 @@ static ssize_t sec_recv(struct Curl_easy *data, int sockindex, buffer += bytes_read; while(len > 0) { - if(read_data(data, sockindex, &conn->in_buffer)) + if(read_data(conn, fd, &conn->in_buffer)) return -1; if(conn->in_buffer.size == 0) { if(bytes_read > 0) @@ -721,7 +717,8 @@ int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn, return 0; if(buf[3] != '-') - ret_code = atoi(buf); + /* safe to ignore return code */ + (void)sscanf(buf, "%d", &ret_code); if(buf[decoded_len - 1] == '\n') buf[decoded_len - 1] = '\0'; @@ -764,9 +761,8 @@ static int sec_set_protection_level(struct Curl_easy *data) pbsz = strstr(data->state.buffer, "PBSZ="); if(pbsz) { - /* stick to default value if the check fails */ - if(!strncmp(pbsz, "PBSZ=", 5) && ISDIGIT(pbsz[5])) - buffer_size = atoi(&pbsz[5]); + /* ignore return code, use default value if it fails */ + (void)sscanf(pbsz, "PBSZ=%u", &buffer_size); if(buffer_size < conn->buffer_size) conn->buffer_size = buffer_size; } diff --git a/contrib/libs/curl/lib/ldap.c b/contrib/libs/curl/lib/ldap.c index c98be53609..3b9c595051 100644 --- a/contrib/libs/curl/lib/ldap.c +++ b/contrib/libs/curl/lib/ldap.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -50,14 +50,6 @@ #endif #ifdef USE_WIN32_LDAP /* Use Windows LDAP implementation. */ -# ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable: 4201) -# endif -# error #include <subauth.h> /* for [P]UNICODE_STRING */ -# ifdef _MSC_VER -# pragma warning(pop) -# endif # include <winldap.h> # ifndef LDAP_VENDOR_NAME # error Your Platform SDK is NOT sufficient for LDAP support! \ @@ -148,14 +140,6 @@ static void _ldap_free_urldesc(LDAPURLDesc *ludp); #define ldap_err2string ldap_err2stringA #endif -#if defined(USE_WIN32_LDAP) && defined(_MSC_VER) && (_MSC_VER <= 1600) -/* Workaround for warning: - 'type cast' : conversion from 'int' to 'void *' of greater size */ -#undef LDAP_OPT_ON -#undef LDAP_OPT_OFF -#define LDAP_OPT_ON ((void *)(size_t)1) -#define LDAP_OPT_OFF ((void *)(size_t)0) -#endif static CURLcode ldap_do(struct Curl_easy *data, bool *done); @@ -581,7 +565,8 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) goto quit; } - result = Curl_client_write(data, CLIENTWRITE_BODY, name, name_len); + result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) name, + name_len); if(result) { FREE_ON_WINLDAP(name); ldap_memfree(dn); @@ -637,7 +622,8 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) goto quit; } - result = Curl_client_write(data, CLIENTWRITE_BODY, attr, attr_len); + result = Curl_client_write(data, CLIENTWRITE_BODY, + (char *) attr, attr_len); if(result) { ldap_value_free_len(vals); FREE_ON_WINLDAP(attr); @@ -662,7 +648,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) dlsize += attr_len + 3; if((attr_len > 7) && - (strcmp(";binary", attr + (attr_len - 7)) == 0)) { + (strcmp(";binary", (char *) attr + (attr_len - 7)) == 0)) { /* Binary attribute, encode to base64. */ result = Curl_base64_encode(vals[i]->bv_val, vals[i]->bv_len, &val_b64, &val_b64_sz); @@ -739,7 +725,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) } if(ber) - ber_free(ber, 0); + ber_free(ber, 0); } quit: @@ -1077,7 +1063,7 @@ static int _ldap_url_parse(struct Curl_easy *data, *ludpp = NULL; if(!ludp) - return LDAP_NO_MEMORY; + return LDAP_NO_MEMORY; rc = _ldap_url_parse2(data, conn, ludp); if(rc != LDAP_SUCCESS) { diff --git a/contrib/libs/curl/lib/llist.c b/contrib/libs/curl/lib/llist.c index 5b6b0336da..fa2d366cb4 100644 --- a/contrib/libs/curl/lib/llist.c +++ b/contrib/libs/curl/lib/llist.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/llist.h b/contrib/libs/curl/lib/llist.h index 320580e33c..2fcb91ca50 100644 --- a/contrib/libs/curl/lib/llist.h +++ b/contrib/libs/curl/lib/llist.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/md4.c b/contrib/libs/curl/lib/md4.c index d09e9f0866..c03af6fe6f 100644 --- a/contrib/libs/curl/lib/md4.c +++ b/contrib/libs/curl/lib/md4.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,13 +24,12 @@ #include "curl_setup.h" -#if defined(USE_CURL_NTLM_CORE) - -#include <string.h> +#if !defined(CURL_DISABLE_CRYPTO_AUTH) #include "curl_md4.h" #include "warnless.h" + #ifdef USE_OPENSSL #include <openssl/opensslconf.h> #if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) && \ @@ -54,41 +53,20 @@ #else #error #include <mbedtls/config.h> #endif + #if(MBEDTLS_VERSION_NUMBER >= 0x02070000) #define HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS #endif #endif /* USE_MBEDTLS */ #if defined(USE_GNUTLS) + #include <nettle/md4.h> -/* When OpenSSL or wolfSSL is available, we use their MD4 functions. */ -#elif defined(USE_WOLFSSL) && !defined(WOLFSSL_NO_MD4) -#error #include <wolfssl/openssl/md4.h> -#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4) -#include <openssl/md4.h> -#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ - (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040) && \ - defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \ - (__MAC_OS_X_VERSION_MIN_REQUIRED < 101500)) || \ - (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ - (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000) && \ - defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && \ - (__IPHONE_OS_VERSION_MIN_REQUIRED < 130000)) -#define AN_APPLE_OS -#include <CommonCrypto/CommonDigest.h> -#elif defined(USE_WIN32_CRYPTO) -#include <wincrypt.h> -#elif(defined(USE_MBEDTLS) && defined(MBEDTLS_MD4_C)) -#error #include <mbedtls/md4.h> -#endif -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" #include "curl_memory.h" -#include "memdebug.h" - -#if defined(USE_GNUTLS) +/* The last #include file should be: */ +#include "memdebug.h" typedef struct md4_ctx MD4_CTX; @@ -107,11 +85,27 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) md4_digest(ctx, MD4_DIGEST_SIZE, result); } +/* When OpenSSL or wolfSSL is available, we use their MD4 functions. */ #elif defined(USE_WOLFSSL) && !defined(WOLFSSL_NO_MD4) +#error #include <wolfssl/openssl/md4.h> #elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4) +#include <openssl/md4.h> + +#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ + (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040) && \ + defined(__MAC_OS_X_VERSION_MIN_ALLOWED) && \ + (__MAC_OS_X_VERSION_MIN_ALLOWED < 101500)) || \ + (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ + (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000)) + +#include <CommonCrypto/CommonDigest.h> + +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" -#elif defined(AN_APPLE_OS) typedef CC_MD4_CTX MD4_CTX; static void MD4_Init(MD4_CTX *ctx) @@ -131,6 +125,13 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) #elif defined(USE_WIN32_CRYPTO) +#include <wincrypt.h> + +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + struct md4_ctx { HCRYPTPROV hCryptProv; HCRYPTHASH hHash; @@ -170,6 +171,13 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) #elif(defined(USE_MBEDTLS) && defined(MBEDTLS_MD4_C)) +#error #include <mbedtls/md4.h> + +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + struct md4_ctx { void *data; unsigned long size; @@ -247,6 +255,9 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) * compile-time configuration. */ + +#include <string.h> + /* Any 32-bit or wider unsigned integer data type will do */ typedef unsigned int MD4_u32plus; @@ -506,4 +517,4 @@ void Curl_md4it(unsigned char *output, const unsigned char *input, MD4_Final(output, &ctx); } -#endif /* USE_CURL_NTLM_CORE */ +#endif /* CURL_DISABLE_CRYPTO_AUTH */ diff --git a/contrib/libs/curl/lib/md5.c b/contrib/libs/curl/lib/md5.c index 5ae368ecb3..f3231b500f 100644 --- a/contrib/libs/curl/lib/md5.c +++ b/contrib/libs/curl/lib/md5.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -26,7 +26,6 @@ #ifndef CURL_DISABLE_CRYPTO_AUTH -#include <string.h> #include <curl/curl.h> #include "curl_md5.h" @@ -57,34 +56,12 @@ #endif #if defined(USE_GNUTLS) -#include <nettle/md5.h> -#elif defined(USE_OPENSSL_MD5) -#include <openssl/md5.h> -#elif defined(USE_WOLFSSL_MD5) -#error #include <wolfssl/openssl/md5.h> -#elif defined(USE_MBEDTLS) -#error #include <mbedtls/md5.h> -#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ - (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040) && \ - defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \ - (__MAC_OS_X_VERSION_MIN_REQUIRED < 101500)) || \ - (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ - (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000) && \ - defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && \ - (__IPHONE_OS_VERSION_MIN_REQUIRED < 130000)) -#define AN_APPLE_OS -#include <CommonCrypto/CommonDigest.h> -#elif defined(USE_WIN32_CRYPTO) -#include <wincrypt.h> -#endif -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" +#include <nettle/md5.h> #include "curl_memory.h" +/* The last #include file should be: */ #include "memdebug.h" -#if defined(USE_GNUTLS) - typedef struct md5_ctx my_md5_ctx; static CURLcode my_md5_init(my_md5_ctx *ctx) @@ -107,6 +84,17 @@ static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) #elif defined(USE_OPENSSL_MD5) || defined(USE_WOLFSSL_MD5) +/* When OpenSSL or wolfSSL is available, we use their MD5 functions. */ +#if defined(USE_OPENSSL_MD5) +#include <openssl/md5.h> +#elif defined(USE_WOLFSSL_MD5) +#error #include <wolfssl/openssl/md5.h> +#endif + +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + typedef MD5_CTX my_md5_ctx; static CURLcode my_md5_init(my_md5_ctx *ctx) @@ -131,6 +119,13 @@ static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) #elif defined(USE_MBEDTLS) +#error #include <mbedtls/md5.h> + +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + typedef mbedtls_md5_context my_md5_ctx; static CURLcode my_md5_init(my_md5_ctx *ctx) @@ -167,7 +162,12 @@ static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) #endif } -#elif defined(AN_APPLE_OS) +#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ + (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040) && \ + defined(__MAC_OS_X_VERSION_MIN_ALLOWED) && \ + (__MAC_OS_X_VERSION_MIN_ALLOWED < 101500)) || \ + (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ + (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000)) /* For Apple operating systems: CommonCrypto has the functions we need. These functions are available on Tiger and later, as well as iOS 2.0 @@ -175,7 +175,11 @@ static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) Declaring the functions as static like this seems to be a bit more reliable than defining COMMON_DIGEST_FOR_OPENSSL on older cats. */ +# include <CommonCrypto/CommonDigest.h> # define my_md5_ctx CC_MD5_CTX +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" static CURLcode my_md5_init(my_md5_ctx *ctx) { @@ -199,6 +203,11 @@ static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) #elif defined(USE_WIN32_CRYPTO) +#include <wincrypt.h> +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + struct md5_ctx { HCRYPTPROV hCryptProv; HCRYPTHASH hHash; @@ -279,6 +288,12 @@ static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) * compile-time configuration. */ +#include <string.h> + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + /* Any 32-bit or wider unsigned integer data type will do */ typedef unsigned int MD5_u32plus; diff --git a/contrib/libs/curl/lib/memdebug.c b/contrib/libs/curl/lib/memdebug.c index d6952a07a1..15fb491559 100644 --- a/contrib/libs/curl/lib/memdebug.c +++ b/contrib/libs/curl/lib/memdebug.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/memdebug.h b/contrib/libs/curl/lib/memdebug.h index c9eb5dc37c..7fc90e83a0 100644 --- a/contrib/libs/curl/lib/memdebug.h +++ b/contrib/libs/curl/lib/memdebug.h @@ -8,7 +8,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/mime.c b/contrib/libs/curl/lib/mime.c index 0a57e1e8a0..042141fc80 100644 --- a/contrib/libs/curl/lib/mime.c +++ b/contrib/libs/curl/lib/mime.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -84,7 +84,7 @@ static const struct mime_encoder encoders[] = { }; /* Base64 encoding table */ -static const char base64enc[] = +static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /* Quoted-printable character class table. @@ -469,10 +469,10 @@ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, i = st->buf[st->bufbeg++] & 0xFF; i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF); i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF); - *ptr++ = base64enc[(i >> 18) & 0x3F]; - *ptr++ = base64enc[(i >> 12) & 0x3F]; - *ptr++ = base64enc[(i >> 6) & 0x3F]; - *ptr++ = base64enc[i & 0x3F]; + *ptr++ = base64[(i >> 18) & 0x3F]; + *ptr++ = base64[(i >> 12) & 0x3F]; + *ptr++ = base64[(i >> 6) & 0x3F]; + *ptr++ = base64[i & 0x3F]; cursize += 4; st->pos += 4; size -= 4; @@ -496,10 +496,10 @@ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, i = (st->buf[st->bufbeg + 1] & 0xFF) << 8; i |= (st->buf[st->bufbeg] & 0xFF) << 16; - ptr[0] = base64enc[(i >> 18) & 0x3F]; - ptr[1] = base64enc[(i >> 12) & 0x3F]; + ptr[0] = base64[(i >> 18) & 0x3F]; + ptr[1] = base64[(i >> 12) & 0x3F]; if(++st->bufbeg != st->bufend) { - ptr[2] = base64enc[(i >> 6) & 0x3F]; + ptr[2] = base64[(i >> 6) & 0x3F]; st->bufbeg++; } cursize += 4; @@ -750,6 +750,7 @@ static void mime_file_free(void *ptr) part->fp = NULL; } Curl_safefree(part->data); + part->data = NULL; } @@ -1107,7 +1108,7 @@ static int mime_subparts_seek(void *instream, curl_off_t offset, int whence) return CURL_SEEKFUNC_CANTSEEK; /* Only support full rewind. */ if(mime->state.state == MIMESTATE_BEGIN) - return CURL_SEEKFUNC_OK; /* Already rewound. */ + return CURL_SEEKFUNC_OK; /* Already rewound. */ for(part = mime->firstpart; part; part = part->nextpart) { int res = mime_part_rewind(part); @@ -1174,7 +1175,7 @@ void Curl_mime_cleanpart(curl_mimepart *part) Curl_safefree(part->mimetype); Curl_safefree(part->name); Curl_safefree(part->filename); - Curl_mime_initpart(part); + Curl_mime_initpart(part, part->easy); } /* Recursively delete a mime handle and its parts. */ @@ -1194,8 +1195,7 @@ void curl_mime_free(curl_mime *mime) } } -CURLcode Curl_mime_duppart(struct Curl_easy *data, - curl_mimepart *dst, const curl_mimepart *src) +CURLcode Curl_mime_duppart(curl_mimepart *dst, const curl_mimepart *src) { curl_mime *mime; curl_mimepart *d; @@ -1224,13 +1224,13 @@ CURLcode Curl_mime_duppart(struct Curl_easy *data, case MIMEKIND_MULTIPART: /* No one knows about the cloned subparts, thus always attach ownership to the part. */ - mime = curl_mime_init(data); + mime = curl_mime_init(dst->easy); res = mime? curl_mime_subparts(dst, mime): CURLE_OUT_OF_MEMORY; /* Duplicate subparts. */ for(s = ((curl_mime *) src->arg)->firstpart; !res && s; s = s->nextpart) { d = curl_mime_addpart(mime); - res = d? Curl_mime_duppart(data, d, s): CURLE_OUT_OF_MEMORY; + res = d? Curl_mime_duppart(d, s): CURLE_OUT_OF_MEMORY; } break; default: /* Invalid kind: should not occur. */ @@ -1282,6 +1282,7 @@ curl_mime *curl_mime_init(struct Curl_easy *easy) mime = (curl_mime *) malloc(sizeof(*mime)); if(mime) { + mime->easy = easy; mime->parent = NULL; mime->firstpart = NULL; mime->lastpart = NULL; @@ -1301,9 +1302,10 @@ curl_mime *curl_mime_init(struct Curl_easy *easy) } /* Initialize a mime part. */ -void Curl_mime_initpart(curl_mimepart *part) +void Curl_mime_initpart(curl_mimepart *part, struct Curl_easy *easy) { memset((char *) part, 0, sizeof(*part)); + part->easy = easy; part->lastreadstatus = 1; /* Successful read status. */ mimesetstate(&part->state, MIMESTATE_BEGIN, NULL); } @@ -1319,7 +1321,7 @@ curl_mimepart *curl_mime_addpart(curl_mime *mime) part = (curl_mimepart *) malloc(sizeof(*part)); if(part) { - Curl_mime_initpart(part); + Curl_mime_initpart(part, mime->easy); part->parent = mime; if(mime->lastpart) @@ -1340,6 +1342,7 @@ CURLcode curl_mime_name(curl_mimepart *part, const char *name) return CURLE_BAD_FUNCTION_ARGUMENT; Curl_safefree(part->name); + part->name = NULL; if(name) { part->name = strdup(name); @@ -1357,6 +1360,7 @@ CURLcode curl_mime_filename(curl_mimepart *part, const char *filename) return CURLE_BAD_FUNCTION_ARGUMENT; Curl_safefree(part->filename); + part->filename = NULL; if(filename) { part->filename = strdup(filename); @@ -1456,6 +1460,7 @@ CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype) return CURLE_BAD_FUNCTION_ARGUMENT; Curl_safefree(part->mimetype); + part->mimetype = NULL; if(mimetype) { part->mimetype = strdup(mimetype); @@ -1546,6 +1551,10 @@ CURLcode Curl_mime_set_subparts(curl_mimepart *part, cleanup_part_content(part); if(subparts) { + /* Must belong to the same data handle. */ + if(part->easy && subparts->easy && part->easy != subparts->easy) + return CURLE_BAD_FUNCTION_ARGUMENT; + /* Should not have been attached already. */ if(subparts->parent) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -1556,7 +1565,8 @@ CURLcode Curl_mime_set_subparts(curl_mimepart *part, while(root->parent && root->parent->parent) root = root->parent->parent; if(subparts == root) { - /* Can't add as a subpart of itself. */ + if(part->easy) + failf(part->easy, "Can't add itself as a subpart"); return CURLE_BAD_FUNCTION_ARGUMENT; } } @@ -1626,7 +1636,7 @@ static size_t slist_size(struct curl_slist *s, static curl_off_t multipart_size(curl_mime *mime) { curl_off_t size; - curl_off_t boundarysize; + size_t boundarysize; curl_mimepart *part; if(!mime) @@ -1734,7 +1744,7 @@ const char *Curl_mime_contenttype(const char *filename) size_t len2 = strlen(ctts[i].extension); if(len1 >= len2 && strcasecompare(nameend - len2, ctts[i].extension)) - return ctts[i].type; + return ctts[i].type; } } return NULL; @@ -1756,8 +1766,7 @@ static bool content_type_match(const char *contenttype, return FALSE; } -CURLcode Curl_mime_prepare_headers(struct Curl_easy *data, - curl_mimepart *part, +CURLcode Curl_mime_prepare_headers(curl_mimepart *part, const char *contenttype, const char *disposition, enum mimestrategy strategy) @@ -1826,12 +1835,12 @@ CURLcode Curl_mime_prepare_headers(struct Curl_easy *data, char *filename = NULL; if(part->name) { - name = escape_string(data, part->name, strategy); + name = escape_string(part->easy, part->name, strategy); if(!name) ret = CURLE_OUT_OF_MEMORY; } if(!ret && part->filename) { - filename = escape_string(data, part->filename, strategy); + filename = escape_string(part->easy, part->filename, strategy); if(!filename) ret = CURLE_OUT_OF_MEMORY; } @@ -1888,8 +1897,7 @@ CURLcode Curl_mime_prepare_headers(struct Curl_easy *data, if(content_type_match(contenttype, STRCONST("multipart/form-data"))) disposition = "form-data"; for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart) { - ret = Curl_mime_prepare_headers(data, subpart, NULL, - disposition, strategy); + ret = Curl_mime_prepare_headers(subpart, NULL, disposition, strategy); if(ret) return ret; } diff --git a/contrib/libs/curl/lib/mime.h b/contrib/libs/curl/lib/mime.h index 04adf2d247..bafde29f40 100644 --- a/contrib/libs/curl/lib/mime.h +++ b/contrib/libs/curl/lib/mime.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -99,6 +99,7 @@ struct mime_state { /* A mime multipart. */ struct curl_mime { + struct Curl_easy *easy; /* The associated easy handle. */ curl_mimepart *parent; /* Parent part. */ curl_mimepart *firstpart; /* First part. */ curl_mimepart *lastpart; /* Last part. */ @@ -108,6 +109,7 @@ struct curl_mime { /* A mime part. */ struct curl_mimepart { + struct Curl_easy *easy; /* The associated easy handle. */ curl_mime *parent; /* Parent mime structure. */ curl_mimepart *nextpart; /* Forward linked list. */ enum mimekind kind; /* The part kind. */ @@ -137,16 +139,14 @@ CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...); !defined(CURL_DISABLE_IMAP)) /* Prototypes. */ -void Curl_mime_initpart(struct curl_mimepart *part); +void Curl_mime_initpart(struct curl_mimepart *part, struct Curl_easy *easy); void Curl_mime_cleanpart(struct curl_mimepart *part); -CURLcode Curl_mime_duppart(struct Curl_easy *data, - struct curl_mimepart *dst, +CURLcode Curl_mime_duppart(struct curl_mimepart *dst, const curl_mimepart *src); CURLcode Curl_mime_set_subparts(struct curl_mimepart *part, struct curl_mime *subparts, int take_ownership); -CURLcode Curl_mime_prepare_headers(struct Curl_easy *data, - struct curl_mimepart *part, +CURLcode Curl_mime_prepare_headers(struct curl_mimepart *part, const char *contenttype, const char *disposition, enum mimestrategy strategy); @@ -159,11 +159,11 @@ void Curl_mime_unpause(struct curl_mimepart *part); #else /* if disabled */ -#define Curl_mime_initpart(x) +#define Curl_mime_initpart(x,y) #define Curl_mime_cleanpart(x) -#define Curl_mime_duppart(x,y,z) CURLE_OK /* Nothing to duplicate. Succeed */ +#define Curl_mime_duppart(x,y) CURLE_OK /* Nothing to duplicate. Succeed */ #define Curl_mime_set_subparts(a,b,c) CURLE_NOT_BUILT_IN -#define Curl_mime_prepare_headers(a,b,c,d,e) CURLE_NOT_BUILT_IN +#define Curl_mime_prepare_headers(a,b,c,d) CURLE_NOT_BUILT_IN #define Curl_mime_size(x) (curl_off_t) -1 #define Curl_mime_read NULL #define Curl_mime_rewind(x) ((void)x, CURLE_NOT_BUILT_IN) diff --git a/contrib/libs/curl/lib/mprintf.c b/contrib/libs/curl/lib/mprintf.c index af5d753d6a..8a7c17a7ff 100644 --- a/contrib/libs/curl/lib/mprintf.c +++ b/contrib/libs/curl/lib/mprintf.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1999 - 2022, 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 @@ -400,7 +400,7 @@ static int dprintf_Pass1(const char *format, struct va_stack *vto, /* out of allowed range */ return 1; - switch(*fmt) { + switch (*fmt) { case 'S': flags |= FLAGS_ALT; /* FALLTHROUGH */ @@ -743,11 +743,11 @@ static int dprintf_formatf( goto number; -unsigned_number: + unsigned_number: /* Unsigned number of base BASE. */ is_neg = 0; -number: + number: /* Number of base BASE. */ /* Supply a default precision if none was given. */ diff --git a/contrib/libs/curl/lib/mqtt.c b/contrib/libs/curl/lib/mqtt.c index 799a21a568..4f3d14386f 100644 --- a/contrib/libs/curl/lib/mqtt.c +++ b/contrib/libs/curl/lib/mqtt.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) Björn Stenberg, <bjorn@haxx.se> + * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2019, Björn Stenberg, <bjorn@haxx.se> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -122,9 +122,8 @@ static CURLcode mqtt_send(struct Curl_easy *data, struct MQTT *mq = data->req.p.mqtt; ssize_t n; result = Curl_write(data, sockfd, buf, len, &n); - if(result) - return result; - Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)n); + if(!result) + Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)n); if(len != (size_t)n) { size_t nsend = len - n; char *sendleftovers = Curl_memdup(&buf[n], nsend); @@ -243,7 +242,7 @@ static int init_connpack(char *packet, char *remain, int remain_pos) /* keep-alive 0 = disabled */ packet[remain_pos + 9] = 0x00; packet[remain_pos + 10] = 0x3c; - /* end of variable header */ + /*end of variable header*/ return remain_pos + 10; } @@ -252,7 +251,7 @@ static CURLcode mqtt_connect(struct Curl_easy *data) CURLcode result = CURLE_OK; int pos = 0; int rc = 0; - /* remain length */ + /*remain length*/ int remain_pos = 0; char remain[4] = {0}; size_t packetlen = 0; @@ -605,7 +604,7 @@ static CURLcode mqtt_read_publish(struct Curl_easy *data, bool *done) unsigned char packet; switch(mqtt->state) { -MQTT_SUBACK_COMING: + MQTT_SUBACK_COMING: case MQTT_SUBACK_COMING: result = mqtt_verify_suback(data); if(result) @@ -636,7 +635,7 @@ MQTT_SUBACK_COMING: /* -- switched state -- */ remlen = mq->remaining_length; - infof(data, "Remaining length: %zu bytes", remlen); + infof(data, "Remaining length: %zd bytes", remlen); if(data->set.max_filesize && (curl_off_t)remlen > data->set.max_filesize) { failf(data, "Maximum file size exceeded"); @@ -688,7 +687,7 @@ MQTT_SUBACK_COMING: result = CURLE_WEIRD_SERVER_REPLY; goto end; } -end: + end: return result; } diff --git a/contrib/libs/curl/lib/mqtt.h b/contrib/libs/curl/lib/mqtt.h index 63961366fc..c400d9b14e 100644 --- a/contrib/libs/curl/lib/mqtt.h +++ b/contrib/libs/curl/lib/mqtt.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Björn Stenberg, <bjorn@haxx.se> + * Copyright (C) 2019 - 2022, Björn Stenberg, <bjorn@haxx.se> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/contrib/libs/curl/lib/multi.c b/contrib/libs/curl/lib/multi.c index 8cfd7e8144..7a54387253 100644 --- a/contrib/libs/curl/lib/multi.c +++ b/contrib/libs/curl/lib/multi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -29,7 +29,6 @@ #include "urldata.h" #include "transfer.h" #include "url.h" -#include "cfilters.h" #include "connect.h" #include "progress.h" #include "easyif.h" @@ -90,17 +89,8 @@ #define CURL_MULTI_HANDLE 0x000bab1e -#ifdef DEBUGBUILD -/* On a debug build, we want to fail hard on multi handles that - * are not NULL, but no longer have the MAGIC touch. This gives - * us early warning on things only discovered by valgrind otherwise. */ -#define GOOD_MULTI_HANDLE(x) \ - (((x) && (x)->magic == CURL_MULTI_HANDLE)? TRUE: \ - (DEBUGASSERT(!(x)), FALSE)) -#else #define GOOD_MULTI_HANDLE(x) \ ((x) && (x)->magic == CURL_MULTI_HANDLE) -#endif static CURLMcode singlesocket(struct Curl_multi *multi, struct Curl_easy *data); @@ -112,7 +102,7 @@ static CURLMcode multi_timeout(struct Curl_multi *multi, static void process_pending_handles(struct Curl_multi *multi); #ifdef DEBUGBUILD -static const char * const multi_statename[]={ +static const char * const statename[]={ "INIT", "PENDING", "CONNECT", @@ -170,7 +160,7 @@ static void mstate(struct Curl_easy *data, CURLMstate state NULL, /* TUNNELING */ NULL, /* PROTOCONNECT */ NULL, /* PROTOCONNECTING */ - NULL, /* DO */ + Curl_connect_free, /* DO */ NULL, /* DOING */ NULL, /* DOING_MORE */ before_perform, /* DID */ @@ -194,10 +184,15 @@ static void mstate(struct Curl_easy *data, CURLMstate state #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) if(data->mstate >= MSTATE_PENDING && data->mstate < MSTATE_COMPLETED) { + long connection_id = -5000; + + if(data->conn) + connection_id = data->conn->connection_id; + infof(data, - "STATE: %s => %s handle %p; line %d", - multi_statename[oldstate], multi_statename[data->mstate], - (void *)data, lineno); + "STATE: %s => %s handle %p; line %d (connection #%ld)", + statename[oldstate], statename[data->mstate], + (void *)data, lineno, connection_id); } #endif @@ -387,10 +382,12 @@ static void sh_init(struct Curl_hash *hash, int hashsize) * Called when a transfer is completed. Adds the given msg pointer to * the list kept in the multi handle. */ -static void multi_addmsg(struct Curl_multi *multi, struct Curl_message *msg) +static CURLMcode multi_addmsg(struct Curl_multi *multi, + struct Curl_message *msg) { Curl_llist_insert_next(&multi->msglist, multi->msglist.tail, msg, &msg->list); + return CURLM_OK; } struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ @@ -413,7 +410,6 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ Curl_llist_init(&multi->msglist, NULL); Curl_llist_init(&multi->pending, NULL); - Curl_llist_init(&multi->msgsent, NULL); multi->multiplexing = TRUE; @@ -443,11 +439,14 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ return multi; -error: + error: sockhash_destroy(&multi->sockhash); Curl_hash_destroy(&multi->hostcache); Curl_conncache_destroy(&multi->conn_cache); + Curl_llist_destroy(&multi->msglist, NULL); + Curl_llist_destroy(&multi->pending, NULL); + free(multi); return NULL; } @@ -459,66 +458,6 @@ struct Curl_multi *curl_multi_init(void) CURL_DNS_HASH_SIZE); } -#ifdef DEBUGBUILD -static void multi_warn_debug(struct Curl_multi *multi, struct Curl_easy *data) -{ - if(!multi->warned) { - infof(data, "!!! WARNING !!!"); - infof(data, "This is a debug build of libcurl, " - "do not use in production."); - multi->warned = true; - } -} -#else -#define multi_warn_debug(x,y) Curl_nop_stmt -#endif - -/* returns TRUE if the easy handle is supposed to be present in the main link - list */ -static bool in_main_list(struct Curl_easy *data) -{ - return ((data->mstate != MSTATE_PENDING) && - (data->mstate != MSTATE_MSGSENT)); -} - -static void link_easy(struct Curl_multi *multi, - struct Curl_easy *data) -{ - /* We add the new easy entry last in the list. */ - data->next = NULL; /* end of the line */ - if(multi->easyp) { - struct Curl_easy *last = multi->easylp; - last->next = data; - data->prev = last; - multi->easylp = data; /* the new last node */ - } - else { - /* first node, make prev NULL! */ - data->prev = NULL; - multi->easylp = multi->easyp = data; /* both first and last */ - } -} - -/* unlink the given easy handle from the linked list of easy handles */ -static void unlink_easy(struct Curl_multi *multi, - struct Curl_easy *data) -{ - /* make the previous node point to our next */ - if(data->prev) - data->prev->next = data->next; - else - multi->easyp = data->next; /* point to first node */ - - /* make our next point to our previous node */ - if(data->next) - data->next->prev = data->prev; - else - multi->easylp = data->prev; /* point to last node */ - - data->prev = data->next = NULL; -} - - CURLMcode curl_multi_add_handle(struct Curl_multi *multi, struct Curl_easy *data) { @@ -614,7 +553,19 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, data->psl = &multi->psl; #endif - link_easy(multi, data); + /* We add the new entry last in the list. */ + data->next = NULL; /* end of the line */ + if(multi->easyp) { + struct Curl_easy *last = multi->easylp; + last->next = data; + data->prev = last; + multi->easylp = data; /* the new last node */ + } + else { + /* first node, make prev NULL! */ + data->prev = NULL; + multi->easylp = multi->easyp = data; /* both first and last */ + } /* increase the node-counter */ multi->num_easy++; @@ -632,14 +583,8 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, data->set.server_response_timeout; data->state.conn_cache->closure_handle->set.no_signal = data->set.no_signal; - data->id = data->state.conn_cache->next_easy_id++; - if(data->state.conn_cache->next_easy_id <= 0) - data->state.conn_cache->next_easy_id = 0; CONNCACHE_UNLOCK(data); - multi_warn_debug(multi, data); - infof(data, "processing: %s", data->state.url); - return CURLM_OK; } @@ -711,20 +656,8 @@ static CURLcode multi_done(struct Curl_easy *data, result = CURLE_ABORTED_BY_CALLBACK; } - /* Inform connection filters that this transfer is done */ - Curl_conn_ev_data_done(data, premature); - process_pending_handles(data->multi); /* connection / multiplex */ - Curl_safefree(data->state.ulbuf); - - /* if the transfer was completed in a paused state there can be buffered - data left to free */ - for(i = 0; i < data->state.tempcount; i++) { - Curl_dyn_free(&data->state.tempwrite[i].b); - } - data->state.tempcount = 0; - CONNCACHE_LOCK(data); Curl_detach_connection(data); if(CONN_INUSE(conn)) { @@ -743,6 +676,14 @@ static CURLcode multi_done(struct Curl_easy *data, conn->dns_entry = NULL; } Curl_hostcache_prune(data); + Curl_safefree(data->state.ulbuf); + + /* if the transfer was completed in a paused state there can be buffered + data left to free */ + for(i = 0; i < data->state.tempcount; i++) { + Curl_dyn_free(&data->state.tempwrite[i].b); + } + data->state.tempcount = 0; /* if data->set.reuse_forbid is TRUE, it means the libcurl client has forced us to close this connection. This is ignored for requests taking @@ -759,7 +700,6 @@ static CURLcode multi_done(struct Curl_easy *data, but currently we have no such detail knowledge. */ - data->state.recent_conn_id = conn->connection_id; if((data->set.reuse_forbid #if defined(USE_NTLM) && !(conn->http_ntlm_state == NTLMSTATE_TYPE2 || @@ -770,13 +710,7 @@ static CURLcode multi_done(struct Curl_easy *data, conn->proxy_negotiate_state == GSS_AUTHRECV) #endif ) || conn->bits.close - || (premature && !Curl_conn_is_multiplex(conn, FIRSTSOCKET))) { - DEBUGF(infof(data, "multi_done, not re-using connection=%" - CURL_FORMAT_CURL_OFF_T ", forbid=%d" - ", close=%d, premature=%d, conn_multiplex=%d", - conn->connection_id, - data->set.reuse_forbid, conn->bits.close, premature, - Curl_conn_is_multiplex(conn, FIRSTSOCKET))); + || (premature && !(conn->handler->flags & PROTOPT_STREAM))) { connclose(conn, "disconnecting"); Curl_conncache_remove_conn(data, conn, FALSE); CONNCACHE_UNLOCK(data); @@ -793,16 +727,15 @@ static CURLcode multi_done(struct Curl_easy *data, conn->bits.conn_to_host ? conn->conn_to_host.dispname : conn->host.dispname; /* create string before returning the connection */ - curl_off_t connection_id = conn->connection_id; + long connection_id = conn->connection_id; msnprintf(buffer, sizeof(buffer), - "Connection #%" CURL_FORMAT_CURL_OFF_T " to host %s left intact", + "Connection #%ld to host %s left intact", connection_id, host); /* the connection is no longer in use by this transfer */ CONNCACHE_UNLOCK(data); if(Curl_conncache_return_conn(data, conn)) { /* remember the most recently used connection */ data->state.lastconnect_id = connection_id; - data->state.recent_conn_id = connection_id; infof(data, "%s", buffer); } else @@ -887,16 +820,10 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, called. Do it after multi_done() in case that sets another time! */ Curl_expire_clear(data); - if(data->connect_queue.ptr) { - /* the handle is in the pending or msgsent lists, so go ahead and remove - it */ - if(data->mstate == MSTATE_PENDING) - Curl_llist_remove(&multi->pending, &data->connect_queue, NULL); - else - Curl_llist_remove(&multi->msgsent, &data->connect_queue, NULL); - } - if(in_main_list(data)) - unlink_easy(multi, data); + if(data->connect_queue.ptr) + /* the handle was in the pending list waiting for an available connection, + so go ahead and remove it */ + Curl_llist_remove(&multi->pending, &data->connect_queue, NULL); if(data->dns.hostcachetype == HCACHE_MULTI) { /* stop using the multi handle's DNS cache, *after* the possible @@ -907,6 +834,10 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, Curl_wildcard_dtor(&data->wildcard); + /* destroy the timeout list that is held in the easy handle, do this *after* + multi_done() as that may actually call Curl_expire that uses this */ + Curl_llist_destroy(&data->state.timeoutlist, NULL); + /* change state without using multistate(), only to make singlesocket() do what we want */ data->mstate = MSTATE_COMPLETED; @@ -957,6 +888,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, /* make sure there's no pending message in the queue sent from this easy handle */ + for(e = multi->msglist.head; e; e = e->next) { struct Curl_message *msg = e->ptr; @@ -967,6 +899,29 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, } } + /* Remove from the pending list if it is there. Otherwise this will + remain on the pending list forever due to the state change. */ + for(e = multi->pending.head; e; e = e->next) { + struct Curl_easy *curr_data = e->ptr; + + if(curr_data == data) { + Curl_llist_remove(&multi->pending, e, NULL); + break; + } + } + + /* make the previous node point to our next */ + if(data->prev) + data->prev->next = data->next; + else + multi->easyp = data->next; /* point to first node */ + + /* make our next point to our previous node */ + if(data->next) + data->next->prev = data->prev; + else + multi->easylp = data->prev; /* point to last node */ + /* NOTE NOTE NOTE We do not touch the easy handle here! */ multi->num_easy--; /* one less to care about now */ @@ -995,8 +950,9 @@ void Curl_detach_connection(struct Curl_easy *data) { struct connectdata *conn = data->conn; if(conn) { - Curl_conn_ev_data_detach(conn, data); + Curl_connect_done(data); /* if mid-CONNECT, shut it down */ Curl_llist_remove(&conn->easyq, &data->conn_queue, NULL); + Curl_ssl_detach_conn(data, conn); } data->conn = NULL; } @@ -1014,9 +970,53 @@ void Curl_attach_connection(struct Curl_easy *data, data->conn = conn; Curl_llist_insert_next(&conn->easyq, conn->easyq.tail, data, &data->conn_queue); - if(conn->handler && conn->handler->attach) + if(conn->handler->attach) conn->handler->attach(data, conn); - Curl_conn_ev_data_attach(conn, data); + Curl_ssl_associate_conn(data, conn); +} + +static int waitconnect_getsock(struct connectdata *conn, + curl_socket_t *sock) +{ + int i; + int s = 0; + int rc = 0; + +#ifdef USE_SSL +#ifndef CURL_DISABLE_PROXY + if(CONNECT_FIRSTSOCKET_PROXY_SSL()) + return Curl_ssl->getsock(conn, sock); +#endif +#endif + + if(SOCKS_STATE(conn->cnnct.state)) + return Curl_SOCKS_getsock(conn, sock, FIRSTSOCKET); + + for(i = 0; i<2; i++) { + if(conn->tempsock[i] != CURL_SOCKET_BAD) { + sock[s] = conn->tempsock[i]; + rc |= GETSOCK_WRITESOCK(s); +#ifdef ENABLE_QUIC + if(conn->transport == TRNSPRT_QUIC) + /* when connecting QUIC, we want to read the socket too */ + rc |= GETSOCK_READSOCK(s); +#endif + s++; + } + } + + return rc; +} + +static int waitproxyconnect_getsock(struct connectdata *conn, + curl_socket_t *sock) +{ + sock[0] = conn->sock[FIRSTSOCKET]; + + if(conn->connect_state) + return Curl_connect_getsock(conn); + + return GETSOCK_WRITESOCK(0); } static int domore_getsock(struct Curl_easy *data, @@ -1043,7 +1043,11 @@ static int protocol_getsock(struct Curl_easy *data, { if(conn->handler->proto_getsock) return conn->handler->proto_getsock(data, conn, socks); - return Curl_conn_get_select_socks(data, FIRSTSOCKET, socks); + /* Backup getsock logic. Since there is a live socket in use, we must wait + for it or it will be removed from watching when the multi_socket API is + used. */ + socks[0] = conn->sock[FIRSTSOCKET]; + return GETSOCK_READSOCK(0) | GETSOCK_WRITESOCK(0); } /* returns bitmapped flags for this handle and its sockets. The 'socks[]' @@ -1074,8 +1078,10 @@ static int multi_getsock(struct Curl_easy *data, return doing_getsock(data, conn, socks); case MSTATE_TUNNELING: + return waitproxyconnect_getsock(conn, socks); + case MSTATE_CONNECTING: - return Curl_conn_get_select_socks(data, FIRSTSOCKET, socks); + return waitconnect_getsock(conn, socks); case MSTATE_DOING_MORE: return domore_getsock(data, conn, socks); @@ -1148,22 +1154,6 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi, return CURLM_OK; } -#ifdef USE_WINSOCK -/* Reset FD_WRITE for TCP sockets. Nothing is actually sent. UDP sockets can't - * be reset this way because an empty datagram would be sent. #9203 - * - * "On Windows the internal state of FD_WRITE as returned from - * WSAEnumNetworkEvents is only reset after successful send()." - */ -static void reset_socket_fdwrite(curl_socket_t s) -{ - int t; - int l = (int)sizeof(t); - if(!getsockopt(s, SOL_SOCKET, SO_TYPE, (char *)&t, &l) && t == SOCK_STREAM) - send(s, NULL, 0, 0); -} -#endif - #define NUM_POLLS_ON_STACK 10 static CURLMcode multi_wait(struct Curl_multi *multi, @@ -1285,7 +1275,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi, s = sockbunch[i]; #ifdef USE_WINSOCK mask |= FD_WRITE|FD_CONNECT|FD_CLOSE; - reset_socket_fdwrite(s); + send(s, NULL, 0, 0); /* reset FD_WRITE */ #endif ufds[nfds].fd = s; ufds[nfds].events = POLLOUT; @@ -1319,7 +1309,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi, mask |= FD_OOB; if(extra_fds[i].events & CURL_WAIT_POLLOUT) { mask |= FD_WRITE|FD_CONNECT|FD_CLOSE; - reset_socket_fdwrite(extra_fds[i].fd); + send(extra_fds[i].fd, NULL, 0, 0); /* reset FD_WRITE */ } if(WSAEventSelect(extra_fds[i].fd, multi->wsa_event, mask) != 0) { if(ufds_malloc) @@ -1749,8 +1739,7 @@ static CURLcode protocol_connect(struct Curl_easy *data, *protocol_done = FALSE; - if(Curl_conn_is_connected(conn, FIRSTSOCKET) - && conn->bits.protoconnstart) { + if(conn->bits.tcpconnect[FIRSTSOCKET] && conn->bits.protoconnstart) { /* We already are connected, get back. This may happen when the connect worked fine in the first call, like when we connect to a local server or proxy. Note that we don't know if the protocol is actually done. @@ -1764,6 +1753,21 @@ static CURLcode protocol_connect(struct Curl_easy *data, } if(!conn->bits.protoconnstart) { +#ifndef CURL_DISABLE_PROXY + result = Curl_proxy_connect(data, FIRSTSOCKET); + if(result) + return result; + + if(CONNECT_FIRSTSOCKET_PROXY_SSL()) + /* wait for HTTPS proxy SSL initialization to complete */ + return CURLE_OK; + + if(conn->bits.tunnel_proxy && conn->bits.httpproxy && + Curl_connect_ongoing(conn)) + /* when using an HTTP tunnel proxy, await complete tunnel establishment + before proceeding further. Return CURLE_OK so we'll be called again */ + return CURLE_OK; +#endif if(conn->handler->connect_it) { /* is there a protocol-specific connect() procedure? */ @@ -1783,90 +1787,6 @@ static CURLcode protocol_connect(struct Curl_easy *data, } /* - * readrewind() rewinds the read stream. This is typically used for HTTP - * POST/PUT with multi-pass authentication when a sending was denied and a - * resend is necessary. - */ -static CURLcode readrewind(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - curl_mimepart *mimepart = &data->set.mimepost; - DEBUGASSERT(conn); - - data->state.rewindbeforesend = FALSE; /* we rewind now */ - - /* explicitly switch off sending data on this connection now since we are - about to restart a new transfer and thus we want to avoid inadvertently - sending more data on the existing connection until the next transfer - starts */ - data->req.keepon &= ~KEEP_SEND; - - /* We have sent away data. If not using CURLOPT_POSTFIELDS or - CURLOPT_HTTPPOST, call app to rewind - */ - if(conn->handler->protocol & PROTO_FAMILY_HTTP) { - struct HTTP *http = data->req.p.http; - - if(http->sendit) - mimepart = http->sendit; - } - if(data->set.postfields || - (data->state.httpreq == HTTPREQ_GET) || - (data->state.httpreq == HTTPREQ_HEAD)) - ; /* no need to rewind */ - else if(data->state.httpreq == HTTPREQ_POST_MIME || - data->state.httpreq == HTTPREQ_POST_FORM) { - CURLcode result = Curl_mime_rewind(mimepart); - if(result) { - failf(data, "Cannot rewind mime/post data"); - return result; - } - } - else { - if(data->set.seek_func) { - int err; - - Curl_set_in_callback(data, true); - err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET); - Curl_set_in_callback(data, false); - if(err) { - failf(data, "seek callback returned error %d", (int)err); - return CURLE_SEND_FAIL_REWIND; - } - } - else if(data->set.ioctl_func) { - curlioerr err; - - Curl_set_in_callback(data, true); - err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD, - data->set.ioctl_client); - Curl_set_in_callback(data, false); - infof(data, "the ioctl callback returned %d", (int)err); - - if(err) { - failf(data, "ioctl callback returned error %d", (int)err); - return CURLE_SEND_FAIL_REWIND; - } - } - else { - /* If no CURLOPT_READFUNCTION is used, we know that we operate on a - given FILE * stream and we can actually attempt to rewind that - ourselves with fseek() */ - if(data->state.fread_func == (curl_read_callback)fread) { - if(-1 != fseek(data->state.in, 0, SEEK_SET)) - /* successful rewind */ - return CURLE_OK; - } - - /* no callback set or failure above, makes us fail at once */ - failf(data, "necessary data rewind wasn't possible"); - return CURLE_SEND_FAIL_REWIND; - } - } - return CURLE_OK; -} - -/* * Curl_preconnect() is called immediately before a connect starts. When a * redirect is followed, this is then called multiple times during a single * transfer. @@ -1878,7 +1798,6 @@ CURLcode Curl_preconnect(struct Curl_easy *data) if(!data->state.buffer) return CURLE_OUT_OF_MEMORY; } - return CURLE_OK; } @@ -1915,8 +1834,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multistate(data, MSTATE_COMPLETED); } - multi_warn_debug(multi, data); - do { /* A "stream" here is a logical stream if the protocol can handle that (HTTP/2), or the full connection for older protocols */ @@ -1967,6 +1884,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } break; + case MSTATE_PENDING: + /* We will stay here until there is a connection available. Then + we try again in the MSTATE_CONNECT state. */ + break; + case MSTATE_CONNECT: /* Connect. We want to get a connection identifier filled in. */ /* init this transfer. */ @@ -1981,7 +1903,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(data->set.connecttimeout) Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT); - result = Curl_connect(data, &async, &connected); + result = Curl_connect(data, &async, &protocol_connected); if(CURLE_NO_CONNECTION_AVAILABLE == result) { /* There was no connection available. We will go to the pending state and wait for an available connection. */ @@ -1990,8 +1912,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* add this handle to the list of connect-pending handles */ Curl_llist_insert_next(&multi->pending, multi->pending.tail, data, &data->connect_queue); - /* unlink from the main list */ - unlink_easy(multi, data); result = CURLE_OK; break; } @@ -2011,10 +1931,15 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, WAITDO or DO! */ rc = CURLM_CALL_MULTI_PERFORM; - if(connected) - multistate(data, MSTATE_PROTOCONNECT); + if(protocol_connected) + multistate(data, MSTATE_DO); else { - multistate(data, MSTATE_CONNECTING); +#ifndef CURL_DISABLE_HTTP + if(Curl_connect_ongoing(data->conn)) + multistate(data, MSTATE_TUNNELING); + else +#endif + multistate(data, MSTATE_CONNECTING); } } } @@ -2034,7 +1959,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else #endif if(conn->bits.conn_to_host) - hostname = conn->conn_to_host.name; + hostname = conn->conn_to_host.name; else hostname = conn->host.name; @@ -2066,7 +1991,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(dns) { /* Perform the next step in the connection phase, and then move on to the WAITCONNECT state */ - result = Curl_once_resolved(data, &connected); + result = Curl_once_resolved(data, &protocol_connected); if(result) /* if Curl_once_resolved() returns failure, the connection struct @@ -2075,10 +2000,15 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else { /* call again please so that we get the next socket setup */ rc = CURLM_CALL_MULTI_PERFORM; - if(connected) - multistate(data, MSTATE_PROTOCONNECT); + if(protocol_connected) + multistate(data, MSTATE_DO); else { - multistate(data, MSTATE_CONNECTING); +#ifndef CURL_DISABLE_HTTP + if(Curl_connect_ongoing(data->conn)) + multistate(data, MSTATE_TUNNELING); + else +#endif + multistate(data, MSTATE_CONNECTING); } } } @@ -2107,9 +2037,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else #endif if(!result) { - rc = CURLM_CALL_MULTI_PERFORM; - /* initiate protocol connect phase */ - multistate(data, MSTATE_PROTOCONNECT); + if( +#ifndef CURL_DISABLE_PROXY + (data->conn->http_proxy.proxytype != CURLPROXY_HTTPS || + data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) && +#endif + Curl_connect_complete(data->conn)) { + rc = CURLM_CALL_MULTI_PERFORM; + /* initiate protocol connect phase */ + multistate(data, MSTATE_PROTOCONNECT); + } } else stream_error = TRUE; @@ -2119,10 +2056,27 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case MSTATE_CONNECTING: /* awaiting a completion of an asynch TCP connect */ DEBUGASSERT(data->conn); - result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &connected); + result = Curl_is_connected(data, data->conn, FIRSTSOCKET, &connected); if(connected && !result) { +#ifndef CURL_DISABLE_HTTP + if( +#ifndef CURL_DISABLE_PROXY + (data->conn->http_proxy.proxytype == CURLPROXY_HTTPS && + !data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) || +#endif + Curl_connect_ongoing(data->conn)) { + multistate(data, MSTATE_TUNNELING); + break; + } +#endif rc = CURLM_CALL_MULTI_PERFORM; +#ifndef CURL_DISABLE_PROXY + multistate(data, + data->conn->bits.tunnel_proxy? + MSTATE_TUNNELING : MSTATE_PROTOCONNECT); +#else multistate(data, MSTATE_PROTOCONNECT); +#endif } else if(result) { /* failure detected */ @@ -2134,19 +2088,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; case MSTATE_PROTOCONNECT: - if(data->state.rewindbeforesend) - result = readrewind(data); - - if(!result && data->conn->bits.reuse) { - /* ftp seems to hang when protoconnect on reused connection - * since we handle PROTOCONNECT in general inside the filers, it - * seems wrong to restart this on a reused connection. */ - multistate(data, MSTATE_DO); - rc = CURLM_CALL_MULTI_PERFORM; - break; - } - if(!result) - result = protocol_connect(data, &protocol_connected); + result = protocol_connect(data, &protocol_connected); if(!result && !protocol_connected) /* switch to waiting state */ multistate(data, MSTATE_PROTOCONNECTING); @@ -2220,7 +2162,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, #ifndef CURL_DISABLE_FTP /* some steps needed for wildcard matching */ if(data->state.wildcardmatch) { - struct WildcardData *wc = data->wildcard; + struct WildcardData *wc = &data->wildcard; if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) { /* skip some states if it is important */ multi_done(data, CURLE_OK, FALSE); @@ -2236,6 +2178,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* DO was not completed in one function call, we must continue DOING... */ multistate(data, MSTATE_DOING); + rc = CURLM_OK; } /* after DO, go DO_DONE... or DO_MORE */ @@ -2243,6 +2186,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* we're supposed to do more, but we need to sit down, relax and wait a little while first */ multistate(data, MSTATE_DOING_MORE); + rc = CURLM_OK; } else { /* we're done with the DO, now DID */ @@ -2343,8 +2287,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, MSTATE_DID : MSTATE_DOING); rc = CURLM_CALL_MULTI_PERFORM; } - /* else - stay in DO_MORE */ + else + /* stay in DO_MORE */ + rc = CURLM_OK; } else { /* failure detected */ @@ -2369,7 +2314,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, #ifndef CURL_DISABLE_FTP if(data->state.wildcardmatch && ((data->conn->handler->flags & PROTOPT_WILDCARD) == 0)) { - data->wildcard->state = CURLWC_DONE; + data->wildcard.state = CURLWC_DONE; } #endif multistate(data, MSTATE_DONE); @@ -2573,6 +2518,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, won't get stuck on this transfer at the expense of other concurrent transfers */ Curl_expire(data, 0, EXPIRE_RUN_NOW); + rc = CURLM_OK; } break; } @@ -2598,7 +2544,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, #ifndef CURL_DISABLE_FTP if(data->state.wildcardmatch) { - if(data->wildcard->state != CURLWC_DONE) { + if(data->wildcard.state != CURLWC_DONE) { /* if a wildcard is set and we are not ending -> lets start again with MSTATE_INIT */ multistate(data, MSTATE_INIT); @@ -2614,11 +2560,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case MSTATE_COMPLETED: break; - case MSTATE_PENDING: case MSTATE_MSGSENT: - /* handles in these states should NOT be in this list */ - DEBUGASSERT(0); - break; + data->result = result; + return CURLM_OK; /* do nothing */ default: return CURLM_INTERNAL_ERROR; @@ -2638,7 +2582,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multi_handle_timeout(data, nowp, &stream_error, &result, TRUE); } -statemachine_end: + statemachine_end: if(data->mstate < MSTATE_COMPLETED) { if(result) { @@ -2706,17 +2650,10 @@ statemachine_end: msg->extmsg.easy_handle = data; msg->extmsg.data.result = result; - multi_addmsg(multi, msg); + rc = multi_addmsg(multi, msg); DEBUGASSERT(!data->conn); } multistate(data, MSTATE_MSGSENT); - - /* add this handle to the list of msgsent handles */ - Curl_llist_insert_next(&multi->msgsent, multi->msgsent.tail, data, - &data->connect_queue); - /* unlink from the main list */ - unlink_easy(multi, data); - return CURLM_OK; } } while((rc == CURLM_CALL_MULTI_PERFORM) || multi_ischanged(multi, FALSE)); @@ -2739,28 +2676,18 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles) return CURLM_RECURSIVE_API_CALL; data = multi->easyp; - if(data) { + while(data) { CURLMcode result; - bool nosig = data->set.no_signal; SIGPIPE_VARIABLE(pipe_st); + sigpipe_ignore(data, &pipe_st); - /* Do the loop and only alter the signal ignore state if the next handle - has a different NO_SIGNAL state than the previous */ - do { - /* the current node might be unlinked in multi_runsingle(), get the next - pointer now */ - struct Curl_easy *datanext = data->next; - if(data->set.no_signal != nosig) { - sigpipe_restore(&pipe_st); - sigpipe_ignore(data, &pipe_st); - nosig = data->set.no_signal; - } - result = multi_runsingle(multi, &now, data); - if(result) - returncode = result; - data = datanext; /* operate on next handle */ - } while(data); + result = multi_runsingle(multi, &now, data); sigpipe_restore(&pipe_st); + + if(result) + returncode = result; + + data = data->next; /* operate on next handle */ } /* @@ -2789,18 +2716,6 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles) return returncode; } -/* unlink_all_msgsent_handles() detaches all those easy handles from this - multi handle */ -static void unlink_all_msgsent_handles(struct Curl_multi *multi) -{ - struct Curl_llist_element *e = multi->msgsent.head; - if(e) { - struct Curl_easy *data = e->ptr; - DEBUGASSERT(data->mstate == MSTATE_MSGSENT); - data->multi = NULL; - } -} - CURLMcode curl_multi_cleanup(struct Curl_multi *multi) { struct Curl_easy *data; @@ -2812,8 +2727,6 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi) multi->magic = 0; /* not good anymore */ - unlink_all_msgsent_handles(multi); - process_pending_handles(multi); /* First remove all remaining easy handles */ data = multi->easyp; while(data) { @@ -2845,6 +2758,9 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi) sockhash_destroy(&multi->sockhash); Curl_conncache_destroy(&multi->conn_cache); + Curl_llist_destroy(&multi->msglist, NULL); + Curl_llist_destroy(&multi->pending, NULL); + Curl_hash_destroy(&multi->hostcache); Curl_psl_destroy(&multi->psl); @@ -2856,11 +2772,6 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi) wakeup_close(multi->wakeup_pair[1]); #endif #endif - -#ifdef USE_SSL - Curl_free_multi_ssl_backend_data(multi->ssl_backend_data); -#endif - free(multi); return CURLM_OK; @@ -3193,9 +3104,6 @@ static CURLMcode multi_socket(struct Curl_multi *multi, struct Curl_easy *data = NULL; struct Curl_tree *t; struct curltime now = Curl_now(); - bool first = FALSE; - bool nosig = FALSE; - SIGPIPE_VARIABLE(pipe_st); if(checkall) { /* *perform() deals with running_handles on its own */ @@ -3238,7 +3146,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK)) /* set socket event bitmask if they're not locked */ - data->conn->cselect_bits = (unsigned char)ev_bitmask; + data->conn->cselect_bits = ev_bitmask; Curl_expire(data, 0, EXPIRE_RUN_NOW); } @@ -3270,24 +3178,18 @@ static CURLMcode multi_socket(struct Curl_multi *multi, do { /* the first loop lap 'data' can be NULL */ if(data) { - if(!first) { - first = TRUE; - nosig = data->set.no_signal; /* initial state */ - sigpipe_ignore(data, &pipe_st); - } - else if(data->set.no_signal != nosig) { - sigpipe_restore(&pipe_st); - sigpipe_ignore(data, &pipe_st); - nosig = data->set.no_signal; /* remember new state */ - } + SIGPIPE_VARIABLE(pipe_st); + + sigpipe_ignore(data, &pipe_st); result = multi_runsingle(multi, &now, data); + sigpipe_restore(&pipe_st); if(CURLM_OK >= result) { /* get the socket(s) and check if the state has been changed since last */ result = singlesocket(multi, data); if(result) - break; + return result; } } @@ -3301,8 +3203,6 @@ static CURLMcode multi_socket(struct Curl_multi *multi, } } while(t); - if(first) - sigpipe_restore(&pipe_st); *running_handles = multi->num_alive; return result; @@ -3337,7 +3237,7 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi, multi->push_userp = va_arg(param, void *); break; case CURLMOPT_PIPELINING: - multi->multiplexing = va_arg(param, long) & CURLPIPE_MULTIPLEX ? 1 : 0; + multi->multiplexing = va_arg(param, long) & CURLPIPE_MULTIPLEX; break; case CURLMOPT_TIMERFUNCTION: multi->timer_cb = va_arg(param, curl_multi_timer_callback); @@ -3703,7 +3603,7 @@ void Curl_expire_clear(struct Curl_easy *data) } #ifdef DEBUGBUILD - infof(data, "Expire cleared"); + infof(data, "Expire cleared (transfer %p)", data); #endif nowp->tv_sec = 0; nowp->tv_usec = 0; @@ -3756,8 +3656,6 @@ void Curl_multiuse_state(struct Curl_easy *data, process_pending_handles(data->multi); } -/* process_pending_handles() moves all handles from PENDING - back into the main list and change state to CONNECT */ static void process_pending_handles(struct Curl_multi *multi) { struct Curl_llist_element *e = multi->pending.head; @@ -3766,9 +3664,6 @@ static void process_pending_handles(struct Curl_multi *multi) DEBUGASSERT(data->mstate == MSTATE_PENDING); - /* put it back into the main list */ - link_easy(multi, data); - multistate(data, MSTATE_CONNECT); /* Remove this node from the list */ @@ -3811,7 +3706,7 @@ void Curl_multi_dump(struct Curl_multi *multi) /* only display handles that are not completed */ fprintf(stderr, "handle %p, state %s, %d sockets\n", (void *)data, - multi_statename[data->mstate], data->numsocks); + statename[data->mstate], data->numsocks); for(i = 0; i < data->numsocks; i++) { curl_socket_t s = data->sockets[i]; struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s); diff --git a/contrib/libs/curl/lib/multihandle.h b/contrib/libs/curl/lib/multihandle.h index 5b16bb605f..a997784ea3 100644 --- a/contrib/libs/curl/lib/multihandle.h +++ b/contrib/libs/curl/lib/multihandle.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -79,10 +79,6 @@ typedef enum { /* value for MAXIMUM CONCURRENT STREAMS upper limit */ #define INITIAL_MAX_CONCURRENT_STREAMS ((1U << 31) - 1) -/* Curl_multi SSL backend-specific data; declared differently by each SSL - backend */ -struct multi_ssl_backend_data; - /* This is the struct known as CURLM on the outside */ struct Curl_multi { /* First a simple identifier to easier detect if a user mix up @@ -101,8 +97,6 @@ struct Curl_multi { struct Curl_llist pending; /* Curl_easys that are in the MSTATE_PENDING state */ - struct Curl_llist msgsent; /* Curl_easys that are in the - MSTATE_MSGSENT state */ /* callback function and user data pointer for the *socket() API */ curl_socket_callback socket_cb; @@ -124,10 +118,6 @@ struct Curl_multi { times of all currently set timers */ struct Curl_tree *timetree; -#if defined(USE_SSL) - struct multi_ssl_backend_data *ssl_backend_data; -#endif - /* 'sockhash' is the lookup hash for socket descriptor => easy handles (note the pluralis form, there can be more than one easy handle waiting on the same actual socket) */ @@ -164,17 +154,14 @@ struct Curl_multi { #define IPV6_DEAD 1 #define IPV6_WORKS 2 unsigned char ipv6_up; /* IPV6_* defined */ - BIT(multiplexing); /* multiplexing wanted */ - BIT(recheckstate); /* see Curl_multi_connchanged */ - BIT(in_callback); /* true while executing a callback */ + bool multiplexing; /* multiplexing wanted */ + bool recheckstate; /* see Curl_multi_connchanged */ + bool in_callback; /* true while executing a callback */ #ifdef USE_OPENSSL - BIT(ssl_seeded); + bool ssl_seeded; #endif - BIT(dead); /* a callback returned error, everything needs to crash and + bool dead; /* a callback returned error, everything needs to crash and burn */ -#ifdef DEBUGBUILD - BIT(warned); /* true after user warned of DEBUGBUILD */ -#endif }; #endif /* HEADER_CURL_MULTIHANDLE_H */ diff --git a/contrib/libs/curl/lib/multiif.h b/contrib/libs/curl/lib/multiif.h index cae02cb08c..0cb9d4f7f2 100644 --- a/contrib/libs/curl/lib/multiif.h +++ b/contrib/libs/curl/lib/multiif.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/netrc.c b/contrib/libs/curl/lib/netrc.c index e6a09b187a..4461b8492f 100644 --- a/contrib/libs/curl/lib/netrc.c +++ b/contrib/libs/curl/lib/netrc.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -244,7 +244,7 @@ static int parsenetrc(const char *host, } } /* while Curl_get_line() */ -out: + out: if(!retcode) { /* success */ if(login_alloc) { diff --git a/contrib/libs/curl/lib/netrc.h b/contrib/libs/curl/lib/netrc.h index 9f2815f3bb..53d0056721 100644 --- a/contrib/libs/curl/lib/netrc.h +++ b/contrib/libs/curl/lib/netrc.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/nonblock.c b/contrib/libs/curl/lib/nonblock.c index f4eb656128..ce73af31c1 100644 --- a/contrib/libs/curl/lib/nonblock.c +++ b/contrib/libs/curl/lib/nonblock.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -31,6 +31,9 @@ #include <fcntl.h> #endif +#if (defined(HAVE_IOCTL_FIONBIO) && defined(NETWARE)) +#include <sys/filio.h> +#endif #ifdef __VMS #include <in.h> #include <inet.h> diff --git a/contrib/libs/curl/lib/nonblock.h b/contrib/libs/curl/lib/nonblock.h index 4a1a6151f2..a42f443a49 100644 --- a/contrib/libs/curl/lib/nonblock.h +++ b/contrib/libs/curl/lib/nonblock.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/noproxy.c b/contrib/libs/curl/lib/noproxy.c index 2b9908d894..81f1e09934 100644 --- a/contrib/libs/curl/lib/noproxy.c +++ b/contrib/libs/curl/lib/noproxy.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -34,10 +34,6 @@ #include <netinet/in.h> #endif -#ifdef HAVE_ARPA_INET_H -#include <arpa/inet.h> -#endif - /* * Curl_cidr4_match() returns TRUE if the given IPv4 address is within the * specified CIDR address range. @@ -119,18 +115,8 @@ enum nametype { * Checks if the host is in the noproxy list. returns TRUE if it matches and * therefore the proxy should NOT be used. ****************************************************************/ -bool Curl_check_noproxy(const char *name, const char *no_proxy, - bool *spacesep) +bool Curl_check_noproxy(const char *name, const char *no_proxy) { - char hostip[128]; - *spacesep = FALSE; - /* - * If we don't have a hostname at all, like for example with a FILE - * transfer, we have nothing to interrogate the noproxy list with. - */ - if(!name || name[0] == '\0') - return FALSE; - /* no_proxy=domain1.dom,host.domain2.dom * (a comma-separated list of hosts which should * not be proxied, or an asterisk to override @@ -140,6 +126,7 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy, const char *p = no_proxy; size_t namelen; enum nametype type = TYPE_HOST; + char hostip[128]; if(!strcmp("*", no_proxy)) return TRUE; @@ -162,14 +149,9 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy, } else { unsigned int address; - namelen = strlen(name); if(1 == Curl_inet_pton(AF_INET, name, &address)) type = TYPE_IPV4; - else { - /* ignore trailing dots in the host name */ - if(name[namelen - 1] == '.') - namelen--; - } + namelen = strlen(name); } while(*p) { @@ -191,50 +173,33 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy, if(tokenlen) { switch(type) { case TYPE_HOST: - /* ignore trailing dots in the token to check */ - if(token[tokenlen - 1] == '.') - tokenlen--; - - if(tokenlen && (*token == '.')) { - /* ignore leading token dot as well */ - token++; - tokenlen--; + if(*token == '.') { + ++token; + --tokenlen; + /* tailmatch */ + match = (tokenlen <= namelen) && + strncasecompare(token, name + (namelen - tokenlen), namelen); } - /* A: example.com matches 'example.com' - B: www.example.com matches 'example.com' - C: nonexample.com DOES NOT match 'example.com' - */ - if(tokenlen == namelen) - /* case A, exact match */ - match = strncasecompare(token, name, namelen); - else if(tokenlen < namelen) { - /* case B, tailmatch domain */ - match = (name[namelen - tokenlen - 1] == '.') && - strncasecompare(token, name + (namelen - tokenlen), - tokenlen); - } - /* case C passes through, not a match */ + else + match = (tokenlen == namelen) && + strncasecompare(token, name, namelen); break; case TYPE_IPV4: /* FALLTHROUGH */ case TYPE_IPV6: { const char *check = token; - char *slash; + char *slash = strchr(check, '/'); unsigned int bits = 0; char checkip[128]; - if(tokenlen >= sizeof(checkip)) - /* this cannot match */ - break; - /* copy the check name to a temp buffer */ - memcpy(checkip, check, tokenlen); - checkip[tokenlen] = 0; - check = checkip; - - slash = strchr(check, '/'); /* if the slash is part of this token, use it */ - if(slash) { + if(slash && (slash < &check[tokenlen])) { bits = atoi(slash + 1); - *slash = 0; /* null terminate there */ + /* copy the check name to a temp buffer */ + if(tokenlen >= sizeof(checkip)) + break; + memcpy(checkip, check, tokenlen); + checkip[ slash - check ] = 0; + check = checkip; } if(type == TYPE_IPV6) match = Curl_cidr6_match(name, check, bits); @@ -246,15 +211,6 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy, if(match) return TRUE; } /* if(tokenlen) */ - /* pass blanks after pattern */ - while(ISBLANK(*p)) - p++; - /* if not a comma! */ - if(*p && (*p != ',')) { - *spacesep = TRUE; - continue; - } - /* pass any number of commas */ while(*p == ',') p++; } /* while(*p) */ diff --git a/contrib/libs/curl/lib/noproxy.h b/contrib/libs/curl/lib/noproxy.h index a3a6807722..8800a21276 100644 --- a/contrib/libs/curl/lib/noproxy.h +++ b/contrib/libs/curl/lib/noproxy.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,8 +37,7 @@ UNITTEST bool Curl_cidr6_match(const char *ipv6, unsigned int bits); #endif -bool Curl_check_noproxy(const char *name, const char *no_proxy, - bool *spacesep); +bool Curl_check_noproxy(const char *name, const char *no_proxy); #endif diff --git a/contrib/libs/curl/lib/openldap.c b/contrib/libs/curl/lib/openldap.c index fee353d9ba..7027c88054 100644 --- a/contrib/libs/curl/lib/openldap.c +++ b/contrib/libs/curl/lib/openldap.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) Howard Chu, <hyc@openldap.org> + * Copyright (C) 2011 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2010, Howard Chu, <hyc@openldap.org> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -47,7 +47,6 @@ #include "transfer.h" #include "curl_ldap.h" #include "curl_base64.h" -#include "cfilters.h" #include "connect.h" #include "curl_sasl.h" #include "strcase.h" @@ -295,7 +294,7 @@ static CURLcode oldap_parse_login_options(struct connectdata *conn) const char *value; while(*ptr && *ptr != '=') - ptr++; + ptr++; value = ptr + 1; @@ -501,7 +500,8 @@ static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate) struct ldapconninfo *li = conn->proto.ldapc; bool ssldone = 0; - result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); + result = Curl_ssl_connect_nonblocking(data, conn, FALSE, + FIRSTSOCKET, &ssldone); if(!result) { state(data, newstate); @@ -1153,7 +1153,7 @@ ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg) (void)arg; if(opt == LBER_SB_OPT_DATA_READY) { struct Curl_easy *data = sbiod->sbiod_pvt; - return Curl_conn_data_pending(data, FIRSTSOCKET); + return Curl_ssl_data_pending(data->conn, FIRSTSOCKET); } return 0; } diff --git a/contrib/libs/curl/lib/parsedate.c b/contrib/libs/curl/lib/parsedate.c index 1a7195b16a..5ed88195fb 100644 --- a/contrib/libs/curl/lib/parsedate.c +++ b/contrib/libs/curl/lib/parsedate.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -212,55 +212,56 @@ static int checkday(const char *check, size_t len) { int i; const char * const *what; + bool found = FALSE; if(len > 3) what = &weekday[0]; - else if(len == 3) - what = &Curl_wkday[0]; else - return -1; /* too short */ + what = &Curl_wkday[0]; for(i = 0; i<7; i++) { - size_t ilen = strlen(what[0]); - if((ilen == len) && - strncasecompare(check, what[0], len)) - return i; + if(strcasecompare(check, what[0])) { + found = TRUE; + break; + } what++; } - return -1; + return found?i:-1; } -static int checkmonth(const char *check, size_t len) +static int checkmonth(const char *check) { int i; - const char * const *what = &Curl_month[0]; - if(len != 3) - return -1; /* not a month */ + const char * const *what; + bool found = FALSE; + what = &Curl_month[0]; for(i = 0; i<12; i++) { - if(strncasecompare(check, what[0], 3)) - return i; + if(strcasecompare(check, what[0])) { + found = TRUE; + break; + } what++; } - return -1; /* return the offset or -1, no real offset is -1 */ + return found?i:-1; /* return the offset or -1, no real offset is -1 */ } /* return the time zone offset between GMT and the input one, in number of seconds or -1 if the timezone wasn't found/legal */ -static int checktz(const char *check, size_t len) +static int checktz(const char *check) { unsigned int i; - const struct tzinfo *what = tz; - if(len > 4) /* longer than any valid timezone */ - return -1; + const struct tzinfo *what; + bool found = FALSE; + what = tz; for(i = 0; i< sizeof(tz)/sizeof(tz[0]); i++) { - size_t ilen = strlen(what->name); - if((ilen == len) && - strncasecompare(check, what->name, len)) - return what->offset*60; + if(strcasecompare(check, what->name)) { + found = TRUE; + break; + } what++; } - return -1; + return found?what->offset*60:-1; } static void skip(const char **date) @@ -293,53 +294,6 @@ static time_t time2epoch(int sec, int min, int hour, + hour) * 60 + min) * 60 + sec; } -/* Returns the value of a single-digit or two-digit decimal number, return - then pointer to after the number. The 'date' pointer is known to point to a - digit. */ -static int oneortwodigit(const char *date, const char **endp) -{ - int num = date[0] - '0'; - if(ISDIGIT(date[1])) { - *endp = &date[2]; - return num*10 + (date[1] - '0'); - } - *endp = &date[1]; - return num; -} - - -/* HH:MM:SS or HH:MM and accept single-digits too */ -static bool match_time(const char *date, - int *h, int *m, int *s, char **endp) -{ - const char *p; - int hh, mm, ss = 0; - hh = oneortwodigit(date, &p); - if((hh < 24) && (*p == ':') && ISDIGIT(p[1])) { - mm = oneortwodigit(&p[1], &p); - if(mm < 60) { - if((*p == ':') && ISDIGIT(p[1])) { - ss = oneortwodigit(&p[1], &p); - if(ss <= 60) { - /* valid HH:MM:SS */ - goto match; - } - } - else { - /* valid HH:MM */ - goto match; - } - } - } - return FALSE; /* not a time string */ -match: - *h = hh; - *m = mm; - *s = ss; - *endp = (char *)p; - return TRUE; -} - /* * parsedate() * @@ -351,9 +305,6 @@ match: * PARSEDATE_SOONER - time underflow at the low end of time_t */ -/* Wednesday is the longest name this parser knows about */ -#define NAME_LEN 12 - static int parsedate(const char *date, time_t *output) { time_t t = 0; @@ -376,32 +327,32 @@ static int parsedate(const char *date, time_t *output) if(ISALPHA(*date)) { /* a name coming up */ - size_t len = 0; - const char *p = date; - while(ISALPHA(*p) && (len < NAME_LEN)) { - p++; - len++; + char buf[32]=""; + size_t len; + if(sscanf(date, "%31[ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz]", buf)) + len = strlen(buf); + else + len = 0; + + if(wdaynum == -1) { + wdaynum = checkday(buf, len); + if(wdaynum != -1) + found = TRUE; + } + if(!found && (monnum == -1)) { + monnum = checkmonth(buf); + if(monnum != -1) + found = TRUE; } - if(len != NAME_LEN) { - if(wdaynum == -1) { - wdaynum = checkday(date, len); - if(wdaynum != -1) - found = TRUE; - } - if(!found && (monnum == -1)) { - monnum = checkmonth(date, len); - if(monnum != -1) - found = TRUE; - } - - if(!found && (tzoff == -1)) { - /* this just must be a time zone string */ - tzoff = checktz(date, len); - if(tzoff != -1) - found = TRUE; - } + if(!found && (tzoff == -1)) { + /* this just must be a time zone string */ + tzoff = checktz(buf); + if(tzoff != -1) + found = TRUE; } + if(!found) return PARSEDATE_FAIL; /* bad string */ @@ -411,10 +362,18 @@ static int parsedate(const char *date, time_t *output) /* a digit */ int val; char *end; + int len = 0; if((secnum == -1) && - match_time(date, &hournum, &minnum, &secnum, &end)) { - /* time stamp */ - date = end; + (3 == sscanf(date, "%02d:%02d:%02d%n", + &hournum, &minnum, &secnum, &len))) { + /* time stamp! */ + date += len; + } + else if((secnum == -1) && + (2 == sscanf(date, "%02d:%02d%n", &hournum, &minnum, &len))) { + /* time stamp without seconds */ + date += len; + secnum = 0; } else { long lval; diff --git a/contrib/libs/curl/lib/parsedate.h b/contrib/libs/curl/lib/parsedate.h index 84c37f1675..4e4347754d 100644 --- a/contrib/libs/curl/lib/parsedate.h +++ b/contrib/libs/curl/lib/parsedate.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/pingpong.c b/contrib/libs/curl/lib/pingpong.c index f3f7cb93cb..d4e6be98c4 100644 --- a/contrib/libs/curl/lib/pingpong.c +++ b/contrib/libs/curl/lib/pingpong.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -28,7 +28,6 @@ #include "curl_setup.h" #include "urldata.h" -#include "cfilters.h" #include "sendf.h" #include "select.h" #include "progress.h" @@ -103,12 +102,12 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data, else interval_ms = 0; /* immediate */ - if(Curl_conn_data_pending(data, FIRSTSOCKET)) + if(Curl_ssl_data_pending(conn, FIRSTSOCKET)) rc = 1; else if(Curl_pp_moredata(pp)) /* We are receiving and there is data in the cache so just read it */ rc = 1; - else if(!pp->sendleft && Curl_conn_data_pending(data, FIRSTSOCKET)) + else if(!pp->sendleft && Curl_ssl_data_pending(conn, FIRSTSOCKET)) /* We are receiving and there is data ready in the SSL library */ rc = 1; else @@ -211,7 +210,7 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data, #ifdef HAVE_GSSAPI data_sec = conn->data_prot; DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST); - conn->data_prot = (unsigned char)data_sec; + conn->data_prot = data_sec; #endif Curl_debug(data, CURLINFO_HEADER_OUT, s, (size_t)bytes_written); @@ -316,7 +315,7 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data, &gotbytes); #ifdef HAVE_GSSAPI DEBUGASSERT(prot > PROT_NONE && prot < PROT_LAST); - conn->data_prot = (unsigned char)prot; + conn->data_prot = prot; #endif if(result == CURLE_AGAIN) return CURLE_OK; /* return */ diff --git a/contrib/libs/curl/lib/pingpong.h b/contrib/libs/curl/lib/pingpong.h index 80d3f7718c..cefae073a6 100644 --- a/contrib/libs/curl/lib/pingpong.h +++ b/contrib/libs/curl/lib/pingpong.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/pop3.c b/contrib/libs/curl/lib/pop3.c index ddb98bfdf4..3151a3f56a 100644 --- a/contrib/libs/curl/lib/pop3.c +++ b/contrib/libs/curl/lib/pop3.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -58,6 +58,11 @@ #include <inet.h> #endif +#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) +#undef in_addr_t +#define in_addr_t unsigned long +#endif + #include <curl/curl.h> #include "urldata.h" #include "sendf.h" @@ -71,7 +76,6 @@ #include "strtoofft.h" #include "strcase.h" #include "vtls/vtls.h" -#include "cfilters.h" #include "connect.h" #include "select.h" #include "multiif.h" @@ -282,11 +286,11 @@ static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out) /*********************************************************************** * - * pop3_state() + * state() * * This is the ONLY way to change POP3 state! */ -static void pop3_state(struct Curl_easy *data, pop3state newstate) +static void state(struct Curl_easy *data, pop3state newstate) { struct pop3_conn *pop3c = &data->conn->proto.pop3c; #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) @@ -335,7 +339,7 @@ static CURLcode pop3_perform_capa(struct Curl_easy *data, result = Curl_pp_sendf(data, &pop3c->pp, "%s", "CAPA"); if(!result) - pop3_state(data, POP3_CAPA); + state(data, POP3_CAPA); return result; } @@ -353,7 +357,7 @@ static CURLcode pop3_perform_starttls(struct Curl_easy *data, CURLcode result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", "STLS"); if(!result) - pop3_state(data, POP3_STARTTLS); + state(data, POP3_STARTTLS); return result; } @@ -369,28 +373,20 @@ static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data, { /* Start the SSL connection */ struct pop3_conn *pop3c = &conn->proto.pop3c; - CURLcode result; - bool ssldone = FALSE; - - if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); - if(result) - goto out; - } - - result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); + CURLcode result = + Curl_ssl_connect_nonblocking(data, conn, FALSE, FIRSTSOCKET, + &pop3c->ssldone); if(!result) { - pop3c->ssldone = ssldone; if(pop3c->state != POP3_UPGRADETLS) - pop3_state(data, POP3_UPGRADETLS); + state(data, POP3_UPGRADETLS); if(pop3c->ssldone) { pop3_to_pop3s(conn); result = pop3_perform_capa(data, conn); } } -out: + return result; } @@ -408,7 +404,7 @@ static CURLcode pop3_perform_user(struct Curl_easy *data, /* Check we have a username and password to authenticate with and end the connect phase if we don't */ if(!data->state.aptr.user) { - pop3_state(data, POP3_STOP); + state(data, POP3_STOP); return result; } @@ -417,7 +413,7 @@ static CURLcode pop3_perform_user(struct Curl_easy *data, result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "USER %s", conn->user ? conn->user : ""); if(!result) - pop3_state(data, POP3_USER); + state(data, POP3_USER); return result; } @@ -442,7 +438,7 @@ static CURLcode pop3_perform_apop(struct Curl_easy *data, /* Check we have a username and password to authenticate with and end the connect phase if we don't */ if(!data->state.aptr.user) { - pop3_state(data, POP3_STOP); + state(data, POP3_STOP); return result; } @@ -468,7 +464,7 @@ static CURLcode pop3_perform_apop(struct Curl_easy *data, result = Curl_pp_sendf(data, &pop3c->pp, "APOP %s %s", conn->user, secret); if(!result) - pop3_state(data, POP3_APOP); + state(data, POP3_APOP); return result; } @@ -552,7 +548,7 @@ static CURLcode pop3_perform_authentication(struct Curl_easy *data, /* Check we have enough data to authenticate with and end the connect phase if we don't */ if(!Curl_sasl_can_authenticate(&pop3c->sasl, data)) { - pop3_state(data, POP3_STOP); + state(data, POP3_STOP); return result; } @@ -562,7 +558,7 @@ static CURLcode pop3_perform_authentication(struct Curl_easy *data, if(!result) if(progress == SASL_INPROGRESS) - pop3_state(data, POP3_AUTH); + state(data, POP3_AUTH); } if(!result && progress == SASL_IDLE) { @@ -620,7 +616,7 @@ static CURLcode pop3_perform_command(struct Curl_easy *data) pop3->custom : command)); if(!result) - pop3_state(data, POP3_COMMAND); + state(data, POP3_COMMAND); return result; } @@ -638,7 +634,7 @@ static CURLcode pop3_perform_quit(struct Curl_easy *data, CURLcode result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", "QUIT"); if(!result) - pop3_state(data, POP3_QUIT); + state(data, POP3_QUIT); return result; } @@ -771,7 +767,7 @@ static CURLcode pop3_state_capa_resp(struct Curl_easy *data, int pop3code, if(pop3code != '+') pop3c->authtypes |= POP3_TYPE_CLEARTEXT; - if(!data->set.use_ssl || Curl_conn_is_ssl(conn, FIRSTSOCKET)) + if(!data->set.use_ssl || conn->ssl[FIRSTSOCKET].use) result = pop3_perform_authentication(data, conn); else if(pop3code == '+' && pop3c->tls_supported) /* Switch to TLS connection now */ @@ -831,7 +827,7 @@ static CURLcode pop3_state_auth_resp(struct Curl_easy *data, if(!result) switch(progress) { case SASL_DONE: - pop3_state(data, POP3_STOP); /* Authenticated */ + state(data, POP3_STOP); /* Authenticated */ break; case SASL_IDLE: /* No mechanism left after cancellation */ #ifndef CURL_DISABLE_CRYPTO_AUTH @@ -869,7 +865,7 @@ static CURLcode pop3_state_apop_resp(struct Curl_easy *data, int pop3code, } else /* End of connect phase */ - pop3_state(data, POP3_STOP); + state(data, POP3_STOP); return result; } @@ -892,7 +888,7 @@ static CURLcode pop3_state_user_resp(struct Curl_easy *data, int pop3code, result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "PASS %s", conn->passwd ? conn->passwd : ""); if(!result) - pop3_state(data, POP3_PASS); + state(data, POP3_PASS); return result; } @@ -910,7 +906,7 @@ static CURLcode pop3_state_pass_resp(struct Curl_easy *data, int pop3code, } else /* End of connect phase */ - pop3_state(data, POP3_STOP); + state(data, POP3_STOP); return result; } @@ -929,7 +925,7 @@ static CURLcode pop3_state_command_resp(struct Curl_easy *data, (void)instate; /* no use for this yet */ if(pop3code != '+') { - pop3_state(data, POP3_STOP); + state(data, POP3_STOP); return CURLE_WEIRD_SERVER_REPLY; } @@ -952,7 +948,7 @@ static CURLcode pop3_state_command_resp(struct Curl_easy *data, content so send it as such. Note that there may even be additional "headers" after the body */ - if(!data->req.no_body) { + if(!data->set.opt_no_body) { result = Curl_pop3_write(data, pp->cache, pp->cache_size); if(result) return result; @@ -967,7 +963,7 @@ static CURLcode pop3_state_command_resp(struct Curl_easy *data, } /* End of DO phase */ - pop3_state(data, POP3_STOP); + state(data, POP3_STOP); return result; } @@ -1037,12 +1033,12 @@ static CURLcode pop3_statemachine(struct Curl_easy *data, break; case POP3_QUIT: - pop3_state(data, POP3_STOP); + state(data, POP3_STOP); break; default: /* internal error */ - pop3_state(data, POP3_STOP); + state(data, POP3_STOP); break; } } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp)); @@ -1058,9 +1054,8 @@ static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done) struct pop3_conn *pop3c = &conn->proto.pop3c; if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) { - bool ssldone = FALSE; - result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); - pop3c->ssldone = ssldone; + result = Curl_ssl_connect_nonblocking(data, conn, FALSE, + FIRSTSOCKET, &pop3c->ssldone); if(result || !pop3c->ssldone) return result; } @@ -1143,7 +1138,7 @@ static CURLcode pop3_connect(struct Curl_easy *data, bool *done) return result; /* Start off waiting for the server greeting response */ - pop3_state(data, POP3_SERVERGREET); + state(data, POP3_SERVERGREET); result = pop3_multi_statemach(data, done); @@ -1197,11 +1192,12 @@ static CURLcode pop3_perform(struct Curl_easy *data, bool *connected, { /* This is POP3 and no proxy */ CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; struct POP3 *pop3 = data->req.p.pop3; DEBUGF(infof(data, "DO phase starts")); - if(data->req.no_body) { + if(data->set.opt_no_body) { /* Requested no body means no transfer */ pop3->transfer = PPTRANSFER_INFO; } @@ -1215,7 +1211,7 @@ static CURLcode pop3_perform(struct Curl_easy *data, bool *connected, /* Run the state-machine */ result = pop3_multi_statemach(data, dophase_done); - *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); + *connected = conn->bits.tcpconnect[FIRSTSOCKET]; if(*dophase_done) DEBUGF(infof(data, "DO phase is complete")); @@ -1376,7 +1372,7 @@ static CURLcode pop3_parse_url_options(struct connectdata *conn) const char *value; while(*ptr && *ptr != '=') - ptr++; + ptr++; value = ptr + 1; diff --git a/contrib/libs/curl/lib/pop3.h b/contrib/libs/curl/lib/pop3.h index 83f0f831e6..bb0645f9bf 100644 --- a/contrib/libs/curl/lib/pop3.h +++ b/contrib/libs/curl/lib/pop3.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2009 - 2022, 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 @@ -62,16 +62,16 @@ struct POP3 { struct pop3_conn { struct pingpong pp; pop3state state; /* Always use pop3.c:state() to change state! */ + bool ssldone; /* Is connect() over SSL done? */ + bool tls_supported; /* StartTLS capability supported by server */ size_t eob; /* Number of bytes of the EOB (End Of Body) that have been received so far */ size_t strip; /* Number of bytes from the start to ignore as non-body */ struct SASL sasl; /* SASL-related storage */ + unsigned int authtypes; /* Accepted authentication types */ + unsigned int preftype; /* Preferred authentication type */ char *apoptimestamp; /* APOP timestamp from the server greeting */ - unsigned char authtypes; /* Accepted authentication types */ - unsigned char preftype; /* Preferred authentication type */ - BIT(ssldone); /* Is connect() over SSL done? */ - BIT(tls_supported); /* StartTLS capability supported by server */ }; extern const struct Curl_handler Curl_handler_pop3; @@ -84,7 +84,7 @@ extern const struct Curl_handler Curl_handler_pop3s; /* Authentication type values */ #define POP3_TYPE_NONE 0 -#define POP3_TYPE_ANY (POP3_TYPE_CLEARTEXT|POP3_TYPE_APOP|POP3_TYPE_SASL) +#define POP3_TYPE_ANY ~0U /* This is the 5-bytes End-Of-Body marker for POP3 */ #define POP3_EOB "\x0d\x0a\x2e\x0d\x0a" diff --git a/contrib/libs/curl/lib/progress.c b/contrib/libs/curl/lib/progress.c index 6092b782c7..4a1e1daa81 100644 --- a/contrib/libs/curl/lib/progress.c +++ b/contrib/libs/curl/lib/progress.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -87,6 +87,8 @@ static char *max5data(curl_off_t bytes, char *max5) CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE, (bytes%ONE_MEGABYTE) / (ONE_MEGABYTE/CURL_OFF_T_C(10)) ); +#if (SIZEOF_CURL_OFF_T > 4) + else if(bytes < CURL_OFF_T_C(10000) * ONE_MEGABYTE) /* 'XXXXM' is good until we're at 10000MB or above */ msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE); @@ -109,8 +111,15 @@ static char *max5data(curl_off_t bytes, char *max5) /* up to 10000PB, display without decimal: XXXXP */ msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "P", bytes/ONE_PETABYTE); - /* 16384 petabytes (16 exabytes) is the maximum a 64 bit unsigned number can - hold, but our data type is signed so 8192PB will be the maximum. */ + /* 16384 petabytes (16 exabytes) is the maximum a 64 bit unsigned number + can hold, but our data type is signed so 8192PB will be the maximum. */ + +#else + + else + msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE); + +#endif return max5; } @@ -157,11 +166,14 @@ void Curl_pgrsResetTransferSizes(struct Curl_easy *data) /* * - * Curl_pgrsTimeWas(). Store the timestamp time at the given label. + * Curl_pgrsTime(). Store the current time at the given label. This fetches a + * fresh "now" and returns it. + * + * @unittest: 1399 */ -void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, - struct curltime timestamp) +struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer) { + struct curltime now = Curl_now(); timediff_t *delta = NULL; switch(timer) { @@ -171,15 +183,15 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, break; case TIMER_STARTOP: /* This is set at the start of a transfer */ - data->progress.t_startop = timestamp; + data->progress.t_startop = now; break; case TIMER_STARTSINGLE: /* This is set at the start of each single fetch */ - data->progress.t_startsingle = timestamp; + data->progress.t_startsingle = now; data->progress.is_t_startransfer_set = false; break; case TIMER_STARTACCEPT: - data->progress.t_acceptdata = timestamp; + data->progress.t_acceptdata = now; break; case TIMER_NAMELOOKUP: delta = &data->progress.t_nslookup; @@ -202,7 +214,7 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, * changing the t_starttransfer time. */ if(data->progress.is_t_startransfer_set) { - return; + return now; } else { data->progress.is_t_startransfer_set = true; @@ -212,30 +224,15 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, /* this is the normal end-of-transfer thing */ break; case TIMER_REDIRECT: - data->progress.t_redirect = Curl_timediff_us(timestamp, - data->progress.start); + data->progress.t_redirect = Curl_timediff_us(now, data->progress.start); break; } if(delta) { - timediff_t us = Curl_timediff_us(timestamp, data->progress.t_startsingle); + timediff_t us = Curl_timediff_us(now, data->progress.t_startsingle); if(us < 1) us = 1; /* make sure at least one microsecond passed */ *delta += us; } -} - -/* - * - * Curl_pgrsTime(). Store the current time at the given label. This fetches a - * fresh "now" and returns it. - * - * @unittest: 1399 - */ -struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer) -{ - struct curltime now = Curl_now(); - - Curl_pgrsTimeWas(data, timer, now); return now; } diff --git a/contrib/libs/curl/lib/progress.h b/contrib/libs/curl/lib/progress.h index 0049cd04be..a129315147 100644 --- a/contrib/libs/curl/lib/progress.h +++ b/contrib/libs/curl/lib/progress.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -57,13 +57,6 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize, curl_off_t limit, struct curltime start, struct curltime now); -/** - * Update progress timer with the elapsed time from its start to `timestamp`. - * This allows updating timers later and is used by happy eyeballing, where - * we only want to record the winner's times. - */ -void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, - struct curltime timestamp); #define PGRS_HIDE (1<<4) #define PGRS_UL_SIZE_KNOWN (1<<5) diff --git a/contrib/libs/curl/lib/psl.c b/contrib/libs/curl/lib/psl.c index 626a203a6d..60c98a4ca4 100644 --- a/contrib/libs/curl/lib/psl.c +++ b/contrib/libs/curl/lib/psl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/psl.h b/contrib/libs/curl/lib/psl.h index b9b1dda941..5f290ca1cf 100644 --- a/contrib/libs/curl/lib/psl.h +++ b/contrib/libs/curl/lib/psl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/quic.h b/contrib/libs/curl/lib/quic.h new file mode 100644 index 0000000000..6d0a464e03 --- /dev/null +++ b/contrib/libs/curl/lib/quic.h @@ -0,0 +1,68 @@ +#ifndef HEADER_CURL_QUIC_H +#define HEADER_CURL_QUIC_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef ENABLE_QUIC +#ifdef USE_NGTCP2 +#error #include "vquic/ngtcp2.h" +#endif +#ifdef USE_QUICHE +#error #include "vquic/quiche.h" +#endif +#ifdef USE_MSH3 +#error #include "vquic/msh3.h" +#endif + +#include "urldata.h" + +/* functions provided by the specific backends */ +CURLcode Curl_quic_connect(struct Curl_easy *data, + struct connectdata *conn, + curl_socket_t sockfd, + int sockindex, + const struct sockaddr *addr, + socklen_t addrlen); +CURLcode Curl_quic_is_connected(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + bool *connected); +void Curl_quic_ver(char *p, size_t len); +CURLcode Curl_quic_done_sending(struct Curl_easy *data); +void Curl_quic_done(struct Curl_easy *data, bool premature); +bool Curl_quic_data_pending(const struct Curl_easy *data); +void Curl_quic_disconnect(struct Curl_easy *data, + struct connectdata *conn, int tempindex); +CURLcode Curl_quic_idle(struct Curl_easy *data); + +#else /* ENABLE_QUIC */ +#define Curl_quic_done_sending(x) +#define Curl_quic_done(x,y) +#define Curl_quic_data_pending(x) +#define Curl_quic_disconnect(x,y,z) +#endif /* !ENABLE_QUIC */ + +#endif /* HEADER_CURL_QUIC_H */ diff --git a/contrib/libs/curl/lib/rand.c b/contrib/libs/curl/lib/rand.c index 7d24765ccd..2e7e7e8238 100644 --- a/contrib/libs/curl/lib/rand.c +++ b/contrib/libs/curl/lib/rand.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -27,18 +27,10 @@ #ifdef HAVE_FCNTL_H #include <fcntl.h> #endif -#ifdef HAVE_ARPA_INET_H -#include <arpa/inet.h> -#endif -#ifdef HAVE_ARC4RANDOM -/* Some platforms might have the prototype missing (ubuntu + libressl) */ -uint32_t arc4random(void); -#endif #include <curl/curl.h> #include "vtls/vtls.h" #include "sendf.h" -#include "timeval.h" #include "rand.h" /* The last 3 #include files should be in this order */ @@ -147,11 +139,6 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd) } #endif -#ifdef HAVE_ARC4RANDOM - *rnd = (unsigned int)arc4random(); - return CURLE_OK; -#endif - #if defined(RANDOM_FILE) && !defined(WIN32) if(!seeded) { /* if there's a random file to read a seed from, use it */ @@ -183,8 +170,8 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd) } /* - * Curl_rand() stores 'num' number of random unsigned characters in the buffer - * 'rnd' points to. + * Curl_rand() stores 'num' number of random unsigned integers in the buffer + * 'rndptr' points to. * * If libcurl is built without TLS support or with a TLS backend that lacks a * proper random API (rustls, Gskit or mbedTLS), this function will use "weak" diff --git a/contrib/libs/curl/lib/rand.h b/contrib/libs/curl/lib/rand.h index cbe05677a1..30fc29615a 100644 --- a/contrib/libs/curl/lib/rand.h +++ b/contrib/libs/curl/lib/rand.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/rename.c b/contrib/libs/curl/lib/rename.c index 97a66e947e..cfb3699fb7 100644 --- a/contrib/libs/curl/lib/rename.c +++ b/contrib/libs/curl/lib/rename.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2020 - 2022, 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 diff --git a/contrib/libs/curl/lib/rename.h b/contrib/libs/curl/lib/rename.h index 04440820c5..9958e2cd23 100644 --- a/contrib/libs/curl/lib/rename.h +++ b/contrib/libs/curl/lib/rename.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2020 - 2022, 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 diff --git a/contrib/libs/curl/lib/rtsp.c b/contrib/libs/curl/lib/rtsp.c index ccd7264b00..6d3bf97e6c 100644 --- a/contrib/libs/curl/lib/rtsp.c +++ b/contrib/libs/curl/lib/rtsp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -38,13 +38,14 @@ #include "strcase.h" #include "select.h" #include "connect.h" -#include "cfilters.h" #include "strdup.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" +#define RTP_PKT_CHANNEL(p) ((int)((unsigned char)((p)[1]))) + #define RTP_PKT_LENGTH(p) ((((int)((unsigned char)((p)[2]))) << 8) | \ ((int)((unsigned char)((p)[3])))) @@ -89,8 +90,6 @@ static int rtsp_getsock_do(struct Curl_easy *data, struct connectdata *conn, static CURLcode rtp_client_write(struct Curl_easy *data, char *ptr, size_t len); -static -CURLcode rtsp_parse_transport(struct Curl_easy *data, char *transport); /* @@ -119,7 +118,6 @@ const struct Curl_handler Curl_handler_rtsp = { PROTOPT_NONE /* flags */ }; -#define MAX_RTP_BUFFERSIZE 1000000 /* arbitrary */ static CURLcode rtsp_setup_connection(struct Curl_easy *data, struct connectdata *conn) @@ -131,12 +129,41 @@ static CURLcode rtsp_setup_connection(struct Curl_easy *data, if(!rtsp) return CURLE_OUT_OF_MEMORY; - Curl_dyn_init(&conn->proto.rtspc.buf, MAX_RTP_BUFFERSIZE); return CURLE_OK; } /* + * The server may send us RTP data at any point, and RTSPREQ_RECEIVE does not + * want to block the application forever while receiving a stream. Therefore, + * we cannot assume that an RTSP socket is dead just because it is readable. + * + * Instead, if it is readable, run Curl_connalive() to peek at the socket + * and distinguish between closed and data. + */ +static bool rtsp_connisdead(struct connectdata *check) +{ + int sval; + bool ret_val = TRUE; + + sval = SOCKET_READABLE(check->sock[FIRSTSOCKET], 0); + if(sval == 0) { + /* timeout */ + ret_val = FALSE; + } + else if(sval & CURL_CSELECT_ERR) { + /* socket is in an error state */ + ret_val = TRUE; + } + else if(sval & CURL_CSELECT_IN) { + /* readable with no error. could still be closed */ + ret_val = !Curl_connalive(check); + } + + return ret_val; +} + +/* * Function to check on various aspects of a connection. */ static unsigned int rtsp_conncheck(struct Curl_easy *data, @@ -147,8 +174,7 @@ static unsigned int rtsp_conncheck(struct Curl_easy *data, (void)data; if(checks_to_perform & CONNCHECK_ISDEAD) { - bool input_pending; - if(!Curl_conn_is_alive(data, conn, &input_pending)) + if(rtsp_connisdead(conn)) ret_val |= CONNRESULT_DEAD; } @@ -178,7 +204,7 @@ static CURLcode rtsp_disconnect(struct Curl_easy *data, { (void) dead; (void) data; - Curl_dyn_free(&conn->proto.rtspc.buf); + Curl_safefree(conn->proto.rtspc.rtp_buf); return CURLE_OK; } @@ -206,7 +232,7 @@ static CURLcode rtsp_done(struct Curl_easy *data, return CURLE_RTSP_CSEQ_ERROR; } if(data->set.rtspreq == RTSPREQ_RECEIVE && - (data->conn->proto.rtspc.rtp_channel == -1)) { + (data->conn->proto.rtspc.rtp_channel == -1)) { infof(data, "Got an RTP Receive with a CSeq of %ld", CSeq_recv); } } @@ -241,23 +267,11 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq; rtsp->CSeq_recv = 0; - /* Setup the first_* fields to allow auth details get sent - to this origin */ - - if(!data->state.first_host) { - data->state.first_host = strdup(conn->host.name); - if(!data->state.first_host) - return CURLE_OUT_OF_MEMORY; - - data->state.first_remote_port = conn->remote_port; - data->state.first_remote_protocol = conn->handler->protocol; - } - /* Setup the 'p_request' pointer to the proper p_request string * Since all RTSP requests are included here, there is no need to * support custom requests like HTTP. **/ - data->req.no_body = TRUE; /* most requests don't contain a body */ + data->set.opt_no_body = TRUE; /* most requests don't contain a body */ switch(rtspreq) { default: failf(data, "Got invalid RTSP request"); @@ -267,7 +281,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) break; case RTSPREQ_DESCRIBE: p_request = "DESCRIBE"; - data->req.no_body = FALSE; + data->set.opt_no_body = FALSE; break; case RTSPREQ_ANNOUNCE: p_request = "ANNOUNCE"; @@ -287,7 +301,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) case RTSPREQ_GET_PARAMETER: /* GET_PARAMETER's no_body status is determined later */ p_request = "GET_PARAMETER"; - data->req.no_body = FALSE; + data->set.opt_no_body = FALSE; break; case RTSPREQ_SET_PARAMETER: p_request = "SET_PARAMETER"; @@ -297,8 +311,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) break; case RTSPREQ_RECEIVE: p_request = ""; - /* Treat interleaved RTP as body */ - data->req.no_body = FALSE; + /* Treat interleaved RTP as body*/ + data->set.opt_no_body = FALSE; break; case RTSPREQ_LAST: failf(data, "Got invalid RTSP request: RTSPREQ_LAST"); @@ -376,6 +390,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) if(Curl_checkheaders(data, STRCONST("User-Agent")) && data->state.aptr.uagent) { Curl_safefree(data->state.aptr.uagent); + data->state.aptr.uagent = NULL; } else if(!Curl_checkheaders(data, STRCONST("User-Agent")) && data->set.str[STRING_USERAGENT]) { @@ -395,6 +410,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) Curl_safefree(data->state.aptr.ref); if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer); + else + data->state.aptr.ref = NULL; p_referrer = data->state.aptr.ref; @@ -475,6 +492,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) * with basic and digest, it will be freed anyway by the next request */ Curl_safefree(data->state.aptr.userpwd); + data->state.aptr.userpwd = NULL; if(result) return result; @@ -493,7 +511,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) rtspreq == RTSPREQ_SET_PARAMETER || rtspreq == RTSPREQ_GET_PARAMETER) { - if(data->state.upload) { + if(data->set.upload) { putsize = data->state.infilesize; data->state.httpreq = HTTPREQ_PUT; @@ -512,7 +530,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) result = Curl_dyn_addf(&req_buffer, "Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n", - (data->state.upload ? putsize : postsize)); + (data->set.upload ? putsize : postsize)); if(result) return result; } @@ -543,7 +561,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) else if(rtspreq == RTSPREQ_GET_PARAMETER) { /* Check for an empty GET_PARAMETER (heartbeat) request */ data->state.httpreq = HTTPREQ_HEAD; - data->req.no_body = TRUE; + data->set.opt_no_body = TRUE; } } @@ -562,7 +580,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) } /* issue the request */ - result = Curl_buffer_send(&req_buffer, data, data->req.p.http, + result = Curl_buffer_send(&req_buffer, data, &data->info.request_size, 0, FIRSTSOCKET); if(result) { failf(data, "Failed sending RTSP request"); @@ -592,20 +610,26 @@ static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data, bool *readmore) { struct SingleRequest *k = &data->req; struct rtsp_conn *rtspc = &(conn->proto.rtspc); - unsigned char *rtp_channel_mask = data->state.rtp_channel_mask; char *rtp; /* moving pointer to rtp data */ ssize_t rtp_dataleft; /* how much data left to parse in this round */ + char *scratch; CURLcode result; - bool interleaved = false; - size_t skip_size = 0; - if(Curl_dyn_len(&rtspc->buf)) { - /* There was some leftover data the last time. Append new buffers */ - if(Curl_dyn_addn(&rtspc->buf, k->str, *nread)) + if(rtspc->rtp_buf) { + /* There was some leftover data the last time. Merge buffers */ + char *newptr = Curl_saferealloc(rtspc->rtp_buf, + rtspc->rtp_bufsize + *nread); + if(!newptr) { + rtspc->rtp_buf = NULL; + rtspc->rtp_bufsize = 0; return CURLE_OUT_OF_MEMORY; - rtp = Curl_dyn_ptr(&rtspc->buf); - rtp_dataleft = Curl_dyn_len(&rtspc->buf); + } + rtspc->rtp_buf = newptr; + memcpy(rtspc->rtp_buf + rtspc->rtp_bufsize, k->str, *nread); + rtspc->rtp_bufsize += *nread; + rtp = rtspc->rtp_buf; + rtp_dataleft = rtspc->rtp_bufsize; } else { /* Just parse the request buffer directly */ @@ -613,107 +637,71 @@ static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data, rtp_dataleft = *nread; } - while(rtp_dataleft > 0) { - if(rtp[0] == '$') { - if(rtp_dataleft > 4) { - unsigned char rtp_channel; - int rtp_length; - int idx; - int off; - - /* Parse the header */ - /* The channel identifier immediately follows and is 1 byte */ - rtp_channel = (unsigned char)rtp[1]; - idx = rtp_channel / 8; - off = rtp_channel % 8; - if(!(rtp_channel_mask[idx] & (1 << off))) { - /* invalid channel number, maybe not an RTP packet */ - rtp++; - rtp_dataleft--; - skip_size++; - continue; - } - if(skip_size > 0) { - DEBUGF(infof(data, "Skip the malformed interleaved data %lu " - "bytes", skip_size)); - } - skip_size = 0; - rtspc->rtp_channel = rtp_channel; + while((rtp_dataleft > 0) && + (rtp[0] == '$')) { + if(rtp_dataleft > 4) { + int rtp_length; - /* The length is two bytes */ - rtp_length = RTP_PKT_LENGTH(rtp); + /* Parse the header */ + /* The channel identifier immediately follows and is 1 byte */ + rtspc->rtp_channel = RTP_PKT_CHANNEL(rtp); - if(rtp_dataleft < rtp_length + 4) { - /* Need more - incomplete payload */ - *readmore = TRUE; - break; - } - interleaved = true; - /* We have the full RTP interleaved packet - * Write out the header including the leading '$' */ - DEBUGF(infof(data, "RTP write channel %d rtp_length %d", - rtspc->rtp_channel, rtp_length)); - result = rtp_client_write(data, &rtp[0], rtp_length + 4); - if(result) { - *readmore = FALSE; - return result; - } + /* The length is two bytes */ + rtp_length = RTP_PKT_LENGTH(rtp); - /* Move forward in the buffer */ - rtp_dataleft -= rtp_length + 4; - rtp += rtp_length + 4; - - if(data->set.rtspreq == RTSPREQ_RECEIVE) { - /* If we are in a passive receive, give control back - * to the app as often as we can. - */ - k->keepon &= ~KEEP_RECV; - } - } - else { - /* Need more - incomplete header */ + if(rtp_dataleft < rtp_length + 4) { + /* Need more - incomplete payload*/ *readmore = TRUE; break; } + /* We have the full RTP interleaved packet + * Write out the header including the leading '$' */ + DEBUGF(infof(data, "RTP write channel %d rtp_length %d", + rtspc->rtp_channel, rtp_length)); + result = rtp_client_write(data, &rtp[0], rtp_length + 4); + if(result) { + failf(data, "Got an error writing an RTP packet"); + *readmore = FALSE; + Curl_safefree(rtspc->rtp_buf); + rtspc->rtp_buf = NULL; + rtspc->rtp_bufsize = 0; + return result; + } + + /* Move forward in the buffer */ + rtp_dataleft -= rtp_length + 4; + rtp += rtp_length + 4; + + if(data->set.rtspreq == RTSPREQ_RECEIVE) { + /* If we are in a passive receive, give control back + * to the app as often as we can. + */ + k->keepon &= ~KEEP_RECV; + } } else { - /* If the following data begins with 'RTSP/', which might be an RTSP - message, we should stop skipping the data. */ - /* If `k-> headerline> 0 && !interleaved` is true, we are maybe in the - middle of an RTSP message. It is difficult to determine this, so we - stop skipping. */ - size_t prefix_len = (rtp_dataleft < 5) ? rtp_dataleft : 5; - if((k->headerline > 0 && !interleaved) || - strncmp(rtp, "RTSP/", prefix_len) == 0) { - if(skip_size > 0) { - DEBUGF(infof(data, "Skip the malformed interleaved data %lu " - "bytes", skip_size)); - } - break; /* maybe is an RTSP message */ - } - /* Skip incorrect data util the next RTP packet or RTSP message */ - do { - rtp++; - rtp_dataleft--; - skip_size++; - } while(rtp_dataleft > 0 && rtp[0] != '$' && rtp[0] != 'R'); + /* Need more - incomplete header */ + *readmore = TRUE; + break; } } if(rtp_dataleft && rtp[0] == '$') { DEBUGF(infof(data, "RTP Rewinding %zd %s", rtp_dataleft, - *readmore ? "(READMORE)" : "")); + *readmore ? "(READMORE)" : "")); /* Store the incomplete RTP packet for a "rewind" */ - if(!Curl_dyn_len(&rtspc->buf)) { - /* nothing was stored, add this data */ - if(Curl_dyn_addn(&rtspc->buf, rtp, rtp_dataleft)) - return CURLE_OUT_OF_MEMORY; - } - else { - /* keep the remainder */ - Curl_dyn_tail(&rtspc->buf, rtp_dataleft); + scratch = malloc(rtp_dataleft); + if(!scratch) { + Curl_safefree(rtspc->rtp_buf); + rtspc->rtp_buf = NULL; + rtspc->rtp_bufsize = 0; + return CURLE_OUT_OF_MEMORY; } + memcpy(scratch, rtp, rtp_dataleft); + Curl_safefree(rtspc->rtp_buf); + rtspc->rtp_buf = scratch; + rtspc->rtp_bufsize = rtp_dataleft; /* As far as the transfer is concerned, this data is consumed */ *nread = 0; @@ -722,10 +710,20 @@ static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data, /* Fix up k->str to point just after the last RTP packet */ k->str += *nread - rtp_dataleft; + /* either all of the data has been read or... + * rtp now points at the next byte to parse + */ + if(rtp_dataleft > 0) + DEBUGASSERT(k->str[0] == rtp[0]); + + DEBUGASSERT(rtp_dataleft <= *nread); /* sanity check */ + *nread = rtp_dataleft; /* If we get here, we have finished with the leftover/merge buffer */ - Curl_dyn_free(&rtspc->buf); + Curl_safefree(rtspc->rtp_buf); + rtspc->rtp_buf = NULL; + rtspc->rtp_bufsize = 0; return CURLE_OK; } @@ -774,14 +772,12 @@ CURLcode rtp_client_write(struct Curl_easy *data, char *ptr, size_t len) CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header) { + long CSeq = 0; + if(checkprefix("CSeq:", header)) { - long CSeq = 0; - char *endp; - char *p = &header[5]; - while(ISBLANK(*p)) - p++; - CSeq = strtol(p, &endp, 10); - if(p != endp) { + /* Store the received CSeq. Match is verified in rtsp_done */ + int nc = sscanf(&header[4], ": %ld", &CSeq); + if(nc == 1) { struct RTSP *rtsp = data->req.p.rtsp; rtsp->CSeq_recv = CSeq; /* mark the request */ data->state.rtsp_CSeq_recv = CSeq; /* update the handle */ @@ -840,63 +836,7 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header) (data->set.str[STRING_RTSP_SESSION_ID])[idlen] = '\0'; } } - else if(checkprefix("Transport:", header)) { - CURLcode result; - result = rtsp_parse_transport(data, header + 10); - if(result) - return result; - } - return CURLE_OK; -} - -static -CURLcode rtsp_parse_transport(struct Curl_easy *data, char *transport) -{ - /* If we receive multiple Transport response-headers, the linterleaved - channels of each response header is recorded and used together for - subsequent data validity checks.*/ - /* e.g.: ' RTP/AVP/TCP;unicast;interleaved=5-6' */ - char *start; - char *end; - start = transport; - while(start && *start) { - while(*start && ISBLANK(*start) ) - start++; - end = strchr(start, ';'); - if(checkprefix("interleaved=", start)) { - long chan1, chan2, chan; - char *endp; - char *p = start + 12; - chan1 = strtol(p, &endp, 10); - if(p != endp && chan1 >= 0 && chan1 <= 255) { - unsigned char *rtp_channel_mask = data->state.rtp_channel_mask; - chan2 = chan1; - if(*endp == '-') { - p = endp + 1; - chan2 = strtol(p, &endp, 10); - if(p == endp || chan2 < 0 || chan2 > 255) { - infof(data, "Unable to read the interleaved parameter from " - "Transport header: [%s]", transport); - chan2 = chan1; - } - } - for(chan = chan1; chan <= chan2; chan++) { - long idx = chan / 8; - long off = chan % 8; - rtp_channel_mask[idx] |= (unsigned char)(1 << off); - } - } - else { - infof(data, "Unable to read the interleaved parameter from " - "Transport header: [%s]", transport); - } - break; - } - /* skip to next parameter */ - start = (!end) ? end : (end + 1); - } return CURLE_OK; } - #endif /* CURL_DISABLE_RTSP or using Hyper */ diff --git a/contrib/libs/curl/lib/rtsp.h b/contrib/libs/curl/lib/rtsp.h index 111bac2a61..377c828605 100644 --- a/contrib/libs/curl/lib/rtsp.h +++ b/contrib/libs/curl/lib/rtsp.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -45,7 +45,8 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header); * Currently, only used for tracking incomplete RTP data reads */ struct rtsp_conn { - struct dynbuf buf; + char *rtp_buf; + ssize_t rtp_bufsize; int rtp_channel; }; @@ -61,7 +62,7 @@ struct RTSP { * HTTP functions can safely treat this as an HTTP struct, but RTSP aware * functions can also index into the later elements. */ - struct HTTP http_wrapper; /* wrap HTTP to do the heavy lifting */ + struct HTTP http_wrapper; /*wrap HTTP to do the heavy lifting */ long CSeq_sent; /* CSeq of this request */ long CSeq_recv; /* CSeq received */ diff --git a/contrib/libs/curl/lib/select.c b/contrib/libs/curl/lib/select.c index cae9beb6c7..2ac0746772 100644 --- a/contrib/libs/curl/lib/select.c +++ b/contrib/libs/curl/lib/select.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -61,8 +61,8 @@ * for the intended use of this function in the library. * * Return values: - * -1 = system call error, or invalid timeout value - * 0 = specified timeout has elapsed, or interrupted + * -1 = system call error, invalid timeout value, or interrupted + * 0 = specified timeout has elapsed */ int Curl_wait_ms(timediff_t timeout_ms) { @@ -99,13 +99,8 @@ int Curl_wait_ms(timediff_t timeout_ms) } #endif /* HAVE_POLL_FINE */ #endif /* USE_WINSOCK */ - if(r) { - if((r == -1) && (SOCKERRNO == EINTR)) - /* make EINTR from select or poll not a "lethal" error */ - r = 0; - else - r = -1; - } + if(r) + r = -1; return r; } @@ -235,14 +230,14 @@ int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */ if(readfd0 != CURL_SOCKET_BAD) { if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP)) r |= CURL_CSELECT_IN; - if(pfd[num].revents & (POLLPRI|POLLNVAL)) + if(pfd[num].revents & (POLLRDBAND|POLLPRI|POLLNVAL)) r |= CURL_CSELECT_ERR; num++; } if(readfd1 != CURL_SOCKET_BAD) { if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP)) r |= CURL_CSELECT_IN2; - if(pfd[num].revents & (POLLPRI|POLLNVAL)) + if(pfd[num].revents & (POLLRDBAND|POLLPRI|POLLNVAL)) r |= CURL_CSELECT_ERR; num++; } diff --git a/contrib/libs/curl/lib/select.h b/contrib/libs/curl/lib/select.h index 5b1ca23eb1..f2cf8bbd9f 100644 --- a/contrib/libs/curl/lib/select.h +++ b/contrib/libs/curl/lib/select.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/sendf.c b/contrib/libs/curl/lib/sendf.c index 437fa74b66..d26b7e7cd7 100644 --- a/contrib/libs/curl/lib/sendf.c +++ b/contrib/libs/curl/lib/sendf.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -38,7 +38,6 @@ #include "urldata.h" #include "sendf.h" -#include "cfilters.h" #include "connect.h" #include "vtls/vtls.h" #include "vssh/ssh.h" @@ -138,6 +137,151 @@ static size_t convert_lineends(struct Curl_easy *data, } #endif /* CURL_DO_LINEEND_CONV && !CURL_DISABLE_FTP */ +#ifdef USE_RECV_BEFORE_SEND_WORKAROUND +bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex) +{ + struct postponed_data * const psnd = &(conn->postponed[sockindex]); + return psnd->buffer && psnd->allocated_size && + psnd->recv_size > psnd->recv_processed; +} + +static CURLcode pre_receive_plain(struct Curl_easy *data, + struct connectdata *conn, int num) +{ + const curl_socket_t sockfd = conn->sock[num]; + struct postponed_data * const psnd = &(conn->postponed[num]); + size_t bytestorecv = psnd->allocated_size - psnd->recv_size; + /* WinSock will destroy unread received data if send() is + failed. + To avoid lossage of received data, recv() must be + performed before every send() if any incoming data is + available. However, skip this, if buffer is already full. */ + if((conn->handler->protocol&PROTO_FAMILY_HTTP) != 0 && + conn->recv[num] == Curl_recv_plain && + (!psnd->buffer || bytestorecv)) { + const int readymask = Curl_socket_check(sockfd, CURL_SOCKET_BAD, + CURL_SOCKET_BAD, 0); + if(readymask != -1 && (readymask & CURL_CSELECT_IN) != 0) { + /* Have some incoming data */ + if(!psnd->buffer) { + /* Use buffer double default size for intermediate buffer */ + psnd->allocated_size = 2 * data->set.buffer_size; + psnd->buffer = malloc(psnd->allocated_size); + if(!psnd->buffer) + return CURLE_OUT_OF_MEMORY; + psnd->recv_size = 0; + psnd->recv_processed = 0; +#ifdef DEBUGBUILD + psnd->bindsock = sockfd; /* Used only for DEBUGASSERT */ +#endif /* DEBUGBUILD */ + bytestorecv = psnd->allocated_size; + } + if(psnd->buffer) { + ssize_t recvedbytes; + DEBUGASSERT(psnd->bindsock == sockfd); + recvedbytes = sread(sockfd, psnd->buffer + psnd->recv_size, + bytestorecv); + if(recvedbytes > 0) + psnd->recv_size += recvedbytes; + } + else + psnd->allocated_size = 0; + } + } + return CURLE_OK; +} + +static ssize_t get_pre_recved(struct connectdata *conn, int num, char *buf, + size_t len) +{ + struct postponed_data * const psnd = &(conn->postponed[num]); + size_t copysize; + if(!psnd->buffer) + return 0; + + DEBUGASSERT(psnd->allocated_size > 0); + DEBUGASSERT(psnd->recv_size <= psnd->allocated_size); + DEBUGASSERT(psnd->recv_processed <= psnd->recv_size); + /* Check and process data that already received and storied in internal + intermediate buffer */ + if(psnd->recv_size > psnd->recv_processed) { + DEBUGASSERT(psnd->bindsock == conn->sock[num]); + copysize = CURLMIN(len, psnd->recv_size - psnd->recv_processed); + memcpy(buf, psnd->buffer + psnd->recv_processed, copysize); + psnd->recv_processed += copysize; + } + else + copysize = 0; /* buffer was allocated, but nothing was received */ + + /* Free intermediate buffer if it has no unprocessed data */ + if(psnd->recv_processed == psnd->recv_size) { + free(psnd->buffer); + psnd->buffer = NULL; + psnd->allocated_size = 0; + psnd->recv_size = 0; + psnd->recv_processed = 0; +#ifdef DEBUGBUILD + psnd->bindsock = CURL_SOCKET_BAD; +#endif /* DEBUGBUILD */ + } + return (ssize_t)copysize; +} +#else /* ! USE_RECV_BEFORE_SEND_WORKAROUND */ +/* Use "do-nothing" macros instead of functions when workaround not used */ +bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex) +{ + (void)conn; + (void)sockindex; + return false; +} +#define pre_receive_plain(d,c,n) CURLE_OK +#define get_pre_recved(c,n,b,l) 0 +#endif /* ! USE_RECV_BEFORE_SEND_WORKAROUND */ + +/* Curl_infof() is for info message along the way */ +#define MAXINFO 2048 + +void Curl_infof(struct Curl_easy *data, const char *fmt, ...) +{ + DEBUGASSERT(!strchr(fmt, '\n')); + if(data && data->set.verbose) { + va_list ap; + int len; + char buffer[MAXINFO + 2]; + va_start(ap, fmt); + len = mvsnprintf(buffer, MAXINFO, fmt, ap); + va_end(ap); + buffer[len++] = '\n'; + buffer[len] = '\0'; + Curl_debug(data, CURLINFO_TEXT, buffer, len); + } +} + +/* Curl_failf() is for messages stating why we failed. + * The message SHALL NOT include any LF or CR. + */ + +void Curl_failf(struct Curl_easy *data, const char *fmt, ...) +{ + DEBUGASSERT(!strchr(fmt, '\n')); + if(data->set.verbose || data->set.errorbuffer) { + va_list ap; + int len; + char error[CURL_ERROR_SIZE + 2]; + va_start(ap, fmt); + len = mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap); + + if(data->set.errorbuffer && !data->state.errorbuf) { + strcpy(data->set.errorbuffer, error); + data->state.errorbuf = TRUE; /* wrote error string */ + } + error[len++] = '\n'; + error[len] = '\0'; + Curl_debug(data, CURLINFO_TEXT, error, len); + va_end(ap); + } +} + /* * Curl_write() is an internal write function that sends data to the * server. Works with plain sockets, SCP, SSL or kerberos. @@ -158,7 +302,7 @@ CURLcode Curl_write(struct Curl_easy *data, DEBUGASSERT(data); DEBUGASSERT(data->conn); conn = data->conn; - num = (sockfd != CURL_SOCKET_BAD && sockfd == conn->sock[SECONDARYSOCKET]); + num = (sockfd == conn->sock[SECONDARYSOCKET]); #ifdef CURLDEBUG { @@ -195,6 +339,139 @@ CURLcode Curl_write(struct Curl_easy *data, } } +ssize_t Curl_send_plain(struct Curl_easy *data, int num, + const void *mem, size_t len, CURLcode *code) +{ + struct connectdata *conn; + curl_socket_t sockfd; + ssize_t bytes_written; + + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + conn = data->conn; + sockfd = conn->sock[num]; + /* WinSock will destroy unread received data if send() is + failed. + To avoid lossage of received data, recv() must be + performed before every send() if any incoming data is + available. */ + if(pre_receive_plain(data, conn, num)) { + *code = CURLE_OUT_OF_MEMORY; + return -1; + } + +#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */ + if(conn->bits.tcp_fastopen) { + bytes_written = sendto(sockfd, mem, len, MSG_FASTOPEN, + conn->ip_addr->ai_addr, conn->ip_addr->ai_addrlen); + conn->bits.tcp_fastopen = FALSE; + } + else +#endif + bytes_written = swrite(sockfd, mem, len); + + *code = CURLE_OK; + if(-1 == bytes_written) { + int err = SOCKERRNO; + + if( +#ifdef WSAEWOULDBLOCK + /* This is how Windows does it */ + (WSAEWOULDBLOCK == err) +#else + /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned + due to its inability to send off data without blocking. We therefore + treat both error codes the same here */ + (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err) || + (EINPROGRESS == err) +#endif + ) { + /* this is just a case of EWOULDBLOCK */ + bytes_written = 0; + *code = CURLE_AGAIN; + } + else { + char buffer[STRERROR_LEN]; + failf(data, "Send failure: %s", + Curl_strerror(err, buffer, sizeof(buffer))); + data->state.os_errno = err; + *code = CURLE_SEND_ERROR; + } + } + return bytes_written; +} + +/* + * Curl_write_plain() is an internal write function that sends data to the + * server using plain sockets only. Otherwise meant to have the exact same + * proto as Curl_write() + */ +CURLcode Curl_write_plain(struct Curl_easy *data, + curl_socket_t sockfd, + const void *mem, + size_t len, + ssize_t *written) +{ + CURLcode result; + struct connectdata *conn = data->conn; + int num; + DEBUGASSERT(conn); + num = (sockfd == conn->sock[SECONDARYSOCKET]); + + *written = Curl_send_plain(data, num, mem, len, &result); + + return result; +} + +ssize_t Curl_recv_plain(struct Curl_easy *data, int num, char *buf, + size_t len, CURLcode *code) +{ + struct connectdata *conn; + curl_socket_t sockfd; + ssize_t nread; + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + conn = data->conn; + sockfd = conn->sock[num]; + /* Check and return data that already received and storied in internal + intermediate buffer */ + nread = get_pre_recved(conn, num, buf, len); + if(nread > 0) { + *code = CURLE_OK; + return nread; + } + + nread = sread(sockfd, buf, len); + + *code = CURLE_OK; + if(-1 == nread) { + int err = SOCKERRNO; + + if( +#ifdef WSAEWOULDBLOCK + /* This is how Windows does it */ + (WSAEWOULDBLOCK == err) +#else + /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned + due to its inability to send off data without blocking. We therefore + treat both error codes the same here */ + (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err) +#endif + ) { + /* this is just a case of EWOULDBLOCK */ + *code = CURLE_AGAIN; + } + else { + char buffer[STRERROR_LEN]; + failf(data, "Recv failure: %s", + Curl_strerror(err, buffer, sizeof(buffer))); + data->state.os_errno = err; + *code = CURLE_RECV_ERROR; + } + } + return nread; +} + static CURLcode pausewrite(struct Curl_easy *data, int type, /* what type of data */ const char *ptr, @@ -208,7 +485,8 @@ static CURLcode pausewrite(struct Curl_easy *data, unsigned int i; bool newtype = TRUE; - Curl_conn_ev_data_pause(data, TRUE); + /* If this transfers over HTTP/2, pause the stream! */ + Curl_http2_stream_pause(data, TRUE); if(s->tempcount) { for(i = 0; i< s->tempcount; i++) { @@ -271,8 +549,10 @@ static CURLcode chop_write(struct Curl_easy *data, if(type & CLIENTWRITE_BODY) { #ifdef USE_WEBSOCKETS if(conn->handler->protocol & (CURLPROTO_WS|CURLPROTO_WSS)) { + struct HTTP *ws = data->req.p.http; writebody = Curl_ws_writecb; - writebody_ptr = data; + ws->ws.data = data; + writebody_ptr = ws; } else #endif @@ -384,6 +664,32 @@ CURLcode Curl_client_write(struct Curl_easy *data, return chop_write(data, type, ptr, len); } +CURLcode Curl_read_plain(curl_socket_t sockfd, + char *buf, + size_t bytesfromsocket, + ssize_t *n) +{ + ssize_t nread = sread(sockfd, buf, bytesfromsocket); + + if(-1 == nread) { + const int err = SOCKERRNO; + const bool return_error = +#ifdef USE_WINSOCK + WSAEWOULDBLOCK == err +#else + EWOULDBLOCK == err || EAGAIN == err || EINTR == err +#endif + ; + *n = 0; /* no data returned */ + if(return_error) + return CURLE_AGAIN; + return CURLE_RECV_ERROR; + } + + *n = nread; + return CURLE_OK; +} + /* * Internal read-from-socket function. This is meant to deal with plain * sockets, SSL sockets and kerberos sockets. @@ -414,11 +720,37 @@ CURLcode Curl_read(struct Curl_easy *data, /* transfer */ nread = conn->recv[num](data, num, buffertofill, bytesfromsocket, &result); if(nread < 0) - goto out; + return result; *n += nread; - result = CURLE_OK; -out: - return result; + + return CURLE_OK; } +/* return 0 on success */ +void Curl_debug(struct Curl_easy *data, curl_infotype type, + char *ptr, size_t size) +{ + if(data->set.verbose) { + static const char s_infotype[CURLINFO_END][3] = { + "* ", "< ", "> ", "{ ", "} ", "{ ", "} " }; + if(data->set.fdebug) { + bool inCallback = Curl_is_in_callback(data); + Curl_set_in_callback(data, true); + (void)(*data->set.fdebug)(data, type, ptr, size, data->set.debugdata); + Curl_set_in_callback(data, inCallback); + } + else { + switch(type) { + case CURLINFO_TEXT: + case CURLINFO_HEADER_OUT: + case CURLINFO_HEADER_IN: + fwrite(s_infotype[type], 2, 1, data->set.err); + fwrite(ptr, size, 1, data->set.err); + break; + default: /* nada */ + break; + } + } + } +} diff --git a/contrib/libs/curl/lib/sendf.h b/contrib/libs/curl/lib/sendf.h index d0c9275705..7c4c1280a0 100644 --- a/contrib/libs/curl/lib/sendf.h +++ b/contrib/libs/curl/lib/sendf.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -26,8 +26,26 @@ #include "curl_setup.h" -#include "curl_log.h" +void Curl_infof(struct Curl_easy *, const char *fmt, ...); +void Curl_failf(struct Curl_easy *, const char *fmt, ...); +#if defined(CURL_DISABLE_VERBOSE_STRINGS) + +#if defined(HAVE_VARIADIC_MACROS_C99) +#define infof(...) Curl_nop_stmt +#elif defined(HAVE_VARIADIC_MACROS_GCC) +#define infof(x...) Curl_nop_stmt +#else +#error "missing VARIADIC macro define, fix and rebuild!" +#endif + +#else /* CURL_DISABLE_VERBOSE_STRINGS */ + +#define infof Curl_infof + +#endif /* CURL_DISABLE_VERBOSE_STRINGS */ + +#define failf Curl_failf #define CLIENTWRITE_BODY (1<<0) #define CLIENTWRITE_HEADER (1<<1) @@ -40,6 +58,19 @@ CURLcode Curl_client_write(struct Curl_easy *data, int type, char *ptr, size_t len) WARN_UNUSED_RESULT; +bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex); + +/* internal read-function, does plain socket only */ +CURLcode Curl_read_plain(curl_socket_t sockfd, + char *buf, + size_t bytesfromsocket, + ssize_t *n); + +ssize_t Curl_recv_plain(struct Curl_easy *data, int num, char *buf, + size_t len, CURLcode *code); +ssize_t Curl_send_plain(struct Curl_easy *data, int num, + const void *mem, size_t len, CURLcode *code); + /* internal read-function, does plain socket, SSL and krb4 */ CURLcode Curl_read(struct Curl_easy *data, curl_socket_t sockfd, char *buf, size_t buffersize, @@ -51,4 +82,15 @@ CURLcode Curl_write(struct Curl_easy *data, const void *mem, size_t len, ssize_t *written); +/* internal write-function, does plain sockets ONLY */ +CURLcode Curl_write_plain(struct Curl_easy *data, + curl_socket_t sockfd, + const void *mem, size_t len, + ssize_t *written); + +/* the function used to output verbose information */ +void Curl_debug(struct Curl_easy *data, curl_infotype type, + char *ptr, size_t size); + + #endif /* HEADER_CURL_SENDF_H */ diff --git a/contrib/libs/curl/lib/setopt.c b/contrib/libs/curl/lib/setopt.c index b05162a556..5b5975485c 100644 --- a/contrib/libs/curl/lib/setopt.c +++ b/contrib/libs/curl/lib/setopt.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -115,11 +115,7 @@ static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp) /* Parse the login details if specified. It not then we treat NULL as a hint to clear the existing data */ if(option) { - size_t len = strlen(option); - if(len > CURL_MAX_INPUT_LENGTH) - return CURLE_BAD_FUNCTION_ARGUMENT; - - result = Curl_parse_login_details(option, len, + result = Curl_parse_login_details(option, strlen(option), (userp ? &user : NULL), (passwdp ? &passwd : NULL), NULL); @@ -178,7 +174,7 @@ static CURLcode protocol2num(const char *str, curl_prot_t *val) *val |= h->protocol; } - } while(str && str++); + } while(str++); if(!*val) /* no protocol listed */ @@ -208,15 +204,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.dns_cache_timeout = (int)arg; break; - case CURLOPT_CA_CACHE_TIMEOUT: - arg = va_arg(param, long); - if(arg < -1) - return CURLE_BAD_FUNCTION_ARGUMENT; - else if(arg > INT_MAX) - arg = INT_MAX; - - data->set.general_ssl.ca_cache_timeout = (int)arg; - break; case CURLOPT_DNS_USE_GLOBAL_CACHE: /* deprecated */ break; @@ -233,7 +220,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; #endif case CURLOPT_TLS13_CIPHERS: - if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) { + if(Curl_ssl_tls13_ciphersuites()) { /* set preferred list of TLS 1.3 cipher suites */ result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER13_LIST], va_arg(param, char *)); @@ -243,7 +230,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_TLS13_CIPHERS: - if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) { + if(Curl_ssl_tls13_ciphersuites()) { /* set preferred list of TLS 1.3 cipher suites for proxy */ result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER13_LIST_PROXY], va_arg(param, char *)); @@ -333,8 +320,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * We want to sent data to the remote host. If this is HTTP, that equals * using the PUT request. */ - arg = va_arg(param, long); - if(arg) { + data->set.upload = (0 != va_arg(param, long)) ? TRUE : FALSE; + if(data->set.upload) { /* If this is HTTP, PUT is what's needed to "upload" */ data->set.method = HTTPREQ_PUT; data->set.opt_no_body = FALSE; /* this is implied */ @@ -419,7 +406,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) arg = va_arg(param, long); if((arg < CURL_TIMECOND_NONE) || (arg >= CURL_TIMECOND_LAST)) return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.timecondition = (unsigned char)(curl_TimeCond)arg; + data->set.timecondition = (curl_TimeCond)arg; break; case CURLOPT_TIMEVALUE: /* @@ -467,8 +454,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) version_max >= CURL_SSLVERSION_MAX_LAST) return CURLE_BAD_FUNCTION_ARGUMENT; - primary->version = (unsigned char)version; - primary->version_max = (unsigned int)version_max; + primary->version = version; + primary->version_max = version_max; } #else result = CURLE_NOT_BUILT_IN; @@ -611,7 +598,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_FOLLOWLOCATION: /* - * Follow Location: header hints on an HTTP-server. + * Follow Location: header hints on a HTTP-server. */ data->set.http_follow_location = (0 != va_arg(param, long)) ? TRUE : FALSE; break; @@ -664,6 +651,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) } else data->set.method = HTTPREQ_GET; + data->set.upload = FALSE; break; #ifndef CURL_DISABLE_MIME @@ -735,6 +723,13 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.sep_headers = (bool)((arg & CURLHEADER_SEPARATE)? TRUE: FALSE); break; + case CURLOPT_HTTP200ALIASES: + /* + * Set a list of aliases for HTTP 200 in response header + */ + data->set.http200aliases = va_arg(param, struct curl_slist *); + break; + #if !defined(CURL_DISABLE_COOKIES) case CURLOPT_COOKIE: /* @@ -756,18 +751,18 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) return CURLE_BAD_FUNCTION_ARGUMENT; /* append the cookie file name to the list of file names, and deal with them later */ - cl = curl_slist_append(data->set.cookielist, argptr); + cl = curl_slist_append(data->state.cookielist, argptr); if(!cl) { - curl_slist_free_all(data->set.cookielist); - data->set.cookielist = NULL; + curl_slist_free_all(data->state.cookielist); + data->state.cookielist = NULL; return CURLE_OUT_OF_MEMORY; } - data->set.cookielist = cl; /* store the list for later use */ + data->state.cookielist = cl; /* store the list for later use */ } else { /* clear the list of cookie files */ - curl_slist_free_all(data->set.cookielist); - data->set.cookielist = NULL; + curl_slist_free_all(data->state.cookielist); + data->state.cookielist = NULL; if(!data->share || !data->share->cookies) { /* throw away all existing cookies if this isn't a shared cookie @@ -887,6 +882,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) */ if(va_arg(param, long)) { data->set.method = HTTPREQ_GET; + data->set.upload = FALSE; /* switch off upload */ data->set.opt_no_body = FALSE; /* this is implied */ } break; @@ -897,44 +893,28 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * the listed enums in curl/curl.h. */ arg = va_arg(param, long); - switch(arg) { - case CURL_HTTP_VERSION_NONE: -#ifdef USE_HTTP2 - /* TODO: this seems an undesirable quirk to force a behaviour on - * lower implementations that they should recognize independently? */ - arg = CURL_HTTP_VERSION_2TLS; -#endif - /* accepted */ - break; - case CURL_HTTP_VERSION_1_0: - case CURL_HTTP_VERSION_1_1: - /* accepted */ - break; -#ifdef USE_HTTP2 - case CURL_HTTP_VERSION_2_0: - case CURL_HTTP_VERSION_2TLS: - case CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE: - /* accepted */ - break; -#endif + if(arg < CURL_HTTP_VERSION_NONE) + return CURLE_BAD_FUNCTION_ARGUMENT; #ifdef ENABLE_QUIC - case CURL_HTTP_VERSION_3: - case CURL_HTTP_VERSION_3ONLY: - /* accepted */ - break; + if(arg == CURL_HTTP_VERSION_3) + ; + else #endif - default: - /* not accepted */ - if(arg < CURL_HTTP_VERSION_NONE) - return CURLE_BAD_FUNCTION_ARGUMENT; +#ifndef USE_HTTP2 + if(arg >= CURL_HTTP_VERSION_2) return CURLE_UNSUPPORTED_PROTOCOL; - } +#else + if(arg >= CURL_HTTP_VERSION_LAST) + return CURLE_UNSUPPORTED_PROTOCOL; + if(arg == CURL_HTTP_VERSION_NONE) + arg = CURL_HTTP_VERSION_2TLS; +#endif data->set.httpwant = (unsigned char)arg; break; case CURLOPT_EXPECT_100_TIMEOUT_MS: /* - * Time to wait for a response to an HTTP request containing an + * Time to wait for a response to a HTTP request containing an * Expect: 100-continue header before sending the data anyway. */ arg = va_arg(param, long); @@ -955,13 +935,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.http09_allowed = arg ? TRUE : FALSE; #endif break; - - case CURLOPT_HTTP200ALIASES: - /* - * Set a list of aliases for HTTP 200 in response header - */ - data->set.http200aliases = va_arg(param, struct curl_slist *); - break; #endif /* CURL_DISABLE_HTTP */ #if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \ @@ -1075,7 +1048,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) arg = va_arg(param, long); if((arg < 0) || (arg > 65535)) return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.proxyport = (unsigned short)arg; + data->set.proxyport = arg; break; case CURLOPT_PROXYAUTH: @@ -1157,12 +1130,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_PROXYTYPE: /* - * Set proxy type. + * Set proxy type. HTTP/HTTP_1_0/SOCKS4/SOCKS4a/SOCKS5/SOCKS5_HOSTNAME */ arg = va_arg(param, long); if((arg < CURLPROXY_HTTP) || (arg > CURLPROXY_SOCKS5_HOSTNAME)) return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.proxytype = (unsigned char)(curl_proxytype)arg; + data->set.proxytype = (curl_proxytype)arg; break; case CURLOPT_PROXY_TRANSFER_MODE: @@ -1184,7 +1157,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; case CURLOPT_SOCKS5_AUTH: - data->set.socks5auth = (unsigned char)va_arg(param, unsigned long); + data->set.socks5auth = va_arg(param, unsigned long); if(data->set.socks5auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI)) result = CURLE_NOT_BUILT_IN; break; @@ -1261,7 +1234,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) arg = va_arg(param, long); if((arg < CURLFTPMETHOD_DEFAULT) || (arg >= CURLFTPMETHOD_LAST)) return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.ftp_filemethod = (unsigned char)(curl_ftpfile)arg; + data->set.ftp_filemethod = (curl_ftpfile)arg; break; case CURLOPT_FTPPORT: /* @@ -1288,7 +1261,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) arg = va_arg(param, long); if((arg < CURLFTPSSL_CCC_NONE) || (arg >= CURLFTPSSL_CCC_LAST)) return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.ftp_ccc = (unsigned char)(curl_ftpccc)arg; + data->set.ftp_ccc = (curl_ftpccc)arg; break; case CURLOPT_FTP_SKIP_PASV_IP: @@ -1316,7 +1289,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) arg = va_arg(param, long); if((arg < CURLFTPAUTH_DEFAULT) || (arg >= CURLFTPAUTH_LAST)) return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.ftpsslauth = (unsigned char)(curl_ftpauth)arg; + data->set.ftpsslauth = (curl_ftpauth)arg; break; case CURLOPT_KRBLEVEL: /* @@ -1327,7 +1300,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.krb = (data->set.str[STRING_KRB_LEVEL]) ? TRUE : FALSE; break; #endif -#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) case CURLOPT_FTP_CREATE_MISSING_DIRS: /* * An FTP/SFTP option that modifies an upload to create missing @@ -1341,26 +1313,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) else data->set.ftp_create_missing_dirs = (unsigned char)arg; break; - - case CURLOPT_POSTQUOTE: - /* - * List of RAW FTP commands to use after a transfer - */ - data->set.postquote = va_arg(param, struct curl_slist *); - break; - case CURLOPT_PREQUOTE: - /* - * List of RAW FTP commands to use prior to RETR (Wesley Laxton) - */ - data->set.prequote = va_arg(param, struct curl_slist *); - break; - case CURLOPT_QUOTE: - /* - * List of RAW FTP commands to use before a transfer - */ - data->set.quote = va_arg(param, struct curl_slist *); - break; -#endif case CURLOPT_READDATA: /* * FILE pointer to read the file to be uploaded from. Or possibly @@ -1470,7 +1422,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_TIMEOUT_MS: uarg = va_arg(param, unsigned long); - if(uarg > UINT_MAX) + if(uarg >= UINT_MAX) uarg = UINT_MAX; data->set.timeout = (unsigned int)uarg; break; @@ -1488,7 +1440,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_CONNECTTIMEOUT_MS: uarg = va_arg(param, unsigned long); - if(uarg > UINT_MAX) + if(uarg >= UINT_MAX) uarg = UINT_MAX; data->set.connecttimeout = (unsigned int)uarg; break; @@ -1499,7 +1451,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * The maximum time for curl to wait for FTP server connect */ uarg = va_arg(param, unsigned long); - if(uarg > UINT_MAX) + if(uarg >= UINT_MAX) uarg = UINT_MAX; data->set.accepttimeout = (unsigned int)uarg; break; @@ -1545,6 +1497,24 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) va_arg(param, char *)); break; + case CURLOPT_POSTQUOTE: + /* + * List of RAW FTP commands to use after a transfer + */ + data->set.postquote = va_arg(param, struct curl_slist *); + break; + case CURLOPT_PREQUOTE: + /* + * List of RAW FTP commands to use prior to RETR (Wesley Laxton) + */ + data->set.prequote = va_arg(param, struct curl_slist *); + break; + case CURLOPT_QUOTE: + /* + * List of RAW FTP commands to use before a transfer + */ + data->set.quote = va_arg(param, struct curl_slist *); + break; case CURLOPT_RESOLVE: /* * List of HOST:PORT:[addresses] strings to populate the DNS cache with @@ -1867,15 +1837,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) */ data->set.haproxyprotocol = (0 != va_arg(param, long)) ? TRUE : FALSE; break; - case CURLOPT_HAPROXY_CLIENT_IP: - /* - * Set the client IP to send through HAProxy PROXY protocol - */ - result = Curl_setstropt(&data->set.str[STRING_HAPROXY_CLIENT_IP], - va_arg(param, char *)); - /* We enable implicitly the HAProxy protocol if we use this flag. */ - data->set.haproxyprotocol = TRUE; - break; #endif case CURLOPT_INTERFACE: /* @@ -1901,15 +1862,16 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) arg = va_arg(param, long); if((arg < 0) || (arg > 65535)) return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.localportrange = curlx_sltous(arg); + data->set.localportrange = curlx_sltosi(arg); break; case CURLOPT_GSSAPI_DELEGATION: /* * GSS-API credential delegation bitmask */ - uarg = va_arg(param, unsigned long); - data->set.gssapi_delegation = (unsigned char)uarg& - (CURLGSSAPI_DELEGATION_POLICY_FLAG|CURLGSSAPI_DELEGATION_FLAG); + arg = va_arg(param, long); + if(arg < CURLGSSAPI_DELEGATION_NONE) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.gssapi_delegation = arg; break; case CURLOPT_SSL_VERIFYPEER: /* @@ -2030,7 +1992,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Set a SSL_CTX callback */ #ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX)) + if(Curl_ssl->supports & SSLSUPP_SSL_CTX) data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback); else #endif @@ -2041,7 +2003,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Set a SSL_CTX callback parameter pointer */ #ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX)) + if(Curl_ssl->supports & SSLSUPP_SSL_CTX) data->set.ssl.fsslctxp = va_arg(param, void *); else #endif @@ -2051,7 +2013,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* * Enable TLS false start. */ - if(!Curl_ssl_false_start(data)) { + if(!Curl_ssl_false_start()) { result = CURLE_NOT_BUILT_IN; break; } @@ -2060,7 +2022,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; case CURLOPT_CERTINFO: #ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_CERTINFO)) + if(Curl_ssl->supports & SSLSUPP_CERTINFO) data->set.ssl.certinfo = (0 != va_arg(param, long)) ? TRUE : FALSE; else #endif @@ -2072,7 +2034,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Specify file name of the public key in DER format. */ #ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) + if(Curl_ssl->supports & SSLSUPP_PINNEDPUBKEY) result = Curl_setstropt(&data->set.str[STRING_SSL_PINNEDPUBLICKEY], va_arg(param, char *)); else @@ -2086,7 +2048,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Specify file name of the public key in DER format. */ #ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) + if(Curl_ssl->supports & SSLSUPP_PINNEDPUBKEY) result = Curl_setstropt(&data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY], va_arg(param, char *)); else @@ -2107,7 +2069,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Specify entire PEM of the CA certificate */ #ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) + if(Curl_ssl->supports & SSLSUPP_CAINFO_BLOB) result = Curl_setblobopt(&data->set.blobs[BLOB_CAINFO], va_arg(param, struct curl_blob *)); else @@ -2130,7 +2092,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Specify entire PEM of the CA certificate */ #ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) + if(Curl_ssl->supports & SSLSUPP_CAINFO_BLOB) result = Curl_setblobopt(&data->set.blobs[BLOB_CAINFO_PROXY], va_arg(param, struct curl_blob *)); else @@ -2144,7 +2106,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * certificates which have been prepared using openssl c_rehash utility. */ #ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) + if(Curl_ssl->supports & SSLSUPP_CA_PATH) /* This does not work on windows. */ result = Curl_setstropt(&data->set.str[STRING_SSL_CAPATH], va_arg(param, char *)); @@ -2159,7 +2121,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * CA certificates which have been prepared using openssl c_rehash utility. */ #ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) + if(Curl_ssl->supports & SSLSUPP_CA_PATH) /* This does not work on windows. */ result = Curl_setstropt(&data->set.str[STRING_SSL_CAPATH_PROXY], va_arg(param, char *)); @@ -2243,7 +2205,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) else if(arg < READBUFFER_MIN) arg = READBUFFER_MIN; - data->set.buffer_size = (unsigned int)arg; + data->set.buffer_size = (int)arg; break; case CURLOPT_UPLOAD_BUFFERSIZE: @@ -2289,14 +2251,9 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->cookies = NULL; #endif -#ifndef CURL_DISABLE_HSTS - if(data->share->hsts == data->hsts) - data->hsts = NULL; -#endif -#ifdef USE_SSL if(data->share->sslsession == data->state.session) data->state.session = NULL; -#endif + #ifdef USE_LIBPSL if(data->psl == &data->share->psl) data->psl = data->multi? &data->multi->psl: NULL; @@ -2330,19 +2287,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->cookies = data->share->cookies; } #endif /* CURL_DISABLE_HTTP */ -#ifndef CURL_DISABLE_HSTS - if(data->share->hsts) { - /* first free the private one if any */ - Curl_hsts_cleanup(&data->hsts); - data->hsts = data->share->hsts; - } -#endif /* CURL_DISABLE_HTTP */ -#ifdef USE_SSL if(data->share->sslsession) { data->set.general_ssl.max_ssl_sessions = data->share->max_ssl_sessions; data->state.session = data->share->sslsession; } -#endif #ifdef USE_LIBPSL if(data->share->specifier & (1 << CURL_LOCK_DATA_PSL)) data->psl = &data->share->psl; @@ -2380,7 +2328,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) arg = va_arg(param, long); if((arg < CURLUSESSL_NONE) || (arg >= CURLUSESSL_LAST)) return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.use_ssl = (unsigned char)arg; + data->set.use_ssl = (curl_usessl)arg; break; case CURLOPT_SSL_OPTIONS: @@ -2558,14 +2506,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) va_arg(param, char *)); break; - case CURLOPT_SSH_KNOWNHOSTS: - /* - * Store the file name to read known hosts from. - */ - result = Curl_setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS], - va_arg(param, char *)); - break; -#ifdef USE_LIBSSH2 case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256: /* * Option to allow for the SHA256 of the host public key to be checked @@ -2575,6 +2515,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) va_arg(param, char *)); break; + case CURLOPT_SSH_KNOWNHOSTS: + /* + * Store the file name to read known hosts from. + */ + result = Curl_setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS], + va_arg(param, char *)); + break; +#ifdef USE_LIBSSH2 case CURLOPT_SSH_HOSTKEYFUNCTION: /* the callback to check the hostkey without the knownhost file */ data->set.ssh_hostkeyfunc = va_arg(param, curl_sshhostkeycallback); @@ -2587,7 +2535,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.ssh_hostkeyfunc_userp = va_arg(param, void *); break; #endif - case CURLOPT_SSH_KEYFUNCTION: /* setting to NULL is fine since the ssh.c functions themselves will then revert to use the internal default */ @@ -2634,8 +2581,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) return CURLE_BAD_FUNCTION_ARGUMENT; data->set.new_file_perms = (unsigned int)arg; break; -#endif -#ifdef USE_SSH + case CURLOPT_NEW_DIRECTORY_PERMS: /* * Uses these permissions instead of 0755 @@ -2720,7 +2666,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* Set the list of mail recipients */ data->set.mail_rcpt = va_arg(param, struct curl_slist *); break; - case CURLOPT_MAIL_RCPT_ALLOWFAILS: + case CURLOPT_MAIL_RCPT_ALLLOWFAILS: /* allow RCPT TO command to fail for some recipients */ data->set.mail_rcpt_allowfails = (0 != va_arg(param, long)) ? TRUE : FALSE; break; @@ -2860,7 +2806,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.fnmatch = va_arg(param, curl_fnmatch_callback); break; case CURLOPT_CHUNK_DATA: - data->set.wildcardptr = va_arg(param, void *); + data->wildcard.customptr = va_arg(param, void *); break; case CURLOPT_FNMATCH_DATA: data->set.fnmatch_data = va_arg(param, void *); @@ -2870,33 +2816,52 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_TLSAUTH_USERNAME: result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_USERNAME], va_arg(param, char *)); + if(data->set.str[STRING_TLSAUTH_USERNAME] && + !data->set.ssl.primary.authtype) + data->set.ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default to SRP */ break; #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_TLSAUTH_USERNAME: result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_USERNAME_PROXY], va_arg(param, char *)); + if(data->set.str[STRING_TLSAUTH_USERNAME_PROXY] && + !data->set.proxy_ssl.primary.authtype) + data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default to + SRP */ break; #endif case CURLOPT_TLSAUTH_PASSWORD: result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD], va_arg(param, char *)); + if(data->set.str[STRING_TLSAUTH_USERNAME] && + !data->set.ssl.primary.authtype) + data->set.ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default */ break; #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_TLSAUTH_PASSWORD: result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD_PROXY], va_arg(param, char *)); + if(data->set.str[STRING_TLSAUTH_USERNAME_PROXY] && + !data->set.proxy_ssl.primary.authtype) + data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default */ break; #endif case CURLOPT_TLSAUTH_TYPE: argptr = va_arg(param, char *); - if(argptr && !strncasecompare(argptr, "SRP", strlen("SRP"))) - return CURLE_BAD_FUNCTION_ARGUMENT; + if(!argptr || + strncasecompare(argptr, "SRP", strlen("SRP"))) + data->set.ssl.primary.authtype = CURL_TLSAUTH_SRP; + else + data->set.ssl.primary.authtype = CURL_TLSAUTH_NONE; break; #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_TLSAUTH_TYPE: argptr = va_arg(param, char *); - if(argptr || !strncasecompare(argptr, "SRP", strlen("SRP"))) - return CURLE_BAD_FUNCTION_ARGUMENT; + if(!argptr || + strncasecompare(argptr, "SRP", strlen("SRP"))) + data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_SRP; + else + data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_NONE; break; #endif #endif @@ -2982,23 +2947,29 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.pipewait = (0 != va_arg(param, long)) ? TRUE : FALSE; break; case CURLOPT_STREAM_WEIGHT: -#if defined(USE_HTTP2) || defined(USE_HTTP3) +#ifndef USE_NGHTTP2 + return CURLE_NOT_BUILT_IN; +#else arg = va_arg(param, long); if((arg >= 1) && (arg <= 256)) - data->set.priority.weight = (int)arg; + data->set.stream_weight = (int)arg; break; -#else - return CURLE_NOT_BUILT_IN; #endif case CURLOPT_STREAM_DEPENDS: case CURLOPT_STREAM_DEPENDS_E: { +#ifndef USE_NGHTTP2 + return CURLE_NOT_BUILT_IN; +#else struct Curl_easy *dep = va_arg(param, struct Curl_easy *); if(!dep || GOOD_EASY_HANDLE(dep)) { - return Curl_data_priority_add_child(dep, data, - option == CURLOPT_STREAM_DEPENDS_E); + if(data->set.stream_depends_on) { + Curl_http2_remove_child(data->set.stream_depends_on, data); + } + Curl_http2_add_child(dep, data, (option == CURLOPT_STREAM_DEPENDS_E)); } break; +#endif } case CURLOPT_CONNECT_TO: data->set.connect_to = va_arg(param, struct curl_slist *); @@ -3008,7 +2979,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS: uarg = va_arg(param, unsigned long); - if(uarg > UINT_MAX) + if(uarg >= UINT_MAX) uarg = UINT_MAX; data->set.happy_eyeballs_timeout = (unsigned int)uarg; break; @@ -3069,39 +3040,19 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_HSTSWRITEDATA: data->set.hsts_write_userp = va_arg(param, void *); break; - case CURLOPT_HSTS: { - struct curl_slist *h; + case CURLOPT_HSTS: if(!data->hsts) { data->hsts = Curl_hsts_init(); if(!data->hsts) return CURLE_OUT_OF_MEMORY; } argptr = va_arg(param, char *); - if(argptr) { - result = Curl_setstropt(&data->set.str[STRING_HSTS], argptr); - if(result) - return result; - /* this needs to build a list of file names to read from, so that it can - read them later, as we might get a shared HSTS handle to load them - into */ - h = curl_slist_append(data->set.hstslist, argptr); - if(!h) { - curl_slist_free_all(data->set.hstslist); - data->set.hstslist = NULL; - return CURLE_OUT_OF_MEMORY; - } - data->set.hstslist = h; /* store the list for later use */ - } - else { - /* clear the list of HSTS files */ - curl_slist_free_all(data->set.hstslist); - data->set.hstslist = NULL; - if(!data->share || !data->share->hsts) - /* throw away the HSTS cache unless shared */ - Curl_hsts_cleanup(&data->hsts); - } + result = Curl_setstropt(&data->set.str[STRING_HSTS], argptr); + if(result) + return result; + if(argptr) + (void)Curl_hsts_loadfile(data, data->hsts, argptr); break; - } case CURLOPT_HSTS_CTRL: arg = va_arg(param, long); if(arg & CURLHSTS_ENABLE) { @@ -3156,9 +3107,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; } #endif - case CURLOPT_QUICK_EXIT: - data->set.quick_exit = (0 != va_arg(param, long)) ? 1L:0L; - break; default: /* unknown tag and its companion, just ignore: */ result = CURLE_UNKNOWN_OPTION; diff --git a/contrib/libs/curl/lib/setopt.h b/contrib/libs/curl/lib/setopt.h index 3c14a05e37..ffc77a71dc 100644 --- a/contrib/libs/curl/lib/setopt.h +++ b/contrib/libs/curl/lib/setopt.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/setup-win32.h b/contrib/libs/curl/lib/setup-win32.h index 13948389a4..bc5f8efc3c 100644 --- a/contrib/libs/curl/lib/setup-win32.h +++ b/contrib/libs/curl/lib/setup-win32.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/sha256.c b/contrib/libs/curl/lib/sha256.c index f9f6218ccb..43b7752994 100644 --- a/contrib/libs/curl/lib/sha256.c +++ b/contrib/libs/curl/lib/sha256.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Florin Petriuc, <petriuc.florin@gmail.com> - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2017, Florin Petriuc, <petriuc.florin@gmail.com> + * Copyright (C) 2018 - 2022, 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 @@ -57,34 +57,6 @@ #endif #endif /* USE_MBEDTLS */ -#if defined(USE_OPENSSL_SHA256) - -/* When OpenSSL or wolfSSL is available we use their SHA256-functions. */ -#if defined(USE_OPENSSL) -#include <openssl/evp.h> -#elif defined(USE_WOLFSSL) -#error #include <wolfssl/openssl/evp.h> -#endif - -#elif defined(USE_GNUTLS) -#error #include <nettle/sha.h> -#elif defined(USE_MBEDTLS) -#error #include <mbedtls/sha256.h> -#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ - (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040)) || \ - (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ - (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000)) -#include <CommonCrypto/CommonDigest.h> -#define AN_APPLE_OS -#elif defined(USE_WIN32_CRYPTO) -#include <wincrypt.h> -#endif - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - /* Please keep the SSL backend-specific #if branches in this order: * * 1. USE_OPENSSL @@ -99,6 +71,20 @@ #if defined(USE_OPENSSL_SHA256) +/* When OpenSSL or wolfSSL is available is available we use their + * SHA256-functions. + */ +#if defined(USE_OPENSSL) +#include <openssl/evp.h> +#elif defined(USE_WOLFSSL) +#error #include <wolfssl/openssl/evp.h> +#endif + +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + struct sha256_ctx { EVP_MD_CTX *openssl_ctx; }; @@ -129,6 +115,13 @@ static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) #elif defined(USE_GNUTLS) +#error #include <nettle/sha.h> + +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + typedef struct sha256_ctx my_sha256_ctx; static CURLcode my_sha256_init(my_sha256_ctx *ctx) @@ -151,6 +144,13 @@ static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) #elif defined(USE_MBEDTLS) +#error #include <mbedtls/sha256.h> + +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + typedef mbedtls_sha256_context my_sha256_ctx; static CURLcode my_sha256_init(my_sha256_ctx *ctx) @@ -183,7 +183,18 @@ static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) #endif } -#elif defined(AN_APPLE_OS) +#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ + (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040)) || \ + (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ + (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000)) + +#include <CommonCrypto/CommonDigest.h> + +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + typedef CC_SHA256_CTX my_sha256_ctx; static CURLcode my_sha256_init(my_sha256_ctx *ctx) @@ -206,6 +217,8 @@ static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) #elif defined(USE_WIN32_CRYPTO) +#include <wincrypt.h> + struct sha256_ctx { HCRYPTPROV hCryptProv; HCRYPTHASH hHash; diff --git a/contrib/libs/curl/lib/share.c b/contrib/libs/curl/lib/share.c index c0a8d806f3..1a083e72a0 100644 --- a/contrib/libs/curl/lib/share.c +++ b/contrib/libs/curl/lib/share.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -29,11 +29,9 @@ #include "share.h" #include "psl.h" #include "vtls/vtls.h" -#include "hsts.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" #include "curl_memory.h" + +/* The last #include file should be: */ #include "memdebug.h" struct Curl_share * @@ -91,18 +89,6 @@ curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...) #endif break; - case CURL_LOCK_DATA_HSTS: -#ifndef CURL_DISABLE_HSTS - if(!share->hsts) { - share->hsts = Curl_hsts_init(); - if(!share->hsts) - res = CURLSHE_NOMEM; - } -#else /* CURL_DISABLE_HSTS */ - res = CURLSHE_NOT_BUILT_IN; -#endif - break; - case CURL_LOCK_DATA_SSL_SESSION: #ifdef USE_SSL if(!share->sslsession) { @@ -155,16 +141,6 @@ curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...) #endif break; - case CURL_LOCK_DATA_HSTS: -#ifndef CURL_DISABLE_HSTS - if(share->hsts) { - Curl_hsts_cleanup(&share->hsts); - } -#else /* CURL_DISABLE_HSTS */ - res = CURLSHE_NOT_BUILT_IN; -#endif - break; - case CURL_LOCK_DATA_SSL_SESSION: #ifdef USE_SSL Curl_safefree(share->sslsession); @@ -231,10 +207,6 @@ curl_share_cleanup(struct Curl_share *share) Curl_cookie_cleanup(share->cookies); #endif -#ifndef CURL_DISABLE_HSTS - Curl_hsts_cleanup(&share->hsts); -#endif - #ifdef USE_SSL if(share->sslsession) { size_t i; diff --git a/contrib/libs/curl/lib/share.h b/contrib/libs/curl/lib/share.h index 7f55aac853..32be41691a 100644 --- a/contrib/libs/curl/lib/share.h +++ b/contrib/libs/curl/lib/share.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -59,14 +59,10 @@ struct Curl_share { #ifdef USE_LIBPSL struct PslCache psl; #endif -#ifndef CURL_DISABLE_HSTS - struct hsts *hsts; -#endif -#ifdef USE_SSL + struct Curl_ssl_session *sslsession; size_t max_ssl_sessions; long sessionage; -#endif }; CURLSHcode Curl_share_lock(struct Curl_easy *, curl_lock_data, diff --git a/contrib/libs/curl/lib/sigpipe.h b/contrib/libs/curl/lib/sigpipe.h index 48761ad0fd..d12b31764d 100644 --- a/contrib/libs/curl/lib/sigpipe.h +++ b/contrib/libs/curl/lib/sigpipe.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -50,6 +50,7 @@ static void sigpipe_ignore(struct Curl_easy *data, if(!data->set.no_signal) { struct sigaction action; /* first, extract the existing situation */ + memset(&ig->old_pipe_act, 0, sizeof(struct sigaction)); sigaction(SIGPIPE, NULL, &ig->old_pipe_act); action = ig->old_pipe_act; /* ignore this signal */ diff --git a/contrib/libs/curl/lib/slist.c b/contrib/libs/curl/lib/slist.c index 366b247609..6c80722c77 100644 --- a/contrib/libs/curl/lib/slist.c +++ b/contrib/libs/curl/lib/slist.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/slist.h b/contrib/libs/curl/lib/slist.h index 9561fd0226..4e5834c90a 100644 --- a/contrib/libs/curl/lib/slist.h +++ b/contrib/libs/curl/lib/slist.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/smb.c b/contrib/libs/curl/lib/smb.c index bc4e883551..a62e858143 100644 --- a/contrib/libs/curl/lib/smb.c +++ b/contrib/libs/curl/lib/smb.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) Bill Nagel <wnagel@tycoint.com>, Exacq Technologies + * Copyright (C) 2016 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2014, Bill Nagel <wnagel@tycoint.com>, Exacq Technologies * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -25,17 +25,24 @@ #include "curl_setup.h" -#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) +#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \ + (SIZEOF_CURL_OFF_T > 4) -#ifdef WIN32 +#define BUILDING_CURL_SMB_C + +#ifdef HAVE_PROCESS_H +#include <process.h> +#ifdef CURL_WINDOWS_APP #define getpid GetCurrentProcessId +#elif defined(WIN32) +#define getpid _getpid +#endif #endif #include "smb.h" #include "urldata.h" #include "sendf.h" #include "multiif.h" -#include "cfilters.h" #include "connect.h" #include "progress.h" #include "transfer.h" @@ -48,199 +55,6 @@ #include "curl_memory.h" #include "memdebug.h" -/* - * Definitions for SMB protocol data structures - */ -#if defined(_MSC_VER) || defined(__ILEC400__) -# define PACK -# pragma pack(push) -# pragma pack(1) -#elif defined(__GNUC__) -# define PACK __attribute__((packed)) -#else -# define PACK -#endif - -#define SMB_COM_CLOSE 0x04 -#define SMB_COM_READ_ANDX 0x2e -#define SMB_COM_WRITE_ANDX 0x2f -#define SMB_COM_TREE_DISCONNECT 0x71 -#define SMB_COM_NEGOTIATE 0x72 -#define SMB_COM_SETUP_ANDX 0x73 -#define SMB_COM_TREE_CONNECT_ANDX 0x75 -#define SMB_COM_NT_CREATE_ANDX 0xa2 -#define SMB_COM_NO_ANDX_COMMAND 0xff - -#define SMB_WC_CLOSE 0x03 -#define SMB_WC_READ_ANDX 0x0c -#define SMB_WC_WRITE_ANDX 0x0e -#define SMB_WC_SETUP_ANDX 0x0d -#define SMB_WC_TREE_CONNECT_ANDX 0x04 -#define SMB_WC_NT_CREATE_ANDX 0x18 - -#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10 -#define SMB_FLAGS_CASELESS_PATHNAMES 0x08 -#define SMB_FLAGS2_UNICODE_STRINGS 0x8000 -#define SMB_FLAGS2_IS_LONG_NAME 0x0040 -#define SMB_FLAGS2_KNOWS_LONG_NAME 0x0001 - -#define SMB_CAP_LARGE_FILES 0x08 -#define SMB_GENERIC_WRITE 0x40000000 -#define SMB_GENERIC_READ 0x80000000 -#define SMB_FILE_SHARE_ALL 0x07 -#define SMB_FILE_OPEN 0x01 -#define SMB_FILE_OVERWRITE_IF 0x05 - -#define SMB_ERR_NOACCESS 0x00050001 - -struct smb_header { - unsigned char nbt_type; - unsigned char nbt_flags; - unsigned short nbt_length; - unsigned char magic[4]; - unsigned char command; - unsigned int status; - unsigned char flags; - unsigned short flags2; - unsigned short pid_high; - unsigned char signature[8]; - unsigned short pad; - unsigned short tid; - unsigned short pid; - unsigned short uid; - unsigned short mid; -} PACK; - -struct smb_negotiate_response { - struct smb_header h; - unsigned char word_count; - unsigned short dialect_index; - unsigned char security_mode; - unsigned short max_mpx_count; - unsigned short max_number_vcs; - unsigned int max_buffer_size; - unsigned int max_raw_size; - unsigned int session_key; - unsigned int capabilities; - unsigned int system_time_low; - unsigned int system_time_high; - unsigned short server_time_zone; - unsigned char encryption_key_length; - unsigned short byte_count; - char bytes[1]; -} PACK; - -struct andx { - unsigned char command; - unsigned char pad; - unsigned short offset; -} PACK; - -struct smb_setup { - unsigned char word_count; - struct andx andx; - unsigned short max_buffer_size; - unsigned short max_mpx_count; - unsigned short vc_number; - unsigned int session_key; - unsigned short lengths[2]; - unsigned int pad; - unsigned int capabilities; - unsigned short byte_count; - char bytes[1024]; -} PACK; - -struct smb_tree_connect { - unsigned char word_count; - struct andx andx; - unsigned short flags; - unsigned short pw_len; - unsigned short byte_count; - char bytes[1024]; -} PACK; - -struct smb_nt_create { - unsigned char word_count; - struct andx andx; - unsigned char pad; - unsigned short name_length; - unsigned int flags; - unsigned int root_fid; - unsigned int access; - curl_off_t allocation_size; - unsigned int ext_file_attributes; - unsigned int share_access; - unsigned int create_disposition; - unsigned int create_options; - unsigned int impersonation_level; - unsigned char security_flags; - unsigned short byte_count; - char bytes[1024]; -} PACK; - -struct smb_nt_create_response { - struct smb_header h; - unsigned char word_count; - struct andx andx; - unsigned char op_lock_level; - unsigned short fid; - unsigned int create_disposition; - - curl_off_t create_time; - curl_off_t last_access_time; - curl_off_t last_write_time; - curl_off_t last_change_time; - unsigned int ext_file_attributes; - curl_off_t allocation_size; - curl_off_t end_of_file; -} PACK; - -struct smb_read { - unsigned char word_count; - struct andx andx; - unsigned short fid; - unsigned int offset; - unsigned short max_bytes; - unsigned short min_bytes; - unsigned int timeout; - unsigned short remaining; - unsigned int offset_high; - unsigned short byte_count; -} PACK; - -struct smb_write { - struct smb_header h; - unsigned char word_count; - struct andx andx; - unsigned short fid; - unsigned int offset; - unsigned int timeout; - unsigned short write_mode; - unsigned short remaining; - unsigned short pad; - unsigned short data_length; - unsigned short data_offset; - unsigned int offset_high; - unsigned short byte_count; - unsigned char pad2; -} PACK; - -struct smb_close { - unsigned char word_count; - unsigned short fid; - unsigned int last_mtime; - unsigned short byte_count; -} PACK; - -struct smb_tree_disconnect { - unsigned char word_count; - unsigned short byte_count; -} PACK; - -#if defined(_MSC_VER) || defined(__ILEC400__) -# pragma pack(pop) -#endif - /* Local API functions */ static CURLcode smb_setup_connection(struct Curl_easy *data, struct connectdata *conn); @@ -248,6 +62,8 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done); static CURLcode smb_connection_state(struct Curl_easy *data, bool *done); static CURLcode smb_do(struct Curl_easy *data, bool *done); static CURLcode smb_request_state(struct Curl_easy *data, bool *done); +static CURLcode smb_done(struct Curl_easy *data, CURLcode status, + bool premature); static CURLcode smb_disconnect(struct Curl_easy *data, struct connectdata *conn, bool dead); static int smb_getsock(struct Curl_easy *data, struct connectdata *conn, @@ -262,7 +78,7 @@ const struct Curl_handler Curl_handler_smb = { "SMB", /* scheme */ smb_setup_connection, /* setup_connection */ smb_do, /* do_it */ - ZERO_NULL, /* done */ + smb_done, /* done */ ZERO_NULL, /* do_more */ smb_connect, /* connect_it */ smb_connection_state, /* connecting */ @@ -289,7 +105,7 @@ const struct Curl_handler Curl_handler_smbs = { "SMBS", /* scheme */ smb_setup_connection, /* setup_connection */ smb_do, /* do_it */ - ZERO_NULL, /* done */ + smb_done, /* done */ ZERO_NULL, /* do_more */ smb_connect, /* connect_it */ smb_connection_state, /* connecting */ @@ -721,7 +537,7 @@ static CURLcode smb_send_open(struct Curl_easy *data) byte_count = strlen(req->path); msg.name_length = smb_swap16((unsigned short)byte_count); msg.share_access = smb_swap32(SMB_FILE_SHARE_ALL); - if(data->state.upload) { + if(data->set.upload) { msg.access = smb_swap32(SMB_GENERIC_READ | SMB_GENERIC_WRITE); msg.create_disposition = smb_swap32(SMB_FILE_OVERWRITE_IF); } @@ -855,7 +671,8 @@ static CURLcode smb_connection_state(struct Curl_easy *data, bool *done) #ifdef USE_SSL if((conn->handler->flags & PROTOPT_SSL)) { bool ssl_done = FALSE; - result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssl_done); + result = Curl_ssl_connect_nonblocking(data, conn, FALSE, + FIRSTSOCKET, &ssl_done); if(result && result != CURLE_AGAIN) return result; if(!ssl_done) @@ -953,11 +770,6 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done) void *msg = NULL; const struct smb_nt_create_response *smb_m; - if(data->state.upload && (data->state.infilesize < 0)) { - failf(data, "SMB upload needs to know the size up front"); - return CURLE_SEND_ERROR; - } - /* Start the request */ if(req->state == SMB_REQUESTING) { result = smb_send_tree_connect(data); @@ -1004,12 +816,13 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done) smb_m = (const struct smb_nt_create_response*) msg; req->fid = smb_swap16(smb_m->fid); data->req.offset = 0; - if(data->state.upload) { + if(data->set.upload) { data->req.size = data->state.infilesize; Curl_pgrsSetUploadSize(data, data->req.size); next_state = SMB_UPLOAD; } else { + smb_m = (const struct smb_nt_create_response*) msg; data->req.size = smb_swap64(smb_m->end_of_file); if(data->req.size < 0) { req->result = CURLE_WEIRD_SERVER_REPLY; @@ -1128,6 +941,14 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done) return CURLE_OK; } +static CURLcode smb_done(struct Curl_easy *data, CURLcode status, + bool premature) +{ + (void) premature; + Curl_safefree(data->req.p.smb); + return status; +} + static CURLcode smb_disconnect(struct Curl_easy *data, struct connectdata *conn, bool dead) { @@ -1187,7 +1008,6 @@ static CURLcode smb_parse_url_path(struct Curl_easy *data, /* The share must be present */ if(!slash) { Curl_safefree(smbc->share); - failf(data, "missing share in URL path for SMB"); return CURLE_URL_MALFORMAT; } diff --git a/contrib/libs/curl/lib/smb.h b/contrib/libs/curl/lib/smb.h index 437f4a58a8..919f3ac142 100644 --- a/contrib/libs/curl/lib/smb.h +++ b/contrib/libs/curl/lib/smb.h @@ -7,8 +7,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Bill Nagel <wnagel@tycoint.com>, Exacq Technologies - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2018, Bill Nagel <wnagel@tycoint.com>, Exacq Technologies + * Copyright (C) 2018 - 2022, 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 @@ -48,6 +48,203 @@ struct smb_conn { size_t got; }; +/* + * Definitions for SMB protocol data structures + */ +#ifdef BUILDING_CURL_SMB_C + +#if defined(_MSC_VER) || defined(__ILEC400__) +# define PACK +# pragma pack(push) +# pragma pack(1) +#elif defined(__GNUC__) +# define PACK __attribute__((packed)) +#else +# define PACK +#endif + +#define SMB_COM_CLOSE 0x04 +#define SMB_COM_READ_ANDX 0x2e +#define SMB_COM_WRITE_ANDX 0x2f +#define SMB_COM_TREE_DISCONNECT 0x71 +#define SMB_COM_NEGOTIATE 0x72 +#define SMB_COM_SETUP_ANDX 0x73 +#define SMB_COM_TREE_CONNECT_ANDX 0x75 +#define SMB_COM_NT_CREATE_ANDX 0xa2 +#define SMB_COM_NO_ANDX_COMMAND 0xff + +#define SMB_WC_CLOSE 0x03 +#define SMB_WC_READ_ANDX 0x0c +#define SMB_WC_WRITE_ANDX 0x0e +#define SMB_WC_SETUP_ANDX 0x0d +#define SMB_WC_TREE_CONNECT_ANDX 0x04 +#define SMB_WC_NT_CREATE_ANDX 0x18 + +#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10 +#define SMB_FLAGS_CASELESS_PATHNAMES 0x08 +#define SMB_FLAGS2_UNICODE_STRINGS 0x8000 +#define SMB_FLAGS2_IS_LONG_NAME 0x0040 +#define SMB_FLAGS2_KNOWS_LONG_NAME 0x0001 + +#define SMB_CAP_LARGE_FILES 0x08 +#define SMB_GENERIC_WRITE 0x40000000 +#define SMB_GENERIC_READ 0x80000000 +#define SMB_FILE_SHARE_ALL 0x07 +#define SMB_FILE_OPEN 0x01 +#define SMB_FILE_OVERWRITE_IF 0x05 + +#define SMB_ERR_NOACCESS 0x00050001 + +struct smb_header { + unsigned char nbt_type; + unsigned char nbt_flags; + unsigned short nbt_length; + unsigned char magic[4]; + unsigned char command; + unsigned int status; + unsigned char flags; + unsigned short flags2; + unsigned short pid_high; + unsigned char signature[8]; + unsigned short pad; + unsigned short tid; + unsigned short pid; + unsigned short uid; + unsigned short mid; +} PACK; + +struct smb_negotiate_response { + struct smb_header h; + unsigned char word_count; + unsigned short dialect_index; + unsigned char security_mode; + unsigned short max_mpx_count; + unsigned short max_number_vcs; + unsigned int max_buffer_size; + unsigned int max_raw_size; + unsigned int session_key; + unsigned int capabilities; + unsigned int system_time_low; + unsigned int system_time_high; + unsigned short server_time_zone; + unsigned char encryption_key_length; + unsigned short byte_count; + char bytes[1]; +} PACK; + +struct andx { + unsigned char command; + unsigned char pad; + unsigned short offset; +} PACK; + +struct smb_setup { + unsigned char word_count; + struct andx andx; + unsigned short max_buffer_size; + unsigned short max_mpx_count; + unsigned short vc_number; + unsigned int session_key; + unsigned short lengths[2]; + unsigned int pad; + unsigned int capabilities; + unsigned short byte_count; + char bytes[1024]; +} PACK; + +struct smb_tree_connect { + unsigned char word_count; + struct andx andx; + unsigned short flags; + unsigned short pw_len; + unsigned short byte_count; + char bytes[1024]; +} PACK; + +struct smb_nt_create { + unsigned char word_count; + struct andx andx; + unsigned char pad; + unsigned short name_length; + unsigned int flags; + unsigned int root_fid; + unsigned int access; + curl_off_t allocation_size; + unsigned int ext_file_attributes; + unsigned int share_access; + unsigned int create_disposition; + unsigned int create_options; + unsigned int impersonation_level; + unsigned char security_flags; + unsigned short byte_count; + char bytes[1024]; +} PACK; + +struct smb_nt_create_response { + struct smb_header h; + unsigned char word_count; + struct andx andx; + unsigned char op_lock_level; + unsigned short fid; + unsigned int create_disposition; + + curl_off_t create_time; + curl_off_t last_access_time; + curl_off_t last_write_time; + curl_off_t last_change_time; + unsigned int ext_file_attributes; + curl_off_t allocation_size; + curl_off_t end_of_file; +} PACK; + +struct smb_read { + unsigned char word_count; + struct andx andx; + unsigned short fid; + unsigned int offset; + unsigned short max_bytes; + unsigned short min_bytes; + unsigned int timeout; + unsigned short remaining; + unsigned int offset_high; + unsigned short byte_count; +} PACK; + +struct smb_write { + struct smb_header h; + unsigned char word_count; + struct andx andx; + unsigned short fid; + unsigned int offset; + unsigned int timeout; + unsigned short write_mode; + unsigned short remaining; + unsigned short pad; + unsigned short data_length; + unsigned short data_offset; + unsigned int offset_high; + unsigned short byte_count; + unsigned char pad2; +} PACK; + +struct smb_close { + unsigned char word_count; + unsigned short fid; + unsigned int last_mtime; + unsigned short byte_count; +} PACK; + +struct smb_tree_disconnect { + unsigned char word_count; + unsigned short byte_count; +} PACK; + +#if defined(_MSC_VER) || defined(__ILEC400__) +# pragma pack(pop) +#endif + +#endif /* BUILDING_CURL_SMB_C */ + #if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \ (SIZEOF_CURL_OFF_T > 4) diff --git a/contrib/libs/curl/lib/smtp.c b/contrib/libs/curl/lib/smtp.c index afcdd10ab9..6ebb41af66 100644 --- a/contrib/libs/curl/lib/smtp.c +++ b/contrib/libs/curl/lib/smtp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -60,6 +60,11 @@ #include <inet.h> #endif +#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) +#undef in_addr_t +#define in_addr_t unsigned long +#endif + #include <curl/curl.h> #include "urldata.h" #include "sendf.h" @@ -74,7 +79,6 @@ #include "strtoofft.h" #include "strcase.h" #include "vtls/vtls.h" -#include "cfilters.h" #include "connect.h" #include "select.h" #include "multiif.h" @@ -83,7 +87,6 @@ #include "bufref.h" #include "curl_sasl.h" #include "warnless.h" -#include "idn.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" @@ -106,7 +109,7 @@ static CURLcode smtp_setup_connection(struct Curl_easy *data, static CURLcode smtp_parse_url_options(struct connectdata *conn); static CURLcode smtp_parse_url_path(struct Curl_easy *data); static CURLcode smtp_parse_custom_request(struct Curl_easy *data); -static CURLcode smtp_parse_address(const char *fqma, +static CURLcode smtp_parse_address(struct Curl_easy *data, const char *fqma, char **address, struct hostname *host); static CURLcode smtp_perform_auth(struct Curl_easy *data, const char *mech, const struct bufref *initresp); @@ -281,11 +284,11 @@ static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out) /*********************************************************************** * - * smtp_state() + * state() * * This is the ONLY way to change SMTP state! */ -static void smtp_state(struct Curl_easy *data, smtpstate newstate) +static void state(struct Curl_easy *data, smtpstate newstate) { struct smtp_conn *smtpc = &data->conn->proto.smtpc; #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) @@ -338,7 +341,7 @@ static CURLcode smtp_perform_ehlo(struct Curl_easy *data) result = Curl_pp_sendf(data, &smtpc->pp, "EHLO %s", smtpc->domain); if(!result) - smtp_state(data, SMTP_EHLO); + state(data, SMTP_EHLO); return result; } @@ -362,7 +365,7 @@ static CURLcode smtp_perform_helo(struct Curl_easy *data, result = Curl_pp_sendf(data, &smtpc->pp, "HELO %s", smtpc->domain); if(!result) - smtp_state(data, SMTP_HELO); + state(data, SMTP_HELO); return result; } @@ -381,7 +384,7 @@ static CURLcode smtp_perform_starttls(struct Curl_easy *data, "%s", "STARTTLS"); if(!result) - smtp_state(data, SMTP_STARTTLS); + state(data, SMTP_STARTTLS); return result; } @@ -397,27 +400,20 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data) /* Start the SSL connection */ struct connectdata *conn = data->conn; struct smtp_conn *smtpc = &conn->proto.smtpc; - CURLcode result; - bool ssldone = FALSE; - - if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); - if(result) - goto out; - } + CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FALSE, + FIRSTSOCKET, + &smtpc->ssldone); - result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); if(!result) { - smtpc->ssldone = ssldone; if(smtpc->state != SMTP_UPGRADETLS) - smtp_state(data, SMTP_UPGRADETLS); + state(data, SMTP_UPGRADETLS); if(smtpc->ssldone) { smtp_to_smtps(conn); result = smtp_perform_ehlo(data); } } -out: + return result; } @@ -499,7 +495,7 @@ static CURLcode smtp_perform_authentication(struct Curl_easy *data) server supports authentication, and end the connect phase if not */ if(!smtpc->auth_supported || !Curl_sasl_can_authenticate(&smtpc->sasl, data)) { - smtp_state(data, SMTP_STOP); + state(data, SMTP_STOP); return result; } @@ -508,7 +504,7 @@ static CURLcode smtp_perform_authentication(struct Curl_easy *data) if(!result) { if(progress == SASL_INPROGRESS) - smtp_state(data, SMTP_AUTH); + state(data, SMTP_AUTH); else { /* Other mechanisms not supported */ infof(data, "No known authentication mechanisms supported"); @@ -544,7 +540,7 @@ static CURLcode smtp_perform_command(struct Curl_easy *data) /* Parse the mailbox to verify into the local address and host name parts, converting the host name to an IDN A-label if necessary */ - result = smtp_parse_address(smtp->rcpt->data, + result = smtp_parse_address(data, smtp->rcpt->data, &address, &host); if(result) return result; @@ -586,7 +582,7 @@ static CURLcode smtp_perform_command(struct Curl_easy *data) smtp->custom : "HELP"); if(!result) - smtp_state(data, SMTP_COMMAND); + state(data, SMTP_COMMAND); return result; } @@ -618,7 +614,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data) /* Parse the FROM mailbox into the local address and host name parts, converting the host name to an IDN A-label if necessary */ - result = smtp_parse_address(data->set.str[STRING_MAIL_FROM], + result = smtp_parse_address(data, data->set.str[STRING_MAIL_FROM], &address, &host); if(result) return result; @@ -656,7 +652,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data) /* Parse the AUTH mailbox into the local address and host name parts, converting the host name to an IDN A-label if necessary */ - result = smtp_parse_address(data->set.str[STRING_MAIL_AUTH], + result = smtp_parse_address(data, data->set.str[STRING_MAIL_AUTH], &address, &host); if(result) { free(from); @@ -700,7 +696,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data) /* Add external headers and mime version. */ curl_mime_headers(&data->set.mimepost, data->set.headers, 0); - result = Curl_mime_prepare_headers(data, &data->set.mimepost, NULL, + result = Curl_mime_prepare_headers(&data->set.mimepost, NULL, NULL, MIMESTRATEGY_MAIL); if(!result) @@ -771,7 +767,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data) free(size); if(!result) - smtp_state(data, SMTP_MAIL); + state(data, SMTP_MAIL); return result; } @@ -793,7 +789,7 @@ static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data) /* Parse the recipient mailbox into the local address and host name parts, converting the host name to an IDN A-label if necessary */ - result = smtp_parse_address(smtp->rcpt->data, + result = smtp_parse_address(data, smtp->rcpt->data, &address, &host); if(result) return result; @@ -812,7 +808,7 @@ static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data) free(address); if(!result) - smtp_state(data, SMTP_RCPT); + state(data, SMTP_RCPT); return result; } @@ -830,7 +826,7 @@ static CURLcode smtp_perform_quit(struct Curl_easy *data, CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "QUIT"); if(!result) - smtp_state(data, SMTP_QUIT); + state(data, SMTP_QUIT); return result; } @@ -892,8 +888,7 @@ static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data, (void)instate; /* no use for this yet */ if(smtpcode/100 != 2 && smtpcode != 1) { - if(data->set.use_ssl <= CURLUSESSL_TRY - || Curl_conn_is_ssl(conn, FIRSTSOCKET)) + if(data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) result = smtp_perform_helo(data, conn); else { failf(data, "Remote access denied: %d", smtpcode); @@ -958,7 +953,7 @@ static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data, } if(smtpcode != 1) { - if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) { + if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) { /* We don't have a SSL/TLS connection yet, but SSL is requested */ if(smtpc->tls_supported) /* Switch to TLS connection now */ @@ -996,7 +991,7 @@ static CURLcode smtp_state_helo_resp(struct Curl_easy *data, int smtpcode, } else /* End of connect phase */ - smtp_state(data, SMTP_STOP); + state(data, SMTP_STOP); return result; } @@ -1017,7 +1012,7 @@ static CURLcode smtp_state_auth_resp(struct Curl_easy *data, if(!result) switch(progress) { case SASL_DONE: - smtp_state(data, SMTP_STOP); /* Authenticated */ + state(data, SMTP_STOP); /* Authenticated */ break; case SASL_IDLE: /* No mechanism left after cancellation */ failf(data, "Authentication cancelled"); @@ -1048,7 +1043,7 @@ static CURLcode smtp_state_command_resp(struct Curl_easy *data, int smtpcode, } else { /* Temporarily add the LF character back and send as body to the client */ - if(!data->req.no_body) { + if(!data->set.opt_no_body) { line[len] = '\n'; result = Curl_client_write(data, CLIENTWRITE_BODY, line, len + 1); line[len] = '\0'; @@ -1064,11 +1059,11 @@ static CURLcode smtp_state_command_resp(struct Curl_easy *data, int smtpcode, } else /* End of DO phase */ - smtp_state(data, SMTP_STOP); + state(data, SMTP_STOP); } else /* End of DO phase */ - smtp_state(data, SMTP_STOP); + state(data, SMTP_STOP); } } @@ -1145,7 +1140,7 @@ static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data, result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "DATA"); if(!result) - smtp_state(data, SMTP_DATA); + state(data, SMTP_DATA); } } } @@ -1172,7 +1167,7 @@ static CURLcode smtp_state_data_resp(struct Curl_easy *data, int smtpcode, Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); /* End of DO phase */ - smtp_state(data, SMTP_STOP); + state(data, SMTP_STOP); } return result; @@ -1192,7 +1187,7 @@ static CURLcode smtp_state_postdata_resp(struct Curl_easy *data, result = CURLE_WEIRD_SERVER_REPLY; /* End of DONE phase */ - smtp_state(data, SMTP_STOP); + state(data, SMTP_STOP); return result; } @@ -1274,7 +1269,7 @@ static CURLcode smtp_statemachine(struct Curl_easy *data, /* fallthrough, just stop! */ default: /* internal error */ - smtp_state(data, SMTP_STOP); + state(data, SMTP_STOP); break; } } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp)); @@ -1290,9 +1285,8 @@ static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done) struct smtp_conn *smtpc = &conn->proto.smtpc; if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) { - bool ssldone = FALSE; - result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); - smtpc->ssldone = ssldone; + result = Curl_ssl_connect_nonblocking(data, conn, FALSE, + FIRSTSOCKET, &smtpc->ssldone); if(result || !smtpc->ssldone) return result; } @@ -1379,7 +1373,7 @@ static CURLcode smtp_connect(struct Curl_easy *data, bool *done) return result; /* Start off waiting for the server greeting response */ - smtp_state(data, SMTP_SERVERGREET); + state(data, SMTP_SERVERGREET); result = smtp_multi_statemach(data, done); @@ -1419,7 +1413,7 @@ static CURLcode smtp_done(struct Curl_easy *data, CURLcode status, result = status; /* use the already set error code */ } else if(!data->set.connect_only && data->set.mail_rcpt && - (data->state.upload || data->set.mimepost.kind)) { + (data->set.upload || data->set.mimepost.kind)) { /* Calculate the EOB taking into account any terminating CRLF from the previous line of the email or the CRLF of the DATA command when there is "no mail data". RFC-5321, sect. 4.1.1.4. @@ -1461,7 +1455,7 @@ static CURLcode smtp_done(struct Curl_easy *data, CURLcode status, free(eob); } - smtp_state(data, SMTP_POSTDATA); + state(data, SMTP_POSTDATA); /* Run the state-machine */ result = smtp_block_statemach(data, conn, FALSE); @@ -1485,11 +1479,12 @@ static CURLcode smtp_perform(struct Curl_easy *data, bool *connected, { /* This is SMTP and no proxy */ CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; struct SMTP *smtp = data->req.p.smtp; DEBUGF(infof(data, "DO phase starts")); - if(data->req.no_body) { + if(data->set.opt_no_body) { /* Requested no body means no transfer */ smtp->transfer = PPTRANSFER_INFO; } @@ -1511,7 +1506,7 @@ static CURLcode smtp_perform(struct Curl_easy *data, bool *connected, smtp->eob = 2; /* Start the first command in the DO phase */ - if((data->state.upload || data->set.mimepost.kind) && data->set.mail_rcpt) + if((data->set.upload || data->set.mimepost.kind) && data->set.mail_rcpt) /* MAIL transfer */ result = smtp_perform_mail(data); else @@ -1524,7 +1519,7 @@ static CURLcode smtp_perform(struct Curl_easy *data, bool *connected, /* Run the state-machine */ result = smtp_multi_statemach(data, dophase_done); - *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); + *connected = conn->bits.tcpconnect[FIRSTSOCKET]; if(*dophase_done) DEBUGF(infof(data, "DO phase is complete")); @@ -1787,8 +1782,8 @@ static CURLcode smtp_parse_custom_request(struct Curl_easy *data) * calling function deems it to be) then the input will simply be returned in * the address part with the host name being NULL. */ -static CURLcode smtp_parse_address(const char *fqma, char **address, - struct hostname *host) +static CURLcode smtp_parse_address(struct Curl_easy *data, const char *fqma, + char **address, struct hostname *host) { CURLcode result = CURLE_OK; size_t length; @@ -1812,7 +1807,7 @@ static CURLcode smtp_parse_address(const char *fqma, char **address, host->name = host->name + 1; /* Attempt to convert the host name to IDN ACE */ - (void) Curl_idnconvert_hostname(host); + (void) Curl_idnconvert_hostname(data, host); /* If Curl_idnconvert_hostname() fails then we shall attempt to continue and send the host name using UTF-8 rather than as 7-bit ACE (which is diff --git a/contrib/libs/curl/lib/smtp.h b/contrib/libs/curl/lib/smtp.h index 7a04c21549..24c5589e43 100644 --- a/contrib/libs/curl/lib/smtp.h +++ b/contrib/libs/curl/lib/smtp.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2009 - 2022, 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 @@ -57,28 +57,28 @@ struct SMTP { curl_pp_transfer transfer; char *custom; /* Custom Request */ struct curl_slist *rcpt; /* Recipient list */ + bool rcpt_had_ok; /* Whether any of RCPT TO commands (depends on + total number of recipients) succeeded so far */ + bool trailing_crlf; /* Specifies if the trailing CRLF is present */ int rcpt_last_error; /* The last error received for RCPT TO command */ size_t eob; /* Number of bytes of the EOB (End Of Body) that have been received so far */ - BIT(rcpt_had_ok); /* Whether any of RCPT TO commands (depends on - total number of recipients) succeeded so far */ - BIT(trailing_crlf); /* Specifies if the trailing CRLF is present */ }; /* smtp_conn is used for struct connection-oriented data in the connectdata struct */ struct smtp_conn { struct pingpong pp; - struct SASL sasl; /* SASL-related storage */ smtpstate state; /* Always use smtp.c:state() to change state! */ + bool ssldone; /* Is connect() over SSL done? */ char *domain; /* Client address/name to send in the EHLO */ - BIT(ssldone); /* Is connect() over SSL done? */ - BIT(tls_supported); /* StartTLS capability supported by server */ - BIT(size_supported); /* If server supports SIZE extension according to + struct SASL sasl; /* SASL-related storage */ + bool tls_supported; /* StartTLS capability supported by server */ + bool size_supported; /* If server supports SIZE extension according to RFC 1870 */ - BIT(utf8_supported); /* If server supports SMTPUTF8 extension according + bool utf8_supported; /* If server supports SMTPUTF8 extension according to RFC 6531 */ - BIT(auth_supported); /* AUTH capability supported by server */ + bool auth_supported; /* AUTH capability supported by server */ }; extern const struct Curl_handler Curl_handler_smtp; diff --git a/contrib/libs/curl/lib/sockaddr.h b/contrib/libs/curl/lib/sockaddr.h index 5a6bb207dc..77ec833ee0 100644 --- a/contrib/libs/curl/lib/sockaddr.h +++ b/contrib/libs/curl/lib/sockaddr.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/socketpair.c b/contrib/libs/curl/lib/socketpair.c index 963e1406f6..0f8798f087 100644 --- a/contrib/libs/curl/lib/socketpair.c +++ b/contrib/libs/curl/lib/socketpair.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2019 - 2022, 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,8 +24,6 @@ #include "curl_setup.h" #include "socketpair.h" -#include "urldata.h" -#include "rand.h" #if !defined(HAVE_SOCKETPAIR) && !defined(CURL_DISABLE_SOCKETPAIR) #ifdef WIN32 @@ -67,7 +65,7 @@ int Curl_socketpair(int domain, int type, int protocol, union { struct sockaddr_in inaddr; struct sockaddr addr; - } a; + } a, a2; curl_socket_t listener; curl_socklen_t addrlen = sizeof(a.inaddr); int reuse = 1; @@ -87,22 +85,9 @@ int Curl_socketpair(int domain, int type, int protocol, socks[0] = socks[1] = CURL_SOCKET_BAD; -#if defined(WIN32) || defined(__CYGWIN__) - /* don't set SO_REUSEADDR on Windows */ - (void)reuse; -#ifdef SO_EXCLUSIVEADDRUSE - { - int exclusive = 1; - if(setsockopt(listener, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, - (char *)&exclusive, (curl_socklen_t)sizeof(exclusive)) == -1) - goto error; - } -#endif -#else if(setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, (curl_socklen_t)sizeof(reuse)) == -1) goto error; -#endif if(bind(listener, &a.addr, sizeof(a.inaddr)) == -1) goto error; if(getsockname(listener, &a.addr, &addrlen) == -1 || @@ -122,68 +107,29 @@ int Curl_socketpair(int domain, int type, int protocol, pfd[0].fd = listener; pfd[0].events = POLLIN; pfd[0].revents = 0; - (void)Curl_poll(pfd, 1, 1000); /* one second */ + (void)Curl_poll(pfd, 1, 10*1000); /* 10 seconds */ socks[1] = accept(listener, NULL, NULL); if(socks[1] == CURL_SOCKET_BAD) goto error; - else { - struct curltime start = Curl_now(); - char rnd[9]; - char check[sizeof(rnd)]; - char *p = &check[0]; - size_t s = sizeof(check); - - if(Curl_rand(NULL, (unsigned char *)rnd, sizeof(rnd))) - goto error; - - /* write data to the socket */ - swrite(socks[0], rnd, sizeof(rnd)); - /* verify that we read the correct data */ - do { - ssize_t nread; - - pfd[0].fd = socks[1]; - pfd[0].events = POLLIN; - pfd[0].revents = 0; - (void)Curl_poll(pfd, 1, 1000); /* one second */ - nread = sread(socks[1], p, s); - if(nread == -1) { - int sockerr = SOCKERRNO; - /* Don't block forever */ - if(Curl_timediff(Curl_now(), start) > (60 * 1000)) - goto error; - if( -#ifdef WSAEWOULDBLOCK - /* This is how Windows does it */ - (WSAEWOULDBLOCK == sockerr) -#else - /* errno may be EWOULDBLOCK or on some systems EAGAIN when it - returned due to its inability to send off data without - blocking. We therefore treat both error codes the same here */ - (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || - (EINTR == sockerr) || (EINPROGRESS == sockerr) -#endif - ) { - continue; - } - goto error; - } - s -= nread; - if(s) { - p += nread; - continue; - } - if(memcmp(rnd, check, sizeof(check))) - goto error; - break; - } while(1); - } + /* verify that nothing else connected */ + addrlen = sizeof(a.inaddr); + if(getsockname(socks[0], &a.addr, &addrlen) == -1 || + addrlen < (int)sizeof(a.inaddr)) + goto error; + addrlen = sizeof(a2.inaddr); + if(getpeername(socks[1], &a2.addr, &addrlen) == -1 || + addrlen < (int)sizeof(a2.inaddr)) + goto error; + if(a.inaddr.sin_family != a2.inaddr.sin_family || + a.inaddr.sin_addr.s_addr != a2.inaddr.sin_addr.s_addr || + a.inaddr.sin_port != a2.inaddr.sin_port) + goto error; sclose(listener); return 0; -error: + error: sclose(listener); sclose(socks[0]); sclose(socks[1]); diff --git a/contrib/libs/curl/lib/socketpair.h b/contrib/libs/curl/lib/socketpair.h index 306ab5dc4c..de70df673a 100644 --- a/contrib/libs/curl/lib/socketpair.h +++ b/contrib/libs/curl/lib/socketpair.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2019 - 2022, 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 diff --git a/contrib/libs/curl/lib/socks.c b/contrib/libs/curl/lib/socks.c index c492d663c4..52c29880a6 100644 --- a/contrib/libs/curl/lib/socks.c +++ b/contrib/libs/curl/lib/socks.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -36,52 +36,17 @@ #include "urldata.h" #include "sendf.h" #include "select.h" -#include "cfilters.h" #include "connect.h" #include "timeval.h" #include "socks.h" #include "multiif.h" /* for getsock macros */ #include "inet_pton.h" -#include "url.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" -/* for the (SOCKS) connect state machine */ -enum connect_t { - CONNECT_INIT, - CONNECT_SOCKS_INIT, /* 1 */ - CONNECT_SOCKS_SEND, /* 2 waiting to send more first data */ - CONNECT_SOCKS_READ_INIT, /* 3 set up read */ - CONNECT_SOCKS_READ, /* 4 read server response */ - CONNECT_GSSAPI_INIT, /* 5 */ - CONNECT_AUTH_INIT, /* 6 setup outgoing auth buffer */ - CONNECT_AUTH_SEND, /* 7 send auth */ - CONNECT_AUTH_READ, /* 8 read auth response */ - CONNECT_REQ_INIT, /* 9 init SOCKS "request" */ - CONNECT_RESOLVING, /* 10 */ - CONNECT_RESOLVED, /* 11 */ - CONNECT_RESOLVE_REMOTE, /* 12 */ - CONNECT_REQ_SEND, /* 13 */ - CONNECT_REQ_SENDING, /* 14 */ - CONNECT_REQ_READ, /* 15 */ - CONNECT_REQ_READ_MORE, /* 16 */ - CONNECT_DONE /* 17 connected fine to the remote or the SOCKS proxy */ -}; - -struct socks_state { - enum connect_t state; - ssize_t outstanding; /* send this many bytes more */ - unsigned char *outp; /* send from this pointer */ - - const char *hostname; - int remote_port; - const char *proxy_user; - const char *proxy_password; -}; - #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) /* * Helper read-from-socket functions. Does the same as Curl_read() but it @@ -89,8 +54,8 @@ struct socks_state { * * This is STUPID BLOCKING behavior. Only used by the SOCKS GSSAPI functions. */ -int Curl_blockread_all(struct Curl_cfilter *cf, - struct Curl_easy *data, /* transfer */ +int Curl_blockread_all(struct Curl_easy *data, /* transfer */ + curl_socket_t sockfd, /* read from this socket */ char *buf, /* store read data here */ ssize_t buffersize, /* max amount to read */ ssize_t *n) /* amount bytes read */ @@ -98,8 +63,6 @@ int Curl_blockread_all(struct Curl_cfilter *cf, ssize_t nread = 0; ssize_t allread = 0; int result; - CURLcode err = CURLE_OK; - *n = 0; for(;;) { timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); @@ -110,19 +73,15 @@ int Curl_blockread_all(struct Curl_cfilter *cf, } if(!timeout_ms) timeout_ms = TIMEDIFF_T_MAX; - if(SOCKET_READABLE(cf->conn->sock[cf->sockindex], timeout_ms) <= 0) { + if(SOCKET_READABLE(sockfd, timeout_ms) <= 0) { result = ~CURLE_OK; break; } - nread = Curl_conn_cf_recv(cf->next, data, buf, buffersize, &err); - if(nread <= 0) { - result = err; - if(CURLE_AGAIN == err) - continue; - if(err) { - break; - } - } + result = Curl_read_plain(sockfd, buf, buffersize, &nread); + if(CURLE_AGAIN == result) + continue; + if(result) + break; if(buffersize == nread) { allread += nread; @@ -145,23 +104,24 @@ int Curl_blockread_all(struct Curl_cfilter *cf, #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) #define DEBUG_AND_VERBOSE -#define sxstate(x,d,y) socksstate(x,d,y, __LINE__) +#define sxstate(x,y) socksstate(x,y, __LINE__) #else -#define sxstate(x,d,y) socksstate(x,d,y) +#define sxstate(x,y) socksstate(x,y) #endif /* always use this function to change state, to make debugging easier */ -static void socksstate(struct socks_state *sx, struct Curl_easy *data, +static void socksstate(struct Curl_easy *data, enum connect_t state #ifdef DEBUG_AND_VERBOSE , int lineno #endif ) { - enum connect_t oldstate = sx->state; + struct connectdata *conn = data->conn; + enum connect_t oldstate = conn->cnnct.state; #ifdef DEBUG_AND_VERBOSE /* synced with the state list in urldata.h */ - static const char * const socks_statename[] = { + static const char * const statename[] = { "INIT", "SOCKS_INIT", "SOCKS_SEND", @@ -183,81 +143,38 @@ static void socksstate(struct socks_state *sx, struct Curl_easy *data, }; #endif - (void)data; if(oldstate == state) /* don't bother when the new state is the same as the old state */ return; - sx->state = state; + conn->cnnct.state = state; #ifdef DEBUG_AND_VERBOSE infof(data, - "SXSTATE: %s => %s; line %d", - socks_statename[oldstate], socks_statename[sx->state], + "SXSTATE: %s => %s conn %p; line %d", + statename[oldstate], statename[conn->cnnct.state], conn, lineno); #endif } -static CURLproxycode socks_state_send(struct Curl_cfilter *cf, - struct socks_state *sx, - struct Curl_easy *data, - CURLproxycode failcode, - const char *description) +int Curl_SOCKS_getsock(struct connectdata *conn, curl_socket_t *sock, + int sockindex) { - ssize_t nwritten; - CURLcode result; - - nwritten = Curl_conn_cf_send(cf->next, data, (char *)sx->outp, - sx->outstanding, &result); - if(nwritten <= 0) { - if(CURLE_AGAIN == result) { - return CURLPX_OK; - } - else if(CURLE_OK == result) { - /* connection closed */ - failf(data, "connection to proxy closed"); - return CURLPX_CLOSED; - } - failf(data, "Failed to send %s: %s", description, - curl_easy_strerror(result)); - return failcode; - } - DEBUGASSERT(sx->outstanding >= nwritten); - /* not done, remain in state */ - sx->outstanding -= nwritten; - sx->outp += nwritten; - return CURLPX_OK; -} - -static CURLproxycode socks_state_recv(struct Curl_cfilter *cf, - struct socks_state *sx, - struct Curl_easy *data, - CURLproxycode failcode, - const char *description) -{ - ssize_t nread; - CURLcode result; - - nread = Curl_conn_cf_recv(cf->next, data, (char *)sx->outp, - sx->outstanding, &result); - if(nread <= 0) { - if(CURLE_AGAIN == result) { - return CURLPX_OK; - } - else if(CURLE_OK == result) { - /* connection closed */ - failf(data, "connection to proxy closed"); - return CURLPX_CLOSED; - } - failf(data, "SOCKS4: Failed receiving %s: %s", description, - curl_easy_strerror(result)); - return failcode; + int rc = 0; + sock[0] = conn->sock[sockindex]; + switch(conn->cnnct.state) { + case CONNECT_RESOLVING: + case CONNECT_SOCKS_READ: + case CONNECT_AUTH_READ: + case CONNECT_REQ_READ: + case CONNECT_REQ_READ_MORE: + rc = GETSOCK_READSOCK(0); + break; + default: + rc = GETSOCK_WRITESOCK(0); + break; } - /* remain in reading state */ - DEBUGASSERT(sx->outstanding >= nread); - sx->outstanding -= nread; - sx->outp += nread; - return CURLPX_OK; + return rc; } /* @@ -271,31 +188,39 @@ static CURLproxycode socks_state_recv(struct Curl_cfilter *cf, * Set protocol4a=true for "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)" * Nonsupport "Identification Protocol (RFC1413)" */ -static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf, - struct socks_state *sx, - struct Curl_easy *data) +CURLproxycode Curl_SOCKS4(const char *proxy_user, + const char *hostname, + int remote_port, + int sockindex, + struct Curl_easy *data, + bool *done) { - struct connectdata *conn = cf->conn; + struct connectdata *conn = data->conn; const bool protocol4a = (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE; unsigned char *socksreq = (unsigned char *)data->state.buffer; CURLcode result; - CURLproxycode presult; + curl_socket_t sockfd = conn->sock[sockindex]; + struct connstate *sx = &conn->cnnct; struct Curl_dns_entry *dns = NULL; + ssize_t actualread; + ssize_t written; /* make sure that the buffer is at least 600 bytes */ DEBUGASSERT(READBUFFER_MIN >= 600); + if(!SOCKS_STATE(sx->state) && !*done) + sxstate(data, CONNECT_SOCKS_INIT); + switch(sx->state) { case CONNECT_SOCKS_INIT: /* SOCKS4 can only do IPv4, insist! */ conn->ip_version = CURL_IPRESOLVE_V4; if(conn->bits.httpproxy) infof(data, "SOCKS4%s: connecting to HTTP proxy %s port %d", - protocol4a ? "a" : "", sx->hostname, sx->remote_port); + protocol4a ? "a" : "", hostname, remote_port); - infof(data, "SOCKS4 communication to %s:%d", - sx->hostname, sx->remote_port); + infof(data, "SOCKS4 communication to %s:%d", hostname, remote_port); /* * Compose socks4 request @@ -310,40 +235,40 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf, socksreq[0] = 4; /* version (SOCKS4) */ socksreq[1] = 1; /* connect */ - socksreq[2] = (unsigned char)((sx->remote_port >> 8) & 0xff); /* MSB */ - socksreq[3] = (unsigned char)(sx->remote_port & 0xff); /* LSB */ + socksreq[2] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */ + socksreq[3] = (unsigned char)(remote_port & 0xff); /* PORT LSB */ /* DNS resolve only for SOCKS4, not SOCKS4a */ if(!protocol4a) { enum resolve_t rc = - Curl_resolv(data, sx->hostname, sx->remote_port, TRUE, &dns); + Curl_resolv(data, hostname, remote_port, FALSE, &dns); if(rc == CURLRESOLV_ERROR) return CURLPX_RESOLVE_HOST; else if(rc == CURLRESOLV_PENDING) { - sxstate(sx, data, CONNECT_RESOLVING); - infof(data, "SOCKS4 non-blocking resolve of %s", sx->hostname); + sxstate(data, CONNECT_RESOLVING); + infof(data, "SOCKS4 non-blocking resolve of %s", hostname); return CURLPX_OK; } - sxstate(sx, data, CONNECT_RESOLVED); + sxstate(data, CONNECT_RESOLVED); goto CONNECT_RESOLVED; } /* socks4a doesn't resolve anything locally */ - sxstate(sx, data, CONNECT_REQ_INIT); + sxstate(data, CONNECT_REQ_INIT); goto CONNECT_REQ_INIT; case CONNECT_RESOLVING: /* check if we have the name resolved by now */ - dns = Curl_fetch_addr(data, sx->hostname, (int)conn->port); + dns = Curl_fetch_addr(data, hostname, (int)conn->port); if(dns) { #ifdef CURLRES_ASYNCH data->state.async.dns = dns; data->state.async.done = TRUE; #endif - infof(data, "Hostname '%s' was found", sx->hostname); - sxstate(sx, data, CONNECT_RESOLVED); + infof(data, "Hostname '%s' was found", hostname); + sxstate(data, CONNECT_RESOLVED); } else { result = Curl_resolv_check(data, &dns); @@ -354,7 +279,7 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf, } } /* FALLTHROUGH */ -CONNECT_RESOLVED: + CONNECT_RESOLVED: case CONNECT_RESOLVED: { struct Curl_addrinfo *hp = NULL; /* @@ -384,30 +309,30 @@ CONNECT_RESOLVED: Curl_resolv_unlock(data, dns); /* not used anymore from now on */ } else - failf(data, "SOCKS4 connection to %s not supported", sx->hostname); + failf(data, "SOCKS4 connection to %s not supported", hostname); } else failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", - sx->hostname); + hostname); if(!hp) return CURLPX_RESOLVE_HOST; } /* FALLTHROUGH */ -CONNECT_REQ_INIT: + CONNECT_REQ_INIT: case CONNECT_REQ_INIT: /* * This is currently not supporting "Identification Protocol (RFC1413)". */ socksreq[8] = 0; /* ensure empty userid is NUL-terminated */ - if(sx->proxy_user) { - size_t plen = strlen(sx->proxy_user); + if(proxy_user) { + size_t plen = strlen(proxy_user); if(plen >= (size_t)data->set.buffer_size - 8) { failf(data, "Too long SOCKS proxy user name, can't use"); return CURLPX_LONG_USER; } /* copy the proxy name WITH trailing zero */ - memcpy(socksreq + 8, sx->proxy_user, plen + 1); + memcpy(socksreq + 8, proxy_user, plen + 1); } /* @@ -425,9 +350,9 @@ CONNECT_REQ_INIT: socksreq[6] = 0; socksreq[7] = 1; /* append hostname */ - hostnamelen = strlen(sx->hostname) + 1; /* length including NUL */ + hostnamelen = strlen(hostname) + 1; /* length including NUL */ if(hostnamelen <= 255) - strcpy((char *)socksreq + packetsize, sx->hostname); + strcpy((char *)socksreq + packetsize, hostname); else { failf(data, "SOCKS4: too long host name"); return CURLPX_LONG_HOSTNAME; @@ -436,36 +361,51 @@ CONNECT_REQ_INIT: } sx->outp = socksreq; sx->outstanding = packetsize; - sxstate(sx, data, CONNECT_REQ_SENDING); + sxstate(data, CONNECT_REQ_SENDING); } /* FALLTHROUGH */ case CONNECT_REQ_SENDING: /* Send request */ - presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT, - "SOCKS4 connect request"); - if(CURLPX_OK != presult) - return presult; - else if(sx->outstanding) { - /* remain in sending state */ + result = Curl_write_plain(data, sockfd, (char *)sx->outp, + sx->outstanding, &written); + if(result && (CURLE_AGAIN != result)) { + failf(data, "Failed to send SOCKS4 connect request."); + return CURLPX_SEND_CONNECT; + } + if(written != sx->outstanding) { + /* not done, remain in state */ + sx->outstanding -= written; + sx->outp += written; return CURLPX_OK; } + /* done sending! */ sx->outstanding = 8; /* receive data size */ sx->outp = socksreq; - sxstate(sx, data, CONNECT_SOCKS_READ); + sxstate(data, CONNECT_SOCKS_READ); /* FALLTHROUGH */ case CONNECT_SOCKS_READ: /* Receive response */ - presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT, - "connect request ack"); - if(CURLPX_OK != presult) - return presult; - else if(sx->outstanding) { + result = Curl_read_plain(sockfd, (char *)sx->outp, + sx->outstanding, &actualread); + if(result && (CURLE_AGAIN != result)) { + failf(data, "SOCKS4: Failed receiving connect request ack: %s", + curl_easy_strerror(result)); + return CURLPX_RECV_CONNECT; + } + else if(!result && !actualread) { + /* connection closed */ + failf(data, "connection to proxy closed"); + return CURLPX_CLOSED; + } + else if(actualread != sx->outstanding) { /* remain in reading state */ + sx->outstanding -= actualread; + sx->outp += actualread; return CURLPX_OK; } - sxstate(sx, data, CONNECT_DONE); + sxstate(data, CONNECT_DONE); break; default: /* lots of unused states in SOCKS4 */ break; @@ -538,6 +478,7 @@ CONNECT_REQ_INIT: return CURLPX_UNKNOWN_FAIL; } + *done = TRUE; return CURLPX_OK; /* Proxy was successful! */ } @@ -545,9 +486,13 @@ CONNECT_REQ_INIT: * This function logs in to a SOCKS5 proxy and sends the specifics to the final * destination server. */ -static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, - struct socks_state *sx, - struct Curl_easy *data) +CURLproxycode Curl_SOCKS5(const char *proxy_user, + const char *proxy_password, + const char *hostname, + int remote_port, + int sockindex, + struct Curl_easy *data, + bool *done) { /* According to the RFC1928, section "6. Replies". This is what a SOCK5 @@ -565,25 +510,31 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, o REP Reply field: o X'00' succeeded */ - struct connectdata *conn = cf->conn; + struct connectdata *conn = data->conn; unsigned char *socksreq = (unsigned char *)data->state.buffer; + char dest[256] = "unknown"; /* printable hostname:port */ int idx; + ssize_t actualread; + ssize_t written; CURLcode result; - CURLproxycode presult; + curl_socket_t sockfd = conn->sock[sockindex]; bool socks5_resolve_local = (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE; - const size_t hostname_len = strlen(sx->hostname); + const size_t hostname_len = strlen(hostname); ssize_t len = 0; - const unsigned char auth = data->set.socks5auth; + const unsigned long auth = data->set.socks5auth; bool allow_gssapi = FALSE; + struct connstate *sx = &conn->cnnct; struct Curl_dns_entry *dns = NULL; - DEBUGASSERT(auth & (CURLAUTH_BASIC | CURLAUTH_GSSAPI)); + if(!SOCKS_STATE(sx->state) && !*done) + sxstate(data, CONNECT_SOCKS_INIT); + switch(sx->state) { case CONNECT_SOCKS_INIT: if(conn->bits.httpproxy) infof(data, "SOCKS5: connecting to HTTP proxy %s port %d", - sx->hostname, sx->remote_port); + hostname, remote_port); /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */ if(!socks5_resolve_local && hostname_len > 255) { @@ -594,11 +545,11 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI)) infof(data, - "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %u", + "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %lu", auth); if(!(auth & CURLAUTH_BASIC)) /* disable username/password auth */ - sx->proxy_user = NULL; + proxy_user = NULL; #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) if(auth & CURLAUTH_GSSAPI) allow_gssapi = TRUE; @@ -610,45 +561,59 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, socksreq[idx++] = 0; /* no authentication */ if(allow_gssapi) socksreq[idx++] = 1; /* GSS-API */ - if(sx->proxy_user) + if(proxy_user) socksreq[idx++] = 2; /* username/password */ /* write the number of authentication methods */ socksreq[1] = (unsigned char) (idx - 2); - sx->outp = socksreq; - sx->outstanding = idx; - presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT, - "initial SOCKS5 request"); - if(CURLPX_OK != presult) - return presult; - else if(sx->outstanding) { - /* remain in sending state */ + result = Curl_write_plain(data, sockfd, (char *)socksreq, idx, &written); + if(result && (CURLE_AGAIN != result)) { + failf(data, "Unable to send initial SOCKS5 request."); + return CURLPX_SEND_CONNECT; + } + if(written != idx) { + sxstate(data, CONNECT_SOCKS_SEND); + sx->outstanding = idx - written; + sx->outp = &socksreq[written]; return CURLPX_OK; } - sxstate(sx, data, CONNECT_SOCKS_READ); + sxstate(data, CONNECT_SOCKS_READ); goto CONNECT_SOCKS_READ_INIT; case CONNECT_SOCKS_SEND: - presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT, - "initial SOCKS5 request"); - if(CURLPX_OK != presult) - return presult; - else if(sx->outstanding) { - /* remain in sending state */ + result = Curl_write_plain(data, sockfd, (char *)sx->outp, + sx->outstanding, &written); + if(result && (CURLE_AGAIN != result)) { + failf(data, "Unable to send initial SOCKS5 request."); + return CURLPX_SEND_CONNECT; + } + if(written != sx->outstanding) { + /* not done, remain in state */ + sx->outstanding -= written; + sx->outp += written; return CURLPX_OK; } /* FALLTHROUGH */ -CONNECT_SOCKS_READ_INIT: + CONNECT_SOCKS_READ_INIT: case CONNECT_SOCKS_READ_INIT: sx->outstanding = 2; /* expect two bytes */ sx->outp = socksreq; /* store it here */ /* FALLTHROUGH */ case CONNECT_SOCKS_READ: - presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT, - "initial SOCKS5 response"); - if(CURLPX_OK != presult) - return presult; - else if(sx->outstanding) { + result = Curl_read_plain(sockfd, (char *)sx->outp, + sx->outstanding, &actualread); + if(result && (CURLE_AGAIN != result)) { + failf(data, "Unable to receive initial SOCKS5 response."); + return CURLPX_RECV_CONNECT; + } + else if(!result && !actualread) { + /* connection closed */ + failf(data, "Connection to proxy closed"); + return CURLPX_CLOSED; + } + else if(actualread != sx->outstanding) { /* remain in reading state */ + sx->outstanding -= actualread; + sx->outp += actualread; return CURLPX_OK; } else if(socksreq[0] != 5) { @@ -657,18 +622,18 @@ CONNECT_SOCKS_READ_INIT: } else if(socksreq[1] == 0) { /* DONE! No authentication needed. Send request. */ - sxstate(sx, data, CONNECT_REQ_INIT); + sxstate(data, CONNECT_REQ_INIT); goto CONNECT_REQ_INIT; } else if(socksreq[1] == 2) { /* regular name + password authentication */ - sxstate(sx, data, CONNECT_AUTH_INIT); + sxstate(data, CONNECT_AUTH_INIT); goto CONNECT_AUTH_INIT; } #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) else if(allow_gssapi && (socksreq[1] == 1)) { - sxstate(sx, data, CONNECT_GSSAPI_INIT); - result = Curl_SOCKS5_gssapi_negotiate(cf, data); + sxstate(data, CONNECT_GSSAPI_INIT); + result = Curl_SOCKS5_gssapi_negotiate(sockindex, data); if(result) { failf(data, "Unable to negotiate SOCKS5 GSS-API context."); return CURLPX_GSSAPI; @@ -699,13 +664,13 @@ CONNECT_SOCKS_READ_INIT: default: /* do nothing! */ break; -CONNECT_AUTH_INIT: + CONNECT_AUTH_INIT: case CONNECT_AUTH_INIT: { /* Needs user name and password */ size_t proxy_user_len, proxy_password_len; - if(sx->proxy_user && sx->proxy_password) { - proxy_user_len = strlen(sx->proxy_user); - proxy_password_len = strlen(sx->proxy_password); + if(proxy_user && proxy_password) { + proxy_user_len = strlen(proxy_user); + proxy_password_len = strlen(proxy_password); } else { proxy_user_len = 0; @@ -722,50 +687,63 @@ CONNECT_AUTH_INIT: len = 0; socksreq[len++] = 1; /* username/pw subnegotiation version */ socksreq[len++] = (unsigned char) proxy_user_len; - if(sx->proxy_user && proxy_user_len) { + if(proxy_user && proxy_user_len) { /* the length must fit in a single byte */ - if(proxy_user_len > 255) { + if(proxy_user_len >= 255) { failf(data, "Excessive user name length for proxy auth"); return CURLPX_LONG_USER; } - memcpy(socksreq + len, sx->proxy_user, proxy_user_len); + memcpy(socksreq + len, proxy_user, proxy_user_len); } len += proxy_user_len; socksreq[len++] = (unsigned char) proxy_password_len; - if(sx->proxy_password && proxy_password_len) { + if(proxy_password && proxy_password_len) { /* the length must fit in a single byte */ if(proxy_password_len > 255) { failf(data, "Excessive password length for proxy auth"); return CURLPX_LONG_PASSWD; } - memcpy(socksreq + len, sx->proxy_password, proxy_password_len); + memcpy(socksreq + len, proxy_password, proxy_password_len); } len += proxy_password_len; - sxstate(sx, data, CONNECT_AUTH_SEND); + sxstate(data, CONNECT_AUTH_SEND); sx->outstanding = len; sx->outp = socksreq; } /* FALLTHROUGH */ case CONNECT_AUTH_SEND: - presult = socks_state_send(cf, sx, data, CURLPX_SEND_AUTH, - "SOCKS5 sub-negotiation request"); - if(CURLPX_OK != presult) - return presult; - else if(sx->outstanding) { - /* remain in sending state */ + result = Curl_write_plain(data, sockfd, (char *)sx->outp, + sx->outstanding, &written); + if(result && (CURLE_AGAIN != result)) { + failf(data, "Failed to send SOCKS5 sub-negotiation request."); + return CURLPX_SEND_AUTH; + } + if(sx->outstanding != written) { + /* remain in state */ + sx->outstanding -= written; + sx->outp += written; return CURLPX_OK; } sx->outp = socksreq; sx->outstanding = 2; - sxstate(sx, data, CONNECT_AUTH_READ); + sxstate(data, CONNECT_AUTH_READ); /* FALLTHROUGH */ case CONNECT_AUTH_READ: - presult = socks_state_recv(cf, sx, data, CURLPX_RECV_AUTH, - "SOCKS5 sub-negotiation response"); - if(CURLPX_OK != presult) - return presult; - else if(sx->outstanding) { - /* remain in reading state */ + result = Curl_read_plain(sockfd, (char *)sx->outp, + sx->outstanding, &actualread); + if(result && (CURLE_AGAIN != result)) { + failf(data, "Unable to receive SOCKS5 sub-negotiation response."); + return CURLPX_RECV_AUTH; + } + else if(!result && !actualread) { + /* connection closed */ + failf(data, "connection to proxy closed"); + return CURLPX_CLOSED; + } + else if(actualread != sx->outstanding) { + /* remain in state */ + sx->outstanding -= actualread; + sx->outp += actualread; return CURLPX_OK; } /* ignore the first (VER) byte */ @@ -776,36 +754,36 @@ CONNECT_AUTH_INIT: } /* Everything is good so far, user was authenticated! */ - sxstate(sx, data, CONNECT_REQ_INIT); + sxstate(data, CONNECT_REQ_INIT); /* FALLTHROUGH */ -CONNECT_REQ_INIT: + CONNECT_REQ_INIT: case CONNECT_REQ_INIT: if(socks5_resolve_local) { - enum resolve_t rc = Curl_resolv(data, sx->hostname, sx->remote_port, - TRUE, &dns); + enum resolve_t rc = Curl_resolv(data, hostname, remote_port, + FALSE, &dns); if(rc == CURLRESOLV_ERROR) return CURLPX_RESOLVE_HOST; if(rc == CURLRESOLV_PENDING) { - sxstate(sx, data, CONNECT_RESOLVING); + sxstate(data, CONNECT_RESOLVING); return CURLPX_OK; } - sxstate(sx, data, CONNECT_RESOLVED); + sxstate(data, CONNECT_RESOLVED); goto CONNECT_RESOLVED; } goto CONNECT_RESOLVE_REMOTE; case CONNECT_RESOLVING: /* check if we have the name resolved by now */ - dns = Curl_fetch_addr(data, sx->hostname, sx->remote_port); + dns = Curl_fetch_addr(data, hostname, remote_port); if(dns) { #ifdef CURLRES_ASYNCH data->state.async.dns = dns; data->state.async.done = TRUE; #endif - infof(data, "SOCKS5: hostname '%s' found", sx->hostname); + infof(data, "SOCKS5: hostname '%s' found", hostname); } if(!dns) { @@ -817,19 +795,21 @@ CONNECT_REQ_INIT: } } /* FALLTHROUGH */ -CONNECT_RESOLVED: + CONNECT_RESOLVED: case CONNECT_RESOLVED: { - char dest[MAX_IPADR_LEN] = "unknown"; /* printable address */ struct Curl_addrinfo *hp = NULL; + size_t destlen; if(dns) hp = dns->addr; if(!hp) { failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", - sx->hostname); + hostname); return CURLPX_RESOLVE_HOST; } Curl_printable_address(hp, dest, sizeof(dest)); + destlen = strlen(dest); + msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", remote_port); len = 0; socksreq[len++] = 5; /* version (SOCKS5) */ @@ -845,8 +825,7 @@ CONNECT_RESOLVED: socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i]; } - infof(data, "SOCKS5 connect to %s:%d (locally resolved)", dest, - sx->remote_port); + infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)", dest); } #ifdef ENABLE_IPV6 else if(hp->ai_family == AF_INET6) { @@ -860,8 +839,7 @@ CONNECT_RESOLVED: ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i]; } - infof(data, "SOCKS5 connect to [%s]:%d (locally resolved)", dest, - sx->remote_port); + infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)", dest); } #endif else { @@ -872,7 +850,7 @@ CONNECT_RESOLVED: Curl_resolv_unlock(data, dns); /* not used anymore from now on */ goto CONNECT_REQ_SEND; } -CONNECT_RESOLVE_REMOTE: + CONNECT_RESOLVE_REMOTE: case CONNECT_RESOLVE_REMOTE: /* Authentication is complete, now specify destination to the proxy */ len = 0; @@ -888,7 +866,7 @@ CONNECT_RESOLVE_REMOTE: #ifdef ENABLE_IPV6 if(conn->bits.ipv6_ip) { char ip6[16]; - if(1 != Curl_inet_pton(AF_INET6, sx->hostname, ip6)) + if(1 != Curl_inet_pton(AF_INET6, hostname, ip6)) return CURLPX_BAD_ADDRESS_TYPE; socksreq[len++] = 4; memcpy(&socksreq[len], ip6, sizeof(ip6)); @@ -896,7 +874,7 @@ CONNECT_RESOLVE_REMOTE: } else #endif - if(1 == Curl_inet_pton(AF_INET, sx->hostname, ip4)) { + if(1 == Curl_inet_pton(AF_INET, hostname, ip4)) { socksreq[len++] = 1; memcpy(&socksreq[len], ip4, sizeof(ip4)); len += sizeof(ip4); @@ -904,20 +882,20 @@ CONNECT_RESOLVE_REMOTE: else { socksreq[len++] = 3; socksreq[len++] = (char) hostname_len; /* one byte address length */ - memcpy(&socksreq[len], sx->hostname, hostname_len); /* w/o NULL */ + memcpy(&socksreq[len], hostname, hostname_len); /* address w/o NULL */ len += hostname_len; } infof(data, "SOCKS5 connect to %s:%d (remotely resolved)", - sx->hostname, sx->remote_port); + hostname, remote_port); } /* FALLTHROUGH */ -CONNECT_REQ_SEND: + CONNECT_REQ_SEND: case CONNECT_REQ_SEND: /* PORT MSB */ - socksreq[len++] = (unsigned char)((sx->remote_port >> 8) & 0xff); + socksreq[len++] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT LSB */ - socksreq[len++] = (unsigned char)(sx->remote_port & 0xff); + socksreq[len++] = (unsigned char)(remote_port & 0xff); #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) if(conn->socks5_gssapi_enctype) { @@ -927,15 +905,19 @@ CONNECT_REQ_SEND: #endif sx->outp = socksreq; sx->outstanding = len; - sxstate(sx, data, CONNECT_REQ_SENDING); + sxstate(data, CONNECT_REQ_SENDING); /* FALLTHROUGH */ case CONNECT_REQ_SENDING: - presult = socks_state_send(cf, sx, data, CURLPX_SEND_REQUEST, - "SOCKS5 connect request"); - if(CURLPX_OK != presult) - return presult; - else if(sx->outstanding) { - /* remain in send state */ + result = Curl_write_plain(data, sockfd, (char *)sx->outp, + sx->outstanding, &written); + if(result && (CURLE_AGAIN != result)) { + failf(data, "Failed to send SOCKS5 connect request."); + return CURLPX_SEND_REQUEST; + } + if(sx->outstanding != written) { + /* remain in state */ + sx->outstanding -= written; + sx->outp += written; return CURLPX_OK; } #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) @@ -946,18 +928,28 @@ CONNECT_REQ_SEND: #endif sx->outstanding = 10; /* minimum packet size is 10 */ sx->outp = socksreq; - sxstate(sx, data, CONNECT_REQ_READ); + sxstate(data, CONNECT_REQ_READ); /* FALLTHROUGH */ case CONNECT_REQ_READ: - presult = socks_state_recv(cf, sx, data, CURLPX_RECV_REQACK, - "SOCKS5 connect request ack"); - if(CURLPX_OK != presult) - return presult; - else if(sx->outstanding) { - /* remain in reading state */ + result = Curl_read_plain(sockfd, (char *)sx->outp, + sx->outstanding, &actualread); + if(result && (CURLE_AGAIN != result)) { + failf(data, "Failed to receive SOCKS5 connect request ack."); + return CURLPX_RECV_REQACK; + } + else if(!result && !actualread) { + /* connection closed */ + failf(data, "connection to proxy closed"); + return CURLPX_CLOSED; + } + else if(actualread != sx->outstanding) { + /* remain in state */ + sx->outstanding -= actualread; + sx->outp += actualread; return CURLPX_OK; } - else if(socksreq[0] != 5) { /* version */ + + if(socksreq[0] != 5) { /* version */ failf(data, "SOCKS5 reply has wrong version, version should be 5."); return CURLPX_BAD_VERSION; @@ -966,7 +958,7 @@ CONNECT_REQ_SEND: CURLproxycode rc = CURLPX_REPLY_UNASSIGNED; int code = socksreq[1]; failf(data, "Can't complete SOCKS5 connection to %s. (%d)", - sx->hostname, (unsigned char)socksreq[1]); + hostname, (unsigned char)socksreq[1]); if(code < 9) { /* RFC 1928 section 6 lists: */ static const CURLproxycode lookup[] = { @@ -1027,10 +1019,10 @@ CONNECT_REQ_SEND: if(len > 10) { sx->outstanding = len - 10; /* get the rest */ sx->outp = &socksreq[10]; - sxstate(sx, data, CONNECT_REQ_READ_MORE); + sxstate(data, CONNECT_REQ_READ_MORE); } else { - sxstate(sx, data, CONNECT_DONE); + sxstate(data, CONNECT_DONE); break; } #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) @@ -1038,216 +1030,29 @@ CONNECT_REQ_SEND: #endif /* FALLTHROUGH */ case CONNECT_REQ_READ_MORE: - presult = socks_state_recv(cf, sx, data, CURLPX_RECV_ADDRESS, - "SOCKS5 connect request address"); - if(CURLPX_OK != presult) - return presult; - else if(sx->outstanding) { - /* remain in reading state */ + result = Curl_read_plain(sockfd, (char *)sx->outp, + sx->outstanding, &actualread); + if(result && (CURLE_AGAIN != result)) { + failf(data, "Failed to receive SOCKS5 connect request ack."); + return CURLPX_RECV_ADDRESS; + } + else if(!result && !actualread) { + /* connection closed */ + failf(data, "connection to proxy closed"); + return CURLPX_CLOSED; + } + else if(actualread != sx->outstanding) { + /* remain in state */ + sx->outstanding -= actualread; + sx->outp += actualread; return CURLPX_OK; } - sxstate(sx, data, CONNECT_DONE); + sxstate(data, CONNECT_DONE); } infof(data, "SOCKS5 request granted."); + *done = TRUE; return CURLPX_OK; /* Proxy was successful! */ } -static CURLcode connect_SOCKS(struct Curl_cfilter *cf, - struct socks_state *sxstate, - struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - CURLproxycode pxresult = CURLPX_OK; - struct connectdata *conn = cf->conn; - - switch(conn->socks_proxy.proxytype) { - case CURLPROXY_SOCKS5: - case CURLPROXY_SOCKS5_HOSTNAME: - pxresult = do_SOCKS5(cf, sxstate, data); - break; - - case CURLPROXY_SOCKS4: - case CURLPROXY_SOCKS4A: - pxresult = do_SOCKS4(cf, sxstate, data); - break; - - default: - failf(data, "unknown proxytype option given"); - result = CURLE_COULDNT_CONNECT; - } /* switch proxytype */ - if(pxresult) { - result = CURLE_PROXY; - data->info.pxcode = pxresult; - } - - return result; -} - -static void socks_proxy_cf_free(struct Curl_cfilter *cf) -{ - struct socks_state *sxstate = cf->ctx; - if(sxstate) { - free(sxstate); - cf->ctx = NULL; - } -} - -/* After a TCP connection to the proxy has been verified, this function does - the next magic steps. If 'done' isn't set TRUE, it is not done yet and - must be called again. - - Note: this function's sub-functions call failf() - -*/ -static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking, bool *done) -{ - CURLcode result; - struct connectdata *conn = cf->conn; - int sockindex = cf->sockindex; - struct socks_state *sx = cf->ctx; - - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } - - result = cf->next->cft->do_connect(cf->next, data, blocking, done); - if(result || !*done) - return result; - - if(!sx) { - sx = calloc(sizeof(*sx), 1); - if(!sx) - return CURLE_OUT_OF_MEMORY; - cf->ctx = sx; - } - - if(sx->state == CONNECT_INIT) { - /* for the secondary socket (FTP), use the "connect to host" - * but ignore the "connect to port" (use the secondary port) - */ - sxstate(sx, data, CONNECT_SOCKS_INIT); - sx->hostname = - conn->bits.httpproxy ? - conn->http_proxy.host.name : - conn->bits.conn_to_host ? - conn->conn_to_host.name : - sockindex == SECONDARYSOCKET ? - conn->secondaryhostname : conn->host.name; - sx->remote_port = - conn->bits.httpproxy ? (int)conn->http_proxy.port : - sockindex == SECONDARYSOCKET ? conn->secondary_port : - conn->bits.conn_to_port ? conn->conn_to_port : - conn->remote_port; - sx->proxy_user = conn->socks_proxy.user; - sx->proxy_password = conn->socks_proxy.passwd; - } - - result = connect_SOCKS(cf, sx, data); - if(!result && sx->state == CONNECT_DONE) { - cf->connected = TRUE; - Curl_verboseconnect(data, conn); - socks_proxy_cf_free(cf); - } - - *done = cf->connected; - return result; -} - -static int socks_cf_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) -{ - struct socks_state *sx = cf->ctx; - int fds; - - fds = cf->next->cft->get_select_socks(cf->next, data, socks); - if(!fds && cf->next->connected && !cf->connected && sx) { - /* If we are not connected, the filter below is and has nothing - * to wait on, we determine what to wait for. */ - socks[0] = Curl_conn_cf_get_socket(cf, data); - switch(sx->state) { - case CONNECT_RESOLVING: - case CONNECT_SOCKS_READ: - case CONNECT_AUTH_READ: - case CONNECT_REQ_READ: - case CONNECT_REQ_READ_MORE: - fds = GETSOCK_READSOCK(0); - break; - default: - fds = GETSOCK_WRITESOCK(0); - break; - } - } - return fds; -} - -static void socks_proxy_cf_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - - DEBUGASSERT(cf->next); - cf->connected = FALSE; - socks_proxy_cf_free(cf); - cf->next->cft->do_close(cf->next, data); -} - -static void socks_proxy_cf_destroy(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - (void)data; - socks_proxy_cf_free(cf); -} - -static void socks_cf_get_host(struct Curl_cfilter *cf, - struct Curl_easy *data, - const char **phost, - const char **pdisplay_host, - int *pport) -{ - (void)data; - if(!cf->connected) { - *phost = cf->conn->socks_proxy.host.name; - *pdisplay_host = cf->conn->http_proxy.host.dispname; - *pport = (int)cf->conn->socks_proxy.port; - } - else { - cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport); - } -} - -struct Curl_cftype Curl_cft_socks_proxy = { - "SOCKS-PROXYY", - CF_TYPE_IP_CONNECT, - 0, - socks_proxy_cf_destroy, - socks_proxy_cf_connect, - socks_proxy_cf_close, - socks_cf_get_host, - socks_cf_get_select_socks, - Curl_cf_def_data_pending, - Curl_cf_def_send, - Curl_cf_def_recv, - Curl_cf_def_cntrl, - Curl_cf_def_conn_is_alive, - Curl_cf_def_conn_keep_alive, - Curl_cf_def_query, -}; - -CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data) -{ - struct Curl_cfilter *cf; - CURLcode result; - - (void)data; - result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, NULL); - if(!result) - Curl_conn_cf_insert_after(cf_at, cf); - return result; -} - #endif /* CURL_DISABLE_PROXY */ diff --git a/contrib/libs/curl/lib/socks.h b/contrib/libs/curl/lib/socks.h index a3adcc6e91..ff83aa5614 100644 --- a/contrib/libs/curl/lib/socks.h +++ b/contrib/libs/curl/lib/socks.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,25 +37,46 @@ * * This is STUPID BLOCKING behavior */ -int Curl_blockread_all(struct Curl_cfilter *cf, - struct Curl_easy *data, +int Curl_blockread_all(struct Curl_easy *data, + curl_socket_t sockfd, char *buf, ssize_t buffersize, ssize_t *n); +int Curl_SOCKS_getsock(struct connectdata *conn, + curl_socket_t *sock, + int sockindex); +/* + * This function logs in to a SOCKS4(a) proxy and sends the specifics to the + * final destination server. + */ +CURLproxycode Curl_SOCKS4(const char *proxy_name, + const char *hostname, + int remote_port, + int sockindex, + struct Curl_easy *data, + bool *done); + +/* + * This function logs in to a SOCKS5 proxy and sends the specifics to the + * final destination server. + */ +CURLproxycode Curl_SOCKS5(const char *proxy_name, + const char *proxy_password, + const char *hostname, + int remote_port, + int sockindex, + struct Curl_easy *data, + bool *done); + #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) /* * This function handles the SOCKS5 GSS-API negotiation and initialization */ -CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, +CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, struct Curl_easy *data); #endif -CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data); - -extern struct Curl_cftype Curl_cft_socks_proxy; - #endif /* CURL_DISABLE_PROXY */ #endif /* HEADER_CURL_SOCKS_H */ diff --git a/contrib/libs/curl/lib/socks_gssapi.c b/contrib/libs/curl/lib/socks_gssapi.c index 8a8d1ce28e..f9637660bf 100644 --- a/contrib/libs/curl/lib/socks_gssapi.c +++ b/contrib/libs/curl/lib/socks_gssapi.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) Markus Moeller, <markus_moeller@compuserve.com> + * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012, Markus Moeller, <markus_moeller@compuserve.com> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -30,7 +30,6 @@ #error #include "curl_gssapi.h" #include "urldata.h" #include "sendf.h" -#include "cfilters.h" #include "connect.h" #include "timeval.h" #include "socks.h" @@ -102,14 +101,14 @@ static int check_gss_err(struct Curl_easy *data, return 0; } -CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, +CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, struct Curl_easy *data) { - struct connectdata *conn = cf->conn; - curl_socket_t sock = conn->sock[cf->sockindex]; + struct connectdata *conn = data->conn; + curl_socket_t sock = conn->sock[sockindex]; CURLcode code; ssize_t actualread; - ssize_t nwritten; + ssize_t written; int result; OM_uint32 gss_major_status, gss_minor_status, gss_status; OM_uint32 gss_ret_flags; @@ -204,8 +203,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, us_length = htons((short)gss_send_token.length); memcpy(socksreq + 2, &us_length, sizeof(short)); - nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code); - if(code || (4 != nwritten)) { + code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written); + if(code || (4 != written)) { failf(data, "Failed to send GSS-API authentication request."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); @@ -214,10 +213,10 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, return CURLE_COULDNT_CONNECT; } - nwritten = Curl_conn_cf_send(cf->next, data, - (char *)gss_send_token.value, - gss_send_token.length, &code); - if(code || ((ssize_t)gss_send_token.length != nwritten)) { + code = Curl_write_plain(data, sock, (char *)gss_send_token.value, + gss_send_token.length, &written); + + if(code || ((ssize_t)gss_send_token.length != written)) { failf(data, "Failed to send GSS-API authentication token."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); @@ -243,7 +242,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, * +----+------+-----+----------------+ */ - result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); + result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive GSS-API authentication response."); gss_release_name(&gss_status, &server); @@ -282,7 +281,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, return CURLE_OUT_OF_MEMORY; } - result = Curl_blockread_all(cf, data, (char *)gss_recv_token.value, + result = Curl_blockread_all(data, sock, (char *)gss_recv_token.value, gss_recv_token.length, &actualread); if(result || (actualread != us_length)) { @@ -411,8 +410,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, memcpy(socksreq + 2, &us_length, sizeof(short)); } - nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code); - if(code || (4 != nwritten)) { + code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written); + if(code || (4 != written)) { failf(data, "Failed to send GSS-API encryption request."); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); @@ -421,18 +420,17 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(data->set.socks5_gssapi_nec) { memcpy(socksreq, &gss_enc, 1); - nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, &code); - if(code || ( 1 != nwritten)) { + code = Curl_write_plain(data, sock, socksreq, 1, &written); + if(code || ( 1 != written)) { failf(data, "Failed to send GSS-API encryption type."); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } } else { - nwritten = Curl_conn_cf_send(cf->next, data, - (char *)gss_w_token.value, - gss_w_token.length, &code); - if(code || ((ssize_t)gss_w_token.length != nwritten)) { + code = Curl_write_plain(data, sock, (char *)gss_w_token.value, + gss_w_token.length, &written); + if(code || ((ssize_t)gss_w_token.length != written)) { failf(data, "Failed to send GSS-API encryption type."); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); @@ -441,7 +439,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, gss_release_buffer(&gss_status, &gss_w_token); } - result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); + result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive GSS-API encryption response."); gss_delete_sec_context(&gss_status, &gss_context, NULL); @@ -472,7 +470,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OUT_OF_MEMORY; } - result = Curl_blockread_all(cf, data, (char *)gss_recv_token.value, + result = Curl_blockread_all(data, sock, (char *)gss_recv_token.value, gss_recv_token.length, &actualread); if(result || (actualread != us_length)) { diff --git a/contrib/libs/curl/lib/socks_sspi.c b/contrib/libs/curl/lib/socks_sspi.c index d1200ea037..210a0dfbc5 100644 --- a/contrib/libs/curl/lib/socks_sspi.c +++ b/contrib/libs/curl/lib/socks_sspi.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) Markus Moeller, <markus_moeller@compuserve.com> + * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012, 2011, Markus Moeller, <markus_moeller@compuserve.com> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -29,7 +29,6 @@ #include "urldata.h" #include "sendf.h" -#include "cfilters.h" #include "connect.h" #include "strerror.h" #include "timeval.h" @@ -63,11 +62,11 @@ static int check_sspi_err(struct Curl_easy *data, } /* This is the SSPI-using version of this function */ -CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, +CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, struct Curl_easy *data) { - struct connectdata *conn = cf->conn; - curl_socket_t sock = conn->sock[cf->sockindex]; + struct connectdata *conn = data->conn; + curl_socket_t sock = conn->sock[sockindex]; CURLcode code; ssize_t actualread; ssize_t written; @@ -207,7 +206,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, us_length = htons((short)sspi_send_token.cbBuffer); memcpy(socksreq + 2, &us_length, sizeof(short)); - written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code); + code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written); if(code || (4 != written)) { failf(data, "Failed to send SSPI authentication request."); free(service_name); @@ -220,9 +219,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, return CURLE_COULDNT_CONNECT; } - written = Curl_conn_cf_send(cf->next, data, - (char *)sspi_send_token.pvBuffer, - sspi_send_token.cbBuffer, &code); + code = Curl_write_plain(data, sock, (char *)sspi_send_token.pvBuffer, + sspi_send_token.cbBuffer, &written); if(code || (sspi_send_token.cbBuffer != (size_t)written)) { failf(data, "Failed to send SSPI authentication token."); free(service_name); @@ -262,7 +260,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, * +----+------+-----+----------------+ */ - result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); + result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive SSPI authentication response."); free(service_name); @@ -302,7 +300,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, s_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_OUT_OF_MEMORY; } - result = Curl_blockread_all(cf, data, (char *)sspi_recv_token.pvBuffer, + result = Curl_blockread_all(data, sock, (char *)sspi_recv_token.pvBuffer, sspi_recv_token.cbBuffer, &actualread); if(result || (actualread != us_length)) { @@ -470,7 +468,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, memcpy(socksreq + 2, &us_length, sizeof(short)); } - written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code); + code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written); if(code || (4 != written)) { failf(data, "Failed to send SSPI encryption request."); if(sspi_send_token.pvBuffer) @@ -481,7 +479,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(data->set.socks5_gssapi_nec) { memcpy(socksreq, &gss_enc, 1); - written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, &code); + code = Curl_write_plain(data, sock, (char *)socksreq, 1, &written); if(code || (1 != written)) { failf(data, "Failed to send SSPI encryption type."); s_pSecFn->DeleteSecurityContext(&sspi_context); @@ -489,9 +487,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, } } else { - written = Curl_conn_cf_send(cf->next, data, - (char *)sspi_send_token.pvBuffer, - sspi_send_token.cbBuffer, &code); + code = Curl_write_plain(data, sock, (char *)sspi_send_token.pvBuffer, + sspi_send_token.cbBuffer, &written); if(code || (sspi_send_token.cbBuffer != (size_t)written)) { failf(data, "Failed to send SSPI encryption type."); if(sspi_send_token.pvBuffer) @@ -503,7 +500,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); } - result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); + result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive SSPI encryption response."); s_pSecFn->DeleteSecurityContext(&sspi_context); @@ -535,7 +532,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, return CURLE_OUT_OF_MEMORY; } - result = Curl_blockread_all(cf, data, (char *)sspi_w_token[0].pvBuffer, + result = Curl_blockread_all(data, sock, (char *)sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer, &actualread); if(result || (actualread != us_length)) { diff --git a/contrib/libs/curl/lib/speedcheck.c b/contrib/libs/curl/lib/speedcheck.c index 580efbde75..3ddc43d2de 100644 --- a/contrib/libs/curl/lib/speedcheck.c +++ b/contrib/libs/curl/lib/speedcheck.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/speedcheck.h b/contrib/libs/curl/lib/speedcheck.h index bff2f32b77..cb44eb04ec 100644 --- a/contrib/libs/curl/lib/speedcheck.h +++ b/contrib/libs/curl/lib/speedcheck.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/splay.c b/contrib/libs/curl/lib/splay.c index 48e079b32a..33b44aa1c6 100644 --- a/contrib/libs/curl/lib/splay.c +++ b/contrib/libs/curl/lib/splay.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1997 - 2022, 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 diff --git a/contrib/libs/curl/lib/splay.h b/contrib/libs/curl/lib/splay.h index dd1d07ac2e..015e2ca52f 100644 --- a/contrib/libs/curl/lib/splay.h +++ b/contrib/libs/curl/lib/splay.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1997 - 2022, 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 diff --git a/contrib/libs/curl/lib/strcase.c b/contrib/libs/curl/lib/strcase.c index 7c0b4ef909..09d2a8a961 100644 --- a/contrib/libs/curl/lib/strcase.c +++ b/contrib/libs/curl/lib/strcase.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -83,13 +83,16 @@ char Curl_raw_tolower(char in) } /* - * curl_strequal() is for doing "raw" case insensitive strings. This is meant - * to be locale independent and only compare strings we know are safe for - * this. See https://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for - * further explanations as to why this function is necessary. + * Curl_strcasecompare() is for doing "raw" case insensitive strings. This is + * meant to be locale independent and only compare strings we know are safe + * for this. See + * https://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for some + * further explanation to why this function is necessary. + * + * @unittest: 1301 */ -static int casecompare(const char *first, const char *second) +int Curl_strcasecompare(const char *first, const char *second) { while(*first && *second) { if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second)) @@ -105,22 +108,25 @@ static int casecompare(const char *first, const char *second) return !*first == !*second; } -/* --- public function --- */ -int curl_strequal(const char *first, const char *second) +int Curl_safe_strcasecompare(const char *first, const char *second) { if(first && second) /* both pointers point to something then compare them */ - return casecompare(first, second); + return Curl_strcasecompare(first, second); /* if both pointers are NULL then treat them as equal */ return (NULL == first && NULL == second); } -static int ncasecompare(const char *first, const char *second, size_t max) +/* + * @unittest: 1301 + */ +int Curl_strncasecompare(const char *first, const char *second, size_t max) { while(*first && *second && max) { - if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second)) - return 0; + if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second)) { + break; + } max--; first++; second++; @@ -131,16 +137,6 @@ static int ncasecompare(const char *first, const char *second, size_t max) return Curl_raw_toupper(*first) == Curl_raw_toupper(*second); } -/* --- public function --- */ -int curl_strnequal(const char *first, const char *second, size_t max) -{ - if(first && second) - /* both pointers point to something then compare them */ - return ncasecompare(first, second, max); - - /* if both pointers are NULL then treat them as equal if max is non-zero */ - return (NULL == first && NULL == second && max); -} /* Copy an upper case version of the string from src to dest. The * strings may overlap. No more than n characters of the string are copied * (including any NUL) and the destination string will NOT be @@ -202,3 +198,14 @@ int Curl_timestrcmp(const char *a, const char *b) return a || b; return match; } + +/* --- public functions --- */ + +int curl_strequal(const char *first, const char *second) +{ + return Curl_strcasecompare(first, second); +} +int curl_strnequal(const char *first, const char *second, size_t max) +{ + return Curl_strncasecompare(first, second, max); +} diff --git a/contrib/libs/curl/lib/strcase.h b/contrib/libs/curl/lib/strcase.h index 8c50bbcba6..65a575385d 100644 --- a/contrib/libs/curl/lib/strcase.h +++ b/contrib/libs/curl/lib/strcase.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -35,8 +35,12 @@ * Result is 1 if text matches and 0 if not. */ -#define strcasecompare(a,b) curl_strequal(a,b) -#define strncasecompare(a,b,c) curl_strnequal(a,b,c) +#define strcasecompare(a,b) Curl_strcasecompare(a,b) +#define strncasecompare(a,b,c) Curl_strncasecompare(a,b,c) + +int Curl_strcasecompare(const char *first, const char *second); +int Curl_safe_strcasecompare(const char *first, const char *second); +int Curl_strncasecompare(const char *first, const char *second, size_t max); char Curl_raw_toupper(char in); char Curl_raw_tolower(char in); diff --git a/contrib/libs/curl/lib/strdup.c b/contrib/libs/curl/lib/strdup.c index 07a61391ae..ac22b6ddaf 100644 --- a/contrib/libs/curl/lib/strdup.c +++ b/contrib/libs/curl/lib/strdup.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,7 +37,7 @@ #include "memdebug.h" #ifndef HAVE_STRDUP -char *Curl_strdup(const char *str) +char *curlx_strdup(const char *str) { size_t len; char *newstr; diff --git a/contrib/libs/curl/lib/strdup.h b/contrib/libs/curl/lib/strdup.h index c3430b54d5..fb46808b83 100644 --- a/contrib/libs/curl/lib/strdup.h +++ b/contrib/libs/curl/lib/strdup.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -26,7 +26,7 @@ #include "curl_setup.h" #ifndef HAVE_STRDUP -char *Curl_strdup(const char *str); +extern char *curlx_strdup(const char *str); #endif #ifdef WIN32 wchar_t* Curl_wcsdup(const wchar_t* src); diff --git a/contrib/libs/curl/lib/strerror.c b/contrib/libs/curl/lib/strerror.c index fe755c4f55..4889097775 100644 --- a/contrib/libs/curl/lib/strerror.c +++ b/contrib/libs/curl/lib/strerror.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2004 - 2022, 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 @@ -181,13 +181,13 @@ curl_easy_strerror(CURLcode error) case CURLE_INTERFACE_FAILED: return "Failed binding local connection end"; - case CURLE_TOO_MANY_REDIRECTS: + case CURLE_TOO_MANY_REDIRECTS : return "Number of redirects hit maximum amount"; case CURLE_UNKNOWN_OPTION: return "An unknown option was passed in to libcurl"; - case CURLE_SETOPT_OPTION_SYNTAX: + case CURLE_SETOPT_OPTION_SYNTAX : return "Malformed option provided in a setopt"; case CURLE_GOT_NOTHING: @@ -550,9 +550,6 @@ curl_url_strerror(CURLUcode error) case CURLUE_BAD_USER: return "Bad user"; - case CURLUE_LACKS_IDN: - return "libcurl lacks IDN support"; - case CURLUE_LAST: break; } diff --git a/contrib/libs/curl/lib/strerror.h b/contrib/libs/curl/lib/strerror.h index 399712f8eb..658f16c10e 100644 --- a/contrib/libs/curl/lib/strerror.h +++ b/contrib/libs/curl/lib/strerror.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/strtok.c b/contrib/libs/curl/lib/strtok.c index d8e1e8183f..6120bcc28e 100644 --- a/contrib/libs/curl/lib/strtok.c +++ b/contrib/libs/curl/lib/strtok.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/strtok.h b/contrib/libs/curl/lib/strtok.h index 321cba2326..641a3daed8 100644 --- a/contrib/libs/curl/lib/strtok.h +++ b/contrib/libs/curl/lib/strtok.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/strtoofft.c b/contrib/libs/curl/lib/strtoofft.c index 077b25792e..30deb8c05b 100644 --- a/contrib/libs/curl/lib/strtoofft.c +++ b/contrib/libs/curl/lib/strtoofft.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -221,7 +221,6 @@ CURLofft curlx_strtoofft(const char *str, char **endp, int base, curl_off_t number; errno = 0; *num = 0; /* clear by default */ - DEBUGASSERT(base); /* starting now, avoid base zero */ while(*str && ISBLANK(*str)) str++; diff --git a/contrib/libs/curl/lib/strtoofft.h b/contrib/libs/curl/lib/strtoofft.h index 34d293ba38..311dae4403 100644 --- a/contrib/libs/curl/lib/strtoofft.h +++ b/contrib/libs/curl/lib/strtoofft.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/system_win32.c b/contrib/libs/curl/lib/system_win32.c index 0cdaf3b2fe..bede9c7dcd 100644 --- a/contrib/libs/curl/lib/system_win32.c +++ b/contrib/libs/curl/lib/system_win32.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/contrib/libs/curl/lib/system_win32.h b/contrib/libs/curl/lib/system_win32.h index 24899cb2d5..167804e3c5 100644 --- a/contrib/libs/curl/lib/system_win32.h +++ b/contrib/libs/curl/lib/system_win32.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/contrib/libs/curl/lib/telnet.c b/contrib/libs/curl/lib/telnet.c index 1d7a592e78..923c7f82bd 100644 --- a/contrib/libs/curl/lib/telnet.c +++ b/contrib/libs/curl/lib/telnet.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -571,7 +571,7 @@ void rec_do(struct Curl_easy *data, int option) sendsuboption(data, option); } else if(tn->subnegotiation[option] == CURL_YES) { - /* send information to achieve this option */ + /* send information to achieve this option*/ tn->us[option] = CURL_YES; send_negotiation(data, CURL_WILL, option); sendsuboption(data, option); @@ -770,39 +770,22 @@ static void printsub(struct Curl_easy *data, } } -#ifdef _MSC_VER -#pragma warning(push) -/* warning C4706: assignment within conditional expression */ -#pragma warning(disable:4706) -#endif -static bool str_is_nonascii(const char *str) -{ - char c; - while((c = *str++)) - if(c & 0x80) - return TRUE; - - return FALSE; -} -#ifdef _MSC_VER -#pragma warning(pop) -#endif - static CURLcode check_telnet_options(struct Curl_easy *data) { struct curl_slist *head; struct curl_slist *beg; + char option_keyword[128] = ""; + char option_arg[256] = ""; struct TELNET *tn = data->req.p.telnet; + struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; + int binary_option; /* Add the user name as an environment variable if it was given on the command line */ if(data->state.aptr.user) { - char buffer[256]; - if(str_is_nonascii(data->conn->user)) - return CURLE_BAD_FUNCTION_ARGUMENT; - msnprintf(buffer, sizeof(buffer), "USER,%s", data->conn->user); - beg = curl_slist_append(tn->telnet_vars, buffer); + msnprintf(option_arg, sizeof(option_arg), "USER,%s", conn->user); + beg = curl_slist_append(tn->telnet_vars, option_arg); if(!beg) { curl_slist_free_all(tn->telnet_vars); tn->telnet_vars = NULL; @@ -812,100 +795,68 @@ static CURLcode check_telnet_options(struct Curl_easy *data) tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES; } - for(head = data->set.telnet_options; head && !result; head = head->next) { - size_t olen; - char *option = head->data; - char *arg; - char *sep = strchr(option, '='); - if(sep) { - olen = sep - option; - arg = ++sep; - if(str_is_nonascii(arg)) + for(head = data->set.telnet_options; head; head = head->next) { + if(sscanf(head->data, "%127[^= ]%*[ =]%255s", + option_keyword, option_arg) == 2) { + + /* Terminal type */ + if(strcasecompare(option_keyword, "TTYPE")) { + strncpy(tn->subopt_ttype, option_arg, 31); + tn->subopt_ttype[31] = 0; /* String termination */ + tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES; continue; - switch(olen) { - case 5: - /* Terminal type */ - if(strncasecompare(option, "TTYPE", 5)) { - strncpy(tn->subopt_ttype, arg, 31); - tn->subopt_ttype[31] = 0; /* String termination */ - tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES; - } - else - result = CURLE_UNKNOWN_OPTION; - break; + } - case 8: - /* Display variable */ - if(strncasecompare(option, "XDISPLOC", 8)) { - strncpy(tn->subopt_xdisploc, arg, 127); - tn->subopt_xdisploc[127] = 0; /* String termination */ - tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES; - } - else - result = CURLE_UNKNOWN_OPTION; - break; + /* Display variable */ + if(strcasecompare(option_keyword, "XDISPLOC")) { + strncpy(tn->subopt_xdisploc, option_arg, 127); + tn->subopt_xdisploc[127] = 0; /* String termination */ + tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES; + continue; + } - case 7: - /* Environment variable */ - if(strncasecompare(option, "NEW_ENV", 7)) { - beg = curl_slist_append(tn->telnet_vars, arg); - if(!beg) { - result = CURLE_OUT_OF_MEMORY; - break; - } - tn->telnet_vars = beg; - tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES; + /* Environment variable */ + if(strcasecompare(option_keyword, "NEW_ENV")) { + beg = curl_slist_append(tn->telnet_vars, option_arg); + if(!beg) { + result = CURLE_OUT_OF_MEMORY; + break; } - else - result = CURLE_UNKNOWN_OPTION; - break; + tn->telnet_vars = beg; + tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES; + continue; + } - case 2: - /* Window Size */ - if(strncasecompare(option, "WS", 2)) { - char *p; - unsigned long x = strtoul(arg, &p, 10); - unsigned long y = 0; - if(x && (x <= 0xffff) && Curl_raw_tolower(*p) == 'x') { - p++; - y = strtoul(p, NULL, 10); - if(y && (y <= 0xffff)) { - tn->subopt_wsx = (unsigned short)x; - tn->subopt_wsy = (unsigned short)y; - tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES; - } - } - if(!y) { - failf(data, "Syntax error in telnet option: %s", head->data); - result = CURLE_SETOPT_OPTION_SYNTAX; - } + /* Window Size */ + if(strcasecompare(option_keyword, "WS")) { + if(sscanf(option_arg, "%hu%*[xX]%hu", + &tn->subopt_wsx, &tn->subopt_wsy) == 2) + tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES; + else { + failf(data, "Syntax error in telnet option: %s", head->data); + result = CURLE_SETOPT_OPTION_SYNTAX; + break; } - else - result = CURLE_UNKNOWN_OPTION; - break; + continue; + } - case 6: - /* To take care or not of the 8th bit in data exchange */ - if(strncasecompare(option, "BINARY", 6)) { - int binary_option = atoi(arg); - if(binary_option != 1) { - tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO; - tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO; - } + /* To take care or not of the 8th bit in data exchange */ + if(strcasecompare(option_keyword, "BINARY")) { + binary_option = atoi(option_arg); + if(binary_option != 1) { + tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO; + tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO; } - else - result = CURLE_UNKNOWN_OPTION; - break; - default: - failf(data, "Unknown telnet option %s", head->data); - result = CURLE_UNKNOWN_OPTION; - break; + continue; } + + failf(data, "Unknown telnet option %s", head->data); + result = CURLE_UNKNOWN_OPTION; + break; } - else { - failf(data, "Syntax error in telnet option: %s", head->data); - result = CURLE_SETOPT_OPTION_SYNTAX; - } + failf(data, "Syntax error in telnet option: %s", head->data); + result = CURLE_SETOPT_OPTION_SYNTAX; + break; } if(result) { @@ -930,6 +881,8 @@ static void suboption(struct Curl_easy *data) ssize_t bytes_written; size_t len; int err; + char varname[128] = ""; + char varval[128] = ""; struct TELNET *tn = data->req.p.telnet; struct connectdata *conn = data->conn; @@ -967,18 +920,19 @@ static void suboption(struct Curl_easy *data) for(v = tn->telnet_vars; v; v = v->next) { size_t tmplen = (strlen(v->data) + 1); - /* Add the variable if it fits */ + /* Add the variable only if it fits */ if(len + tmplen < (int)sizeof(temp)-6) { - char *s = strchr(v->data, ','); - if(!s) + int rv; + char sep[2] = ""; + varval[0] = 0; + rv = sscanf(v->data, "%127[^,]%1[,]%127s", varname, sep, varval); + if(rv == 1) len += msnprintf((char *)&temp[len], sizeof(temp) - len, - "%c%s", CURL_NEW_ENV_VAR, v->data); - else { - size_t vlen = s - v->data; + "%c%s", CURL_NEW_ENV_VAR, varname); + else if(rv >= 2) len += msnprintf((char *)&temp[len], sizeof(temp) - len, - "%c%.*s%c%s", CURL_NEW_ENV_VAR, - (int)vlen, v->data, CURL_NEW_ENV_VALUE, ++s); - } + "%c%s%c%s", CURL_NEW_ENV_VAR, varname, + CURL_NEW_ENV_VALUE, varval); } } msnprintf((char *)&temp[len], sizeof(temp) - len, @@ -1110,7 +1064,7 @@ CURLcode telrcv(struct Curl_easy *data, break; case CURL_TS_IAC: -process_iac: + process_iac: DEBUGASSERT(startwrite < 0); switch(c) { case CURL_WILL: @@ -1246,7 +1200,7 @@ static CURLcode send_telnet_data(struct Curl_easy *data, j = 0; for(i = 0; i < nread; i++) { - outbuf[j++] = (unsigned char)buffer[i]; + outbuf[j++] = buffer[i]; if((unsigned char)buffer[i] == CURL_IAC) outbuf[j++] = CURL_IAC; } @@ -1294,6 +1248,9 @@ static CURLcode telnet_done(struct Curl_easy *data, curl_slist_free_all(tn->telnet_vars); tn->telnet_vars = NULL; + + Curl_safefree(data->req.p.telnet); + return CURLE_OK; } @@ -1534,7 +1491,6 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) } while(keepon) { - DEBUGF(infof(data, "telnet_do, poll %d fds", poll_cnt)); switch(Curl_poll(pfd, poll_cnt, interval_ms)) { case -1: /* error, stop reading */ keepon = FALSE; @@ -1553,13 +1509,6 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) /* returned not-zero, this an error */ if(result) { keepon = FALSE; - /* TODO: in test 1452, macOS sees a ECONNRESET sometimes? - * Is this the telnet test server not shutting down the socket - * in a clean way? Seems to be timing related, happens more - * on slow debug build */ - if(data->state.os_errno == ECONNRESET) { - DEBUGF(infof(data, "telnet_do, unexpected ECONNRESET on recv")); - } break; } /* returned zero but actually received 0 or less here, diff --git a/contrib/libs/curl/lib/telnet.h b/contrib/libs/curl/lib/telnet.h index 30782d8375..6dd99b48dc 100644 --- a/contrib/libs/curl/lib/telnet.h +++ b/contrib/libs/curl/lib/telnet.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/tftp.c b/contrib/libs/curl/lib/tftp.c index 8ed1b887b4..9e6d9490ed 100644 --- a/contrib/libs/curl/lib/tftp.c +++ b/contrib/libs/curl/lib/tftp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -48,7 +48,6 @@ #include "urldata.h" #include <curl/curl.h> -#include "cf-socket.h" #include "transfer.h" #include "sendf.h" #include "tftp.h" @@ -370,7 +369,7 @@ static CURLcode tftp_parse_option_ack(struct tftp_state_data *state, /* tsize should be ignored on upload: Who cares about the size of the remote file? */ - if(!data->state.upload) { + if(!data->set.upload) { if(!tsize) { failf(data, "invalid tsize -:%s:- value in OACK packet", value); return CURLE_TFTP_ILLEGAL; @@ -451,7 +450,7 @@ static CURLcode tftp_send_first(struct tftp_state_data *state, return result; } - if(data->state.upload) { + if(data->set.upload) { /* If we are uploading, send an WRQ */ setpacketevent(&state->spacket, TFTP_EVENT_WRQ); state->data->req.upload_fromhere = @@ -486,7 +485,7 @@ static CURLcode tftp_send_first(struct tftp_state_data *state, if(!data->set.tftp_no_options) { char buf[64]; /* add tsize option */ - if(data->state.upload && (data->state.infilesize != -1)) + if(data->set.upload && (data->state.infilesize != -1)) msnprintf(buf, sizeof(buf), "%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize); else @@ -530,8 +529,8 @@ static CURLcode tftp_send_first(struct tftp_state_data *state, not have a size_t argument, like older unixes that want an 'int' */ senddata = sendto(state->sockfd, (void *)state->spacket.data, (SEND_TYPE_ARG3)sbytes, 0, - &data->conn->remote_addr->sa_addr, - data->conn->remote_addr->addrlen); + data->conn->ip_addr->ai_addr, + data->conn->ip_addr->ai_addrlen); if(senddata != (ssize_t)sbytes) { char buffer[STRERROR_LEN]; failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); @@ -540,7 +539,7 @@ static CURLcode tftp_send_first(struct tftp_state_data *state, break; case TFTP_EVENT_OACK: - if(data->state.upload) { + if(data->set.upload) { result = tftp_connect_for_tx(state, event); } else { @@ -1015,7 +1014,7 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done) state->requested_blksize = blksize; ((struct sockaddr *)&state->local_addr)->sa_family = - (CURL_SA_FAMILY_T)(conn->remote_addr->family); + (CURL_SA_FAMILY_T)(conn->ip_addr->ai_family); tftp_set_timeouts(state); @@ -1034,7 +1033,7 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done) * IPv4 and IPv6... */ int rc = bind(state->sockfd, (struct sockaddr *)&state->local_addr, - conn->remote_addr->addrlen); + conn->ip_addr->ai_addrlen); if(rc) { char buffer[STRERROR_LEN]; failf(data, "bind() failed; %s", diff --git a/contrib/libs/curl/lib/tftp.h b/contrib/libs/curl/lib/tftp.h index 5d2d5da615..3f1fda6382 100644 --- a/contrib/libs/curl/lib/tftp.h +++ b/contrib/libs/curl/lib/tftp.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/timediff.c b/contrib/libs/curl/lib/timediff.c index 1b762bbd3e..c5893187dd 100644 --- a/contrib/libs/curl/lib/timediff.c +++ b/contrib/libs/curl/lib/timediff.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/timediff.h b/contrib/libs/curl/lib/timediff.h index fb318d4f2b..90e547457f 100644 --- a/contrib/libs/curl/lib/timediff.h +++ b/contrib/libs/curl/lib/timediff.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/timeval.c b/contrib/libs/curl/lib/timeval.c index 2de79be46d..647d7b0fc5 100644 --- a/contrib/libs/curl/lib/timeval.c +++ b/contrib/libs/curl/lib/timeval.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -58,8 +58,7 @@ struct curltime Curl_now(void) return now; } -#elif defined(HAVE_CLOCK_GETTIME_MONOTONIC) || \ - defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW) +#elif defined(HAVE_CLOCK_GETTIME_MONOTONIC) struct curltime Curl_now(void) { @@ -88,19 +87,6 @@ struct curltime Curl_now(void) have_clock_gettime = TRUE; #endif -#ifdef HAVE_CLOCK_GETTIME_MONOTONIC_RAW - if( -#if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \ - (HAVE_BUILTIN_AVAILABLE == 1) - have_clock_gettime && -#endif - (0 == clock_gettime(CLOCK_MONOTONIC_RAW, &tsnow))) { - cnow.tv_sec = tsnow.tv_sec; - cnow.tv_usec = (unsigned int)(tsnow.tv_nsec / 1000); - } - else -#endif - if( #if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \ (HAVE_BUILTIN_AVAILABLE == 1) diff --git a/contrib/libs/curl/lib/timeval.h b/contrib/libs/curl/lib/timeval.h index 92e484ad1d..8d4fef4e17 100644 --- a/contrib/libs/curl/lib/timeval.h +++ b/contrib/libs/curl/lib/timeval.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/transfer.c b/contrib/libs/curl/lib/transfer.c index b678004b95..441da73429 100644 --- a/contrib/libs/curl/lib/transfer.c +++ b/contrib/libs/curl/lib/transfer.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -64,7 +64,6 @@ #include "content_encoding.h" #include "hostip.h" -#include "cfilters.h" #include "transfer.h" #include "sendf.h" #include "speedcheck.h" @@ -73,7 +72,6 @@ #include "url.h" #include "getinfo.h" #include "vtls/vtls.h" -#include "vquic/vquic.h" #include "select.h" #include "multiif.h" #include "connect.h" @@ -364,17 +362,114 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes, return CURLE_OK; } -static int data_pending(struct Curl_easy *data) + +/* + * Curl_readrewind() rewinds the read stream. This is typically used for HTTP + * POST/PUT with multi-pass authentication when a sending was denied and a + * resend is necessary. + */ +CURLcode Curl_readrewind(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + curl_mimepart *mimepart = &data->set.mimepost; + + conn->bits.rewindaftersend = FALSE; /* we rewind now */ + + /* explicitly switch off sending data on this connection now since we are + about to restart a new transfer and thus we want to avoid inadvertently + sending more data on the existing connection until the next transfer + starts */ + data->req.keepon &= ~KEEP_SEND; + + /* We have sent away data. If not using CURLOPT_POSTFIELDS or + CURLOPT_HTTPPOST, call app to rewind + */ + if(conn->handler->protocol & PROTO_FAMILY_HTTP) { + struct HTTP *http = data->req.p.http; + + if(http->sendit) + mimepart = http->sendit; + } + if(data->set.postfields) + ; /* do nothing */ + else if(data->state.httpreq == HTTPREQ_POST_MIME || + data->state.httpreq == HTTPREQ_POST_FORM) { + CURLcode result = Curl_mime_rewind(mimepart); + if(result) { + failf(data, "Cannot rewind mime/post data"); + return result; + } + } + else { + if(data->set.seek_func) { + int err; + + Curl_set_in_callback(data, true); + err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET); + Curl_set_in_callback(data, false); + if(err) { + failf(data, "seek callback returned error %d", (int)err); + return CURLE_SEND_FAIL_REWIND; + } + } + else if(data->set.ioctl_func) { + curlioerr err; + + Curl_set_in_callback(data, true); + err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD, + data->set.ioctl_client); + Curl_set_in_callback(data, false); + infof(data, "the ioctl callback returned %d", (int)err); + + if(err) { + failf(data, "ioctl callback returned error %d", (int)err); + return CURLE_SEND_FAIL_REWIND; + } + } + else { + /* If no CURLOPT_READFUNCTION is used, we know that we operate on a + given FILE * stream and we can actually attempt to rewind that + ourselves with fseek() */ + if(data->state.fread_func == (curl_read_callback)fread) { + if(-1 != fseek(data->state.in, 0, SEEK_SET)) + /* successful rewind */ + return CURLE_OK; + } + + /* no callback set or failure above, makes us fail at once */ + failf(data, "necessary data rewind wasn't possible"); + return CURLE_SEND_FAIL_REWIND; + } + } + return CURLE_OK; +} + +static int data_pending(const struct Curl_easy *data) { struct connectdata *conn = data->conn; +#ifdef ENABLE_QUIC + if(conn->transport == TRNSPRT_QUIC) + return Curl_quic_data_pending(data); +#endif + if(conn->handler->protocol&PROTO_FAMILY_FTP) - return Curl_conn_data_pending(data, SECONDARYSOCKET); + return Curl_ssl_data_pending(conn, SECONDARYSOCKET); /* in the case of libssh2, we can never be really sure that we have emptied its internal buffers so we MUST always try until we get EAGAIN back */ return conn->handler->protocol&(CURLPROTO_SCP|CURLPROTO_SFTP) || - Curl_conn_data_pending(data, FIRSTSOCKET); +#ifdef USE_NGHTTP2 + /* 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 = + TRUE. The thing is if we read everything, then http2_recv won't + be called and we cannot signal the HTTP/2 stream has closed. As + a workaround, we return nonzero here to call http2_recv. */ + ((conn->handler->protocol&PROTO_FAMILY_HTTP) && conn->httpversion >= 20) || +#endif + Curl_ssl_data_pending(conn, FIRSTSOCKET); } /* @@ -428,8 +523,6 @@ static CURLcode readwrite_data(struct Curl_easy *data, size_t excess = 0; /* excess bytes read */ bool readmore = FALSE; /* used by RTP to signal for more data */ int maxloops = 100; - curl_off_t max_recv = data->set.max_recv_speed? - data->set.max_recv_speed : CURL_OFF_T_MAX; char *buf = data->state.buffer; DEBUGASSERT(buf); @@ -442,16 +535,29 @@ static CURLcode readwrite_data(struct Curl_easy *data, bool is_empty_data = FALSE; size_t buffersize = data->set.buffer_size; size_t bytestoread = buffersize; - /* For HTTP/2 and HTTP/3, read data without caring about the content - length. This is safe because body in HTTP/2 is always segmented - thanks to its framing layer. Meanwhile, we have to call Curl_read - to ensure that http2_handle_stream_close is called when we read all - incoming bytes for a particular stream. */ - bool is_http3 = Curl_conn_is_http3(data, conn, FIRSTSOCKET); - bool data_eof_handled = is_http3 - || Curl_conn_is_http2(data, conn, FIRSTSOCKET); - - if(!data_eof_handled && k->size != -1 && !k->header) { +#ifdef USE_NGHTTP2 + bool is_http2 = ((conn->handler->protocol & PROTO_FAMILY_HTTP) && + (conn->httpversion == 20)); +#endif + bool is_http3 = +#ifdef ENABLE_QUIC + ((conn->handler->protocol & PROTO_FAMILY_HTTP) && + (conn->httpversion == 30)); +#else + FALSE; +#endif + + if( +#ifdef USE_NGHTTP2 + /* For HTTP/2, read data without caring about the content length. This + is safe because body in HTTP/2 is always segmented thanks to its + framing layer. Meanwhile, we have to call Curl_read to ensure that + http2_handle_stream_close is called when we read all incoming bytes + for a particular stream. */ + !is_http2 && +#endif + !is_http3 && /* Same reason mentioned above. */ + k->size != -1 && !k->header) { /* make sure we don't read too much */ curl_off_t totalleft = k->size - k->bytecount; if(totalleft < (curl_off_t)bytestoread) @@ -463,13 +569,11 @@ static CURLcode readwrite_data(struct Curl_easy *data, result = Curl_read(data, conn->sockfd, buf, bytestoread, &nread); /* read would've blocked */ - if(CURLE_AGAIN == result) { - result = CURLE_OK; + if(CURLE_AGAIN == result) break; /* get out of loop */ - } if(result>0) - goto out; + return result; } else { /* read nothing but since we wanted nothing we consider this an OK @@ -493,9 +597,14 @@ static CURLcode readwrite_data(struct Curl_easy *data, buf[nread] = 0; } else { - /* if we receive 0 or less here, either the data transfer is done or the + /* if we receive 0 or less here, either the http2 stream is closed or the server closed the connection and we bail out from this! */ - if(data_eof_handled) +#ifdef USE_NGHTTP2 + if(is_http2 && !nread) + DEBUGF(infof(data, "nread == 0, stream closed, bailing")); + else +#endif + if(is_http3 && !nread) DEBUGF(infof(data, "nread == 0, stream closed, bailing")); else DEBUGF(infof(data, "nread <= 0, server closed connection, bailing")); @@ -510,7 +619,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, if(conn->handler->readwrite) { result = conn->handler->readwrite(data, conn, &nread, &readmore); if(result) - goto out; + return result; if(readmore) break; } @@ -523,13 +632,13 @@ static CURLcode readwrite_data(struct Curl_easy *data, bool stop_reading = FALSE; result = Curl_http_readwrite_headers(data, conn, &nread, &stop_reading); if(result) - goto out; + return result; if(conn->handler->readwrite && (k->maxdownload <= 0 && nread > 0)) { result = conn->handler->readwrite(data, conn, &nread, &readmore); if(result) - goto out; + return result; if(readmore) break; } @@ -556,12 +665,11 @@ static CURLcode readwrite_data(struct Curl_easy *data, is non-headers. */ if(!k->header && (nread > 0 || is_empty_data)) { - if(data->req.no_body) { + if(data->set.opt_no_body) { /* data arrives although we want none, bail out */ streamclose(conn, "ignoring body"); *done = TRUE; - result = CURLE_WEIRD_SERVER_REPLY; - goto out; + return CURLE_WEIRD_SERVER_REPLY; } #ifndef CURL_DISABLE_HTTP @@ -572,7 +680,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, /* HTTP-only checks */ result = Curl_http_firstwrite(data, conn, done); if(result || *done) - goto out; + return result; } } /* this is the first time we write a body part */ #endif /* CURL_DISABLE_HTTP */ @@ -609,12 +717,10 @@ static CURLcode readwrite_data(struct Curl_easy *data, if(CHUNKE_OK < res) { if(CHUNKE_PASSTHRU_ERROR == res) { failf(data, "Failed reading the chunked-encoded stream"); - result = extra; - goto out; + return extra; } failf(data, "%s in chunked-encoding", Curl_chunked_strerror(res)); - result = CURLE_RECV_ERROR; - goto out; + return CURLE_RECV_ERROR; } if(CHUNKE_STOP == res) { /* we're done reading chunks! */ @@ -668,7 +774,6 @@ static CURLcode readwrite_data(struct Curl_easy *data, } k->bytecount += nread; - max_recv -= nread; Curl_pgrsSetDownloadCounter(data, k->bytecount); @@ -691,7 +796,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, (size_t)k->maxdownload); if(result) - goto out; + return result; } if(k->badheader < HEADER_ALLBAD) { /* This switch handles various content encodings. If there's an @@ -716,7 +821,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, k->badheader = HEADER_NORMAL; /* taken care of now */ if(result) - goto out; + return result; } } /* if(!header and data to read) */ @@ -734,7 +839,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, result = conn->handler->readwrite(data, conn, &nread, &readmore); if(result) - goto out; + return result; if(readmore) k->keepon |= KEEP_RECV; /* we're not done reading */ @@ -747,16 +852,16 @@ static CURLcode readwrite_data(struct Curl_easy *data, k->keepon &= ~KEEP_RECV; } - if((k->keepon & KEEP_RECV_PAUSE) || !(k->keepon & KEEP_RECV)) { - /* this is a paused or stopped transfer */ + if(k->keepon & KEEP_RECV_PAUSE) { + /* this is a paused transfer */ break; } - } while((max_recv > 0) && data_pending(data) && maxloops--); + } while(data_pending(data) && maxloops--); - if(maxloops <= 0 || max_recv <= 0) { + if(maxloops <= 0) { /* we mark it as read-again-please */ - data->state.dselect_bits = CURL_CSELECT_IN; + conn->cselect_bits = CURL_CSELECT_IN; *comeback = TRUE; } @@ -769,20 +874,24 @@ static CURLcode readwrite_data(struct Curl_easy *data, k->keepon &= ~KEEP_SEND; /* no writing anymore either */ } -out: - if(result) - DEBUGF(infof(data, "readwrite_data() -> %d", result)); - return result; + return CURLE_OK; } CURLcode Curl_done_sending(struct Curl_easy *data, struct SingleRequest *k) { + struct connectdata *conn = data->conn; k->keepon &= ~KEEP_SEND; /* we're done writing */ /* These functions should be moved into the handler struct! */ - Curl_conn_ev_data_done_send(data); + Curl_http2_done_sending(data, conn); + Curl_quic_done_sending(data); + if(conn->bits.rewindaftersend) { + CURLcode result = Curl_readrewind(data); + if(result) + return result; + } return CURLE_OK; } @@ -983,15 +1092,7 @@ static CURLcode readwrite_upload(struct Curl_easy *data, if(result) return result; -#if defined(WIN32) && defined(USE_WINSOCK) - { - struct curltime n = Curl_now(); - if(Curl_timediff(n, k->last_sndbuf_update) > 1000) { - win_update_buffer_size(conn->writesockfd); - k->last_sndbuf_update = n; - } - } -#endif + win_update_buffer_size(conn->writesockfd); if(k->pendingheader) { /* parts of what was sent was header */ @@ -1066,73 +1167,73 @@ CURLcode Curl_readwrite(struct connectdata *conn, { struct SingleRequest *k = &data->req; CURLcode result; - struct curltime now; int didwhat = 0; - int select_bits; + curl_socket_t fd_read; + curl_socket_t fd_write; + int select_res = conn->cselect_bits; - if(data->state.dselect_bits) { - select_bits = data->state.dselect_bits; - data->state.dselect_bits = 0; - } - else if(conn->cselect_bits) { - select_bits = conn->cselect_bits; - conn->cselect_bits = 0; - } - else { - curl_socket_t fd_read; - curl_socket_t fd_write; - /* only use the proper socket if the *_HOLD bit is not set simultaneously - as then we are in rate limiting state in that transfer direction */ - if((k->keepon & KEEP_RECVBITS) == KEEP_RECV) - fd_read = conn->sockfd; - else - fd_read = CURL_SOCKET_BAD; + conn->cselect_bits = 0; - if((k->keepon & KEEP_SENDBITS) == KEEP_SEND) - fd_write = conn->writesockfd; - else - fd_write = CURL_SOCKET_BAD; + /* only use the proper socket if the *_HOLD bit is not set simultaneously as + then we are in rate limiting state in that transfer direction */ + + if((k->keepon & KEEP_RECVBITS) == KEEP_RECV) + fd_read = conn->sockfd; + else + fd_read = CURL_SOCKET_BAD; + + if((k->keepon & KEEP_SENDBITS) == KEEP_SEND) + fd_write = conn->writesockfd; + else + fd_write = CURL_SOCKET_BAD; - select_bits = Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write, 0); +#if defined(USE_HTTP2) || defined(USE_HTTP3) + if(data->state.drain) { + select_res |= CURL_CSELECT_IN; + DEBUGF(infof(data, "Curl_readwrite: forcibly told to drain data")); } +#endif + + if(!select_res) /* Call for select()/poll() only, if read/write/error + status is not known. */ + select_res = Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write, 0); - if(select_bits == CURL_CSELECT_ERR) { + if(select_res == CURL_CSELECT_ERR) { failf(data, "select/poll returned error"); - result = CURLE_SEND_ERROR; - goto out; + return CURLE_SEND_ERROR; } #ifdef USE_HYPER if(conn->datastream) { - result = conn->datastream(data, conn, &didwhat, done, select_bits); + result = conn->datastream(data, conn, &didwhat, done, select_res); if(result || *done) - goto out; + return result; } else { #endif /* We go ahead and do a read if we have a readable socket or if the stream was rewound (in which case we have data in a buffer) */ - if((k->keepon & KEEP_RECV) && (select_bits & CURL_CSELECT_IN)) { + if((k->keepon & KEEP_RECV) && (select_res & CURL_CSELECT_IN)) { result = readwrite_data(data, conn, k, &didwhat, done, comeback); if(result || *done) - goto out; + return result; } /* If we still have writing to do, we check if we have a writable socket. */ - if((k->keepon & KEEP_SEND) && (select_bits & CURL_CSELECT_OUT)) { + if((k->keepon & KEEP_SEND) && (select_res & CURL_CSELECT_OUT)) { /* write */ result = readwrite_upload(data, conn, &didwhat); if(result) - goto out; + return result; } #ifdef USE_HYPER } #endif - now = Curl_now(); + k->now = Curl_now(); if(!didwhat) { /* no read no write, this is a timeout? */ if(k->exp100 == EXP100_AWAITING_CONTINUE) { @@ -1149,7 +1250,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, */ - timediff_t ms = Curl_timediff(now, k->start100); + timediff_t ms = Curl_timediff(k->now, k->start100); if(ms >= data->set.expect_100_timeout) { /* we've waited long enough, continue anyway */ k->exp100 = EXP100_SEND_DATA; @@ -1159,35 +1260,38 @@ CURLcode Curl_readwrite(struct connectdata *conn, } } - result = Curl_conn_ev_data_idle(data); - if(result) - goto out; +#ifdef ENABLE_QUIC + if(conn->transport == TRNSPRT_QUIC) { + result = Curl_quic_idle(data); + if(result) + return result; + } +#endif } if(Curl_pgrsUpdate(data)) result = CURLE_ABORTED_BY_CALLBACK; else - result = Curl_speedcheck(data, now); + result = Curl_speedcheck(data, k->now); if(result) - goto out; + return result; if(k->keepon) { - if(0 > Curl_timeleft(data, &now, FALSE)) { + if(0 > Curl_timeleft(data, &k->now, FALSE)) { if(k->size != -1) { failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %" CURL_FORMAT_CURL_OFF_T " bytes received", - Curl_timediff(now, data->progress.t_startsingle), + Curl_timediff(k->now, data->progress.t_startsingle), k->bytecount, k->size); } else { failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T " milliseconds with %" CURL_FORMAT_CURL_OFF_T " bytes received", - Curl_timediff(now, data->progress.t_startsingle), + Curl_timediff(k->now, data->progress.t_startsingle), k->bytecount); } - result = CURLE_OPERATION_TIMEDOUT; - goto out; + return CURLE_OPERATION_TIMEDOUT; } } else { @@ -1196,7 +1300,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, * returning. */ - if(!(data->req.no_body) && (k->size != -1) && + if(!(data->set.opt_no_body) && (k->size != -1) && (k->bytecount != k->size) && #ifdef CURL_DO_LINEEND_CONV /* Most FTP servers don't adjust their file SIZE response for CRLFs, @@ -1208,10 +1312,9 @@ CURLcode Curl_readwrite(struct connectdata *conn, !k->newurl) { failf(data, "transfer closed with %" CURL_FORMAT_CURL_OFF_T " bytes remaining to read", k->size - k->bytecount); - result = CURLE_PARTIAL_FILE; - goto out; + return CURLE_PARTIAL_FILE; } - if(!(data->req.no_body) && k->chunk && + if(!(data->set.opt_no_body) && k->chunk && (conn->chunk.state != CHUNK_STOP)) { /* * In chunked mode, return an error if the connection is closed prior to @@ -1223,21 +1326,17 @@ CURLcode Curl_readwrite(struct connectdata *conn, * */ failf(data, "transfer closed with outstanding read data remaining"); - result = CURLE_PARTIAL_FILE; - goto out; - } - if(Curl_pgrsUpdate(data)) { - result = CURLE_ABORTED_BY_CALLBACK; - goto out; + return CURLE_PARTIAL_FILE; } + if(Curl_pgrsUpdate(data)) + return CURLE_ABORTED_BY_CALLBACK; } /* Now update the "done" boolean we return */ - *done = (0 == (k->keepon&(KEEP_RECVBITS|KEEP_SENDBITS))) ? TRUE : FALSE; -out: - if(result) - DEBUGF(infof(data, "Curl_readwrite() -> %d", result)); - return result; + *done = (0 == (k->keepon&(KEEP_RECV|KEEP_SEND| + KEEP_RECV_PAUSE|KEEP_SEND_PAUSE))) ? TRUE : FALSE; + + return CURLE_OK; } /* @@ -1268,6 +1367,7 @@ int Curl_single_getsock(struct Curl_easy *data, /* don't include HOLD and PAUSE connections */ if((data->req.keepon & KEEP_SENDBITS) == KEEP_SEND) { + if((conn->sockfd != conn->writesockfd) || bitmap == GETSOCK_BLANK) { /* only if they are not the same socket and we have a readable @@ -1292,7 +1392,6 @@ void Curl_init_CONNECT(struct Curl_easy *data) { data->state.fread_func = data->set.fread_func_set; data->state.in = data->set.in_set; - data->state.upload = (data->state.httpreq == HTTPREQ_PUT); } /* @@ -1328,12 +1427,6 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) } } - if(data->set.postfields && data->set.set_resume_from) { - /* we can't */ - failf(data, "cannot mix POSTFIELDS with RESUME_FROM"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - data->state.prefer_ascii = data->set.prefer_ascii; data->state.list_only = data->set.list_only; data->state.httpreq = data->set.method; @@ -1356,7 +1449,6 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) data->state.authhost.want = data->set.httpauth; data->state.authproxy.want = data->set.proxyauth; Curl_safefree(data->info.wouldredirect); - Curl_data_priority_clear_state(data); if(data->state.httpreq == HTTPREQ_PUT) data->state.infilesize = data->set.filesize; @@ -1369,16 +1461,15 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) else data->state.infilesize = 0; +#ifndef CURL_DISABLE_COOKIES /* If there is a list of cookie files to read, do it now! */ - Curl_cookie_loadfiles(data); - + if(data->state.cookielist) + Curl_cookie_loadfiles(data); +#endif /* If there is a list of host pairs to deal with */ if(data->state.resolve) result = Curl_loadhostpairs(data); - /* If there is a list of hsts files to read */ - Curl_hsts_loadfiles(data); - if(!result) { /* Allow data->set.use_port to set which port to use. This needs to be * disabled for example when we follow Location: headers to URLs using @@ -1406,31 +1497,21 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) #ifndef CURL_DISABLE_FTP data->state.wildcardmatch = data->set.wildcard_enabled; if(data->state.wildcardmatch) { - struct WildcardData *wc; - if(!data->wildcard) { - data->wildcard = calloc(1, sizeof(struct WildcardData)); - if(!data->wildcard) - return CURLE_OUT_OF_MEMORY; - } - wc = data->wildcard; - if((wc->state < CURLWC_INIT) || - (wc->state >= CURLWC_CLEAN)) { - if(wc->ftpwc) - wc->dtor(wc->ftpwc); - Curl_safefree(wc->pattern); - Curl_safefree(wc->path); + struct WildcardData *wc = &data->wildcard; + if(wc->state < CURLWC_INIT) { result = Curl_wildcard_init(wc); /* init wildcard structures */ if(result) return CURLE_OUT_OF_MEMORY; } } #endif + Curl_http2_init_state(&data->state); result = Curl_hsts_loadcb(data, data->hsts); } /* * Set user-agent. Used for HTTP, but since we can attempt to tunnel - * basically anything through an HTTP proxy we can't limit this based on + * basically anything through a http proxy we can't limit this based on * protocol. */ if(data->set.str[STRING_USERAGENT]) { @@ -1510,8 +1591,10 @@ CURLcode Curl_follow(struct Curl_easy *data, to URL */ } else { - data->state.followlocation++; /* count redirect-followings, including - auth reloads */ + /* mark the next request as a followed location: */ + data->state.this_is_a_follow = TRUE; + + data->state.followlocation++; /* count location-followers */ if(data->set.http_auto_referer) { CURLU *u; @@ -1554,11 +1637,10 @@ CURLcode Curl_follow(struct Curl_easy *data, if((type != FOLLOW_RETRY) && (data->req.httpcode != 401) && (data->req.httpcode != 407) && - Curl_is_absolute_url(newurl, NULL, 0, FALSE)) { + Curl_is_absolute_url(newurl, NULL, 0, FALSE)) /* If this is not redirect due to a 401 or 407 response and an absolute URL: don't allow a custom port number */ disallowport = TRUE; - } DEBUGASSERT(data->state.uh); uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl, @@ -1661,7 +1743,7 @@ CURLcode Curl_follow(struct Curl_easy *data, * differently based on exactly what return code there was. * * News from 7.10.6: we can also get here on a 401 or 407, in case we act on - * an HTTP (proxy-) authentication scheme other than Basic. + * a HTTP (proxy-) authentication scheme other than Basic. */ switch(data->info.httpcode) { /* 401 - Act on a WWW-Authenticate, we keep on moving and do the @@ -1739,8 +1821,9 @@ CURLcode Curl_follow(struct Curl_easy *data, data->state.httpreq != HTTPREQ_POST_MIME) || !(data->set.keep_post & CURL_REDIR_POST_303))) { data->state.httpreq = HTTPREQ_GET; + data->set.upload = false; infof(data, "Switch to %s", - data->req.no_body?"HEAD":"GET"); + data->set.opt_no_body?"HEAD":"GET"); } break; case 304: /* Not Modified */ @@ -1776,13 +1859,13 @@ CURLcode Curl_retry_request(struct Curl_easy *data, char **url) /* if we're talking upload, we can't do the checks below, unless the protocol is HTTP as when uploading over HTTP we will still get a response */ - if(data->state.upload && + if(data->set.upload && !(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP))) return CURLE_OK; if((data->req.bytecount + data->req.headerbytecount == 0) && conn->bits.reuse && - (!data->req.no_body || (conn->handler->protocol & PROTO_FAMILY_HTTP)) + (!data->set.opt_no_body || (conn->handler->protocol & PROTO_FAMILY_HTTP)) #ifndef CURL_DISABLE_RTSP && (data->set.rtspreq != RTSPREQ_RECEIVE) #endif @@ -1828,10 +1911,14 @@ CURLcode Curl_retry_request(struct Curl_easy *data, char **url) transferred! */ - if((conn->handler->protocol&PROTO_FAMILY_HTTP) && - data->req.writebytecount) { - data->state.rewindbeforesend = TRUE; - infof(data, "state.rewindbeforesend = TRUE"); + if(conn->handler->protocol&PROTO_FAMILY_HTTP) { + if(data->req.writebytecount) { + CURLcode result = Curl_readrewind(data); + if(result) { + Curl_safefree(*url); + return result; + } + } } } return CURLE_OK; @@ -1862,7 +1949,7 @@ Curl_setup_transfer( httpsending = ((conn->handler->protocol&PROTO_FAMILY_HTTP) && (http->sending == HTTPSEND_REQUEST)); - if(conn->bits.multiplex || conn->httpversion >= 20 || httpsending) { + if(conn->bits.multiplex || conn->httpversion == 20 || httpsending) { /* when multiplexing, the read/write sockets need to be the same! */ conn->sockfd = sockindex == -1 ? ((writesockindex == -1 ? CURL_SOCKET_BAD : conn->sock[writesockindex])) : @@ -1892,7 +1979,7 @@ Curl_setup_transfer( Curl_pgrsSetDownloadSize(data, size); } /* we want header and/or body, if neither then don't do this! */ - if(k->getheader || !data->req.no_body) { + if(k->getheader || !data->set.opt_no_body) { if(sockindex != -1) k->keepon |= KEEP_RECV; @@ -1928,6 +2015,6 @@ Curl_setup_transfer( k->keepon |= KEEP_SEND; } } /* if(writesockindex != -1) */ - } /* if(k->getheader || !data->req.no_body) */ + } /* if(k->getheader || !data->set.opt_no_body) */ } diff --git a/contrib/libs/curl/lib/transfer.h b/contrib/libs/curl/lib/transfer.h index 536ac249b7..65fe68e812 100644 --- a/contrib/libs/curl/lib/transfer.h +++ b/contrib/libs/curl/lib/transfer.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -50,6 +50,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, bool *comeback); int Curl_single_getsock(struct Curl_easy *data, struct connectdata *conn, curl_socket_t *socks); +CURLcode Curl_readrewind(struct Curl_easy *data); CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes, size_t *nreadp); CURLcode Curl_retry_request(struct Curl_easy *data, char **url); diff --git a/contrib/libs/curl/lib/url.c b/contrib/libs/curl/lib/url.c index e3e7f4507b..7b6d694449 100644 --- a/contrib/libs/curl/lib/url.c +++ b/contrib/libs/curl/lib/url.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -61,9 +61,26 @@ #include <limits.h> +#ifdef USE_LIBIDN2 +#error #include <idn2.h> + +#if defined(WIN32) && defined(UNICODE) +#define IDN2_LOOKUP(name, host, flags) \ + idn2_lookup_u8((const uint8_t *)name, (uint8_t **)host, flags) +#else +#define IDN2_LOOKUP(name, host, flags) \ + idn2_lookup_ul((const char *)name, (char **)host, flags) +#endif + +#elif defined(USE_WIN32_IDN) +/* prototype for Curl_win32_idn_to_ascii() */ +bool Curl_win32_idn_to_ascii(const char *in, char **out); +#endif /* USE_LIBIDN2 */ + #include "doh.h" #include "urldata.h" #include "netrc.h" + #include "formdata.h" #include "mime.h" #include "vtls/vtls.h" @@ -90,8 +107,6 @@ #include "system_win32.h" #include "hsts.h" #include "noproxy.h" -#include "cfilters.h" -#include "idn.h" /* And now for the protocols */ #include "ftp.h" @@ -125,15 +140,7 @@ #include "curl_memory.h" #include "memdebug.h" -#ifndef ARRAYSIZE -#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) -#endif - -#ifdef USE_NGHTTP2 -static void data_priority_cleanup(struct Curl_easy *data); -#else -#define data_priority_cleanup(x) -#endif +static void conn_free(struct connectdata *conn); /* Some parts of the code (e.g. chunked encoding) assume this buffer has at * more than just a few bytes to play with. Don't let it become too small or @@ -292,6 +299,33 @@ static const struct Curl_handler * const protocols[] = { (struct Curl_handler *) NULL }; +/* + * Dummy handler for undefined protocol schemes. + */ + +static const struct Curl_handler Curl_handler_dummy = { + "<no protocol>", /* scheme */ + ZERO_NULL, /* setup_connection */ + ZERO_NULL, /* do_it */ + ZERO_NULL, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_getsock */ + ZERO_NULL, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* readwrite */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + 0, /* defport */ + 0, /* protocol */ + 0, /* family */ + PROTOPT_NONE /* flags */ +}; + void Curl_freeset(struct Curl_easy *data) { /* Free all dynamic strings stored in the data->set substructure. */ @@ -318,11 +352,6 @@ void Curl_freeset(struct Curl_easy *data) data->state.url = NULL; Curl_mime_cleanpart(&data->set.mimepost); - -#ifndef CURL_DISABLE_COOKIES - curl_slist_free_all(data->set.cookielist); - data->set.cookielist = NULL; -#endif } /* free the URL pieces */ @@ -350,6 +379,7 @@ static void up_free(struct Curl_easy *data) CURLcode Curl_close(struct Curl_easy **datap) { + struct Curl_multi *m; struct Curl_easy *data; if(!datap || !*datap) @@ -363,7 +393,8 @@ CURLcode Curl_close(struct Curl_easy **datap) /* Detach connection if any is left. This should not be normal, but can be the case for example with CONNECT_ONLY + recv/send (test 556) */ Curl_detach_connection(data); - if(data->multi) + 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); @@ -375,6 +406,11 @@ CURLcode Curl_close(struct Curl_easy **datap) 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 + case" */ + Curl_llist_destroy(&data->state.timeoutlist, NULL); + data->magic = 0; /* force a clear AFTER the possibly enforced removal from the multi handle, since that function uses the magic field! */ @@ -409,11 +445,7 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]); Curl_altsvc_cleanup(&data->asi); Curl_hsts_save(data, data->hsts, data->set.str[STRING_HSTS]); -#ifndef CURL_DISABLE_HSTS - if(!data->share || !data->share->hsts) - Curl_hsts_cleanup(&data->hsts); - curl_slist_free_all(data->set.hstslist); /* clean up list */ -#endif + Curl_hsts_cleanup(&data->hsts); #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) Curl_http_auth_cleanup_digest(data); #endif @@ -424,7 +456,7 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_resolver_cancel(data); Curl_resolver_cleanup(data->state.async.resolver); - data_priority_cleanup(data); + Curl_http2_cleanup_dependencies(data); /* No longer a dirty share, if it exists */ if(data->share) { @@ -507,14 +539,12 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) /* Set the default size of the SSL session ID cache */ set->general_ssl.max_ssl_sessions = 5; - /* Timeout every 24 hours by default */ - set->general_ssl.ca_cache_timeout = 24 * 60 * 60; + set->proxyport = 0; + set->proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */ set->httpauth = CURLAUTH_BASIC; /* defaults to basic */ #ifndef CURL_DISABLE_PROXY - set->proxyport = 0; - set->proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */ set->proxyauth = CURLAUTH_BASIC; /* defaults to basic */ /* SOCKS5 proxy auth defaults to username/password + GSS-API */ set->socks5auth = CURLAUTH_BASIC | CURLAUTH_GSSAPI; @@ -523,7 +553,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) /* make libcurl quiet by default: */ set->hide_progress = TRUE; /* CURLOPT_NOPROGRESS changes these */ - Curl_mime_initpart(&set->mimepost); + Curl_mime_initpart(&set->mimepost, data); /* * libcurl 7.10 introduced SSL verification *by default*! This needs to be @@ -535,11 +565,11 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) #endif set->ssl.primary.verifypeer = TRUE; set->ssl.primary.verifyhost = TRUE; -#ifdef USE_SSH - /* defaults to any auth type */ - set->ssh_auth_types = CURLSSH_AUTH_DEFAULT; - set->new_directory_perms = 0755; /* Default permissions */ +#ifdef USE_TLS_SRP + set->ssl.primary.authtype = CURL_TLSAUTH_NONE; #endif + /* defaults to any auth type */ + set->ssh_auth_types = CURLSSH_AUTH_DEFAULT; set->ssl.primary.sessionid = TRUE; /* session ID caching enabled by default */ #ifndef CURL_DISABLE_PROXY @@ -547,6 +577,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) #endif set->new_file_perms = 0644; /* Default permissions */ + set->new_directory_perms = 0755; /* Default permissions */ set->allowed_protocols = (curl_prot_t) CURLPROTO_ALL; set->redir_protocols = CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP | CURLPROTO_FTPS; @@ -609,16 +640,14 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->maxage_conn = 118; set->maxlifetime_conn = 0; set->http09_allowed = FALSE; + set->httpwant = #ifdef USE_HTTP2 - set->httpwant = CURL_HTTP_VERSION_2TLS + CURL_HTTP_VERSION_2TLS #else - set->httpwant = CURL_HTTP_VERSION_1_1 + CURL_HTTP_VERSION_1_1 #endif ; -#if defined(USE_HTTP2) || defined(USE_HTTP3) - memset(&set->priority, 0, sizeof(set->priority)); -#endif - set->quick_exit = 0L; + Curl_http2_init_userset(set); return result; } @@ -659,9 +688,6 @@ CURLcode Curl_open(struct Curl_easy **curl) /* most recent connection is not yet defined */ data->state.lastconnect_id = -1; - data->state.recent_conn_id = -1; - /* and not assigned an id yet */ - data->id = -1; data->progress.flags |= PGRS_HIDE; data->state.current_speed = -1; /* init to negative == impossible */ @@ -680,28 +706,76 @@ CURLcode Curl_open(struct Curl_easy **curl) return result; } -static void conn_shutdown(struct Curl_easy *data) +#ifdef USE_RECV_BEFORE_SEND_WORKAROUND +static void conn_reset_postponed_data(struct connectdata *conn, int num) { + struct postponed_data * const psnd = &(conn->postponed[num]); + if(psnd->buffer) { + DEBUGASSERT(psnd->allocated_size > 0); + DEBUGASSERT(psnd->recv_size <= psnd->allocated_size); + DEBUGASSERT(psnd->recv_size ? + (psnd->recv_processed < psnd->recv_size) : + (psnd->recv_processed == 0)); + DEBUGASSERT(psnd->bindsock != CURL_SOCKET_BAD); + free(psnd->buffer); + psnd->buffer = NULL; + psnd->allocated_size = 0; + psnd->recv_size = 0; + psnd->recv_processed = 0; +#ifdef DEBUGBUILD + psnd->bindsock = CURL_SOCKET_BAD; /* used only for DEBUGASSERT */ +#endif /* DEBUGBUILD */ + } + else { + DEBUGASSERT(psnd->allocated_size == 0); + DEBUGASSERT(psnd->recv_size == 0); + DEBUGASSERT(psnd->recv_processed == 0); + DEBUGASSERT(psnd->bindsock == CURL_SOCKET_BAD); + } +} + +static void conn_reset_all_postponed_data(struct connectdata *conn) +{ + conn_reset_postponed_data(conn, 0); + conn_reset_postponed_data(conn, 1); +} +#else /* ! USE_RECV_BEFORE_SEND_WORKAROUND */ +/* Use "do-nothing" macro instead of function when workaround not used */ +#define conn_reset_all_postponed_data(c) do {} while(0) +#endif /* ! USE_RECV_BEFORE_SEND_WORKAROUND */ + + +static void conn_shutdown(struct Curl_easy *data, struct connectdata *conn) +{ + DEBUGASSERT(conn); DEBUGASSERT(data); - infof(data, "Closing connection"); + infof(data, "Closing connection %ld", conn->connection_id); /* possible left-overs from the async name resolvers */ Curl_resolver_cancel(data); - Curl_conn_close(data, SECONDARYSOCKET); - Curl_conn_close(data, FIRSTSOCKET); + /* close the SSL stuff before we close any sockets since they will/may + write to the sockets */ + Curl_ssl_close(data, conn, FIRSTSOCKET); +#ifndef CURL_DISABLE_FTP + Curl_ssl_close(data, conn, SECONDARYSOCKET); +#endif + + /* close possibly still open sockets */ + if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) + Curl_closesocket(data, conn, conn->sock[SECONDARYSOCKET]); + if(CURL_SOCKET_BAD != conn->sock[FIRSTSOCKET]) + Curl_closesocket(data, conn, conn->sock[FIRSTSOCKET]); + if(CURL_SOCKET_BAD != conn->tempsock[0]) + Curl_closesocket(data, conn, conn->tempsock[0]); + if(CURL_SOCKET_BAD != conn->tempsock[1]) + Curl_closesocket(data, conn, conn->tempsock[1]); } -static void conn_free(struct Curl_easy *data, struct connectdata *conn) +static void conn_free(struct connectdata *conn) { - size_t i; - DEBUGASSERT(conn); - for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) { - Curl_conn_cf_discard_all(data, conn, (int)i); - } - Curl_free_idnconverted_hostname(&conn->host); Curl_free_idnconverted_hostname(&conn->conn_to_host); #ifndef CURL_DISABLE_PROXY @@ -720,13 +794,15 @@ static void conn_free(struct Curl_easy *data, struct connectdata *conn) Curl_safefree(conn->sasl_authzid); Curl_safefree(conn->options); Curl_safefree(conn->oauth_bearer); -#ifndef CURL_DISABLE_HTTP Curl_dyn_free(&conn->trailer); -#endif Curl_safefree(conn->host.rawalloc); /* host name buffer */ Curl_safefree(conn->conn_to_host.rawalloc); /* host name buffer */ Curl_safefree(conn->hostname_resolve); Curl_safefree(conn->secondaryhostname); + Curl_safefree(conn->connect_state); + + conn_reset_all_postponed_data(conn); + Curl_llist_destroy(&conn->easyq, NULL); Curl_safefree(conn->localdev); Curl_free_primary_ssl_config(&conn->ssl_config); @@ -734,6 +810,9 @@ static void conn_free(struct Curl_easy *data, struct connectdata *conn) Curl_safefree(conn->unix_domain_socket); #endif +#ifdef USE_SSL + Curl_safefree(conn->ssl_extra); +#endif free(conn); /* free all the connection oriented data */ } @@ -766,9 +845,6 @@ void Curl_disconnect(struct Curl_easy *data, /* the transfer must be detached from the connection */ DEBUGASSERT(!data->conn); - DEBUGF(infof(data, "Curl_disconnect(conn #%" - CURL_FORMAT_CURL_OFF_T ", dead=%d)", - conn->connection_id, dead_connection)); /* * If this connection isn't marked to force-close, leave it open if there * are other users of it @@ -797,16 +873,34 @@ void Curl_disconnect(struct Curl_easy *data, disconnect and shutdown */ Curl_attach_connection(data, conn); - if(conn->handler && conn->handler->disconnect) + if(conn->handler->disconnect) /* This is set if protocol-specific cleanups should be made */ conn->handler->disconnect(data, conn, dead_connection); - conn_shutdown(data); + conn_shutdown(data, conn); /* detach it again */ Curl_detach_connection(data); - conn_free(data, conn); + conn_free(conn); +} + +/* + * This function should return TRUE if the socket is to be assumed to + * be dead. Most commonly this happens when the server has closed the + * connection due to inactivity. + */ +static bool SocketIsDead(curl_socket_t sock) +{ + int sval; + bool ret_val = TRUE; + + sval = SOCKET_READABLE(sock, 0); + if(sval == 0) + /* timeout */ + ret_val = FALSE; + + return ret_val; } /* @@ -820,7 +914,7 @@ static int IsMultiplexingPossible(const struct Curl_easy *handle, { int avail = 0; - /* If an HTTP protocol and multiplexing is enabled */ + /* If a HTTP protocol and multiplexing is enabled */ if((conn->handler->protocol & PROTO_FAMILY_HTTP) && (!conn->bits.protoconnstart || !conn->bits.close)) { @@ -839,7 +933,7 @@ proxy_info_matches(const struct proxy_info *data, { if((data->proxytype == needle->proxytype) && (data->port == needle->port) && - strcasecompare(data->host.name, needle->host.name)) + Curl_safe_strcasecompare(data->host.name, needle->host.name)) return TRUE; return FALSE; @@ -939,27 +1033,12 @@ static bool extract_if_dead(struct connectdata *conn, } else { - bool input_pending; - - Curl_attach_connection(data, conn); - dead = !Curl_conn_is_alive(data, conn, &input_pending); - if(input_pending) { - /* For reuse, we want a "clean" connection state. The includes - * that we expect - in general - no waiting input data. Input - * waiting might be a TLS Notify Close, for example. We reject - * that. - * For protocols where data from other other end may arrive at - * any time (HTTP/2 PING for example), the protocol handler needs - * to install its own `connection_check` callback. - */ - dead = TRUE; - } - Curl_detach_connection(data); + /* Use the general method for determining the death of a connection */ + dead = SocketIsDead(conn->sock[FIRSTSOCKET]); } if(dead) { - infof(data, "Connection %" CURL_FORMAT_CURL_OFF_T " seems to be dead", - conn->connection_id); + infof(data, "Connection %ld seems to be dead", conn->connection_id); Curl_conncache_remove_conn(data, conn, FALSE); return TRUE; } @@ -1118,7 +1197,7 @@ ConnectionExists(struct Curl_easy *data, size_t multiplexed = 0; /* - * Note that if we use an HTTP proxy in normal mode (no tunneling), we + * Note that if we use a HTTP proxy in normal mode (no tunneling), we * check connections to that proxy and not to the actual remote server. */ check = curr->ptr; @@ -1154,20 +1233,20 @@ ConnectionExists(struct Curl_easy *data, /* primary_ip[0] is NUL only if the resolving of the name hasn't completed yet and until then we don't re-use this connection */ if(!check->primary_ip[0]) { - infof(data, "Connection #%" CURL_FORMAT_CURL_OFF_T " is still " - "name resolving, can't reuse", + infof(data, + "Connection #%ld is still name resolving, can't reuse", check->connection_id); continue; } } - } - if(!Curl_conn_is_connected(check, FIRSTSOCKET)) { - foundPendingCandidate = TRUE; - /* Don't pick a connection that hasn't connected yet */ - infof(data, "Connection #%" CURL_FORMAT_CURL_OFF_T - "isn't open enough, can't reuse", check->connection_id); - continue; + if(check->sock[FIRSTSOCKET] == CURL_SOCKET_BAD) { + foundPendingCandidate = TRUE; + /* Don't pick a connection that hasn't connected yet */ + infof(data, "Connection #%ld isn't open enough, can't reuse", + check->connection_id); + continue; + } } #ifdef USE_UNIX_SOCKETS @@ -1220,19 +1299,21 @@ ConnectionExists(struct Curl_easy *data, if(needle->bits.tunnel_proxy != check->bits.tunnel_proxy) continue; - if(IS_HTTPS_PROXY(needle->http_proxy.proxytype)) { + if(needle->http_proxy.proxytype == CURLPROXY_HTTPS) { /* use https proxy */ - if(needle->http_proxy.proxytype != - check->http_proxy.proxytype) - continue; - else if(needle->handler->flags&PROTOPT_SSL) { + if(needle->handler->flags&PROTOPT_SSL) { /* use double layer ssl */ if(!Curl_ssl_config_matches(&needle->proxy_ssl_config, &check->proxy_ssl_config)) continue; + if(check->proxy_ssl[FIRSTSOCKET].state != ssl_connection_complete) + continue; } - else if(!Curl_ssl_config_matches(&needle->ssl_config, - &check->ssl_config)) + + if(!Curl_ssl_config_matches(&needle->ssl_config, + &check->ssl_config)) + continue; + if(check->ssl[FIRSTSOCKET].state != ssl_connection_complete) continue; } } @@ -1283,46 +1364,26 @@ ConnectionExists(struct Curl_easy *data, } } - /* GSS delegation differences do not actually affect every connection - and auth method, but this check takes precaution before efficiency */ - if(needle->gssapi_delegation != check->gssapi_delegation) - continue; - /* If multiplexing isn't enabled on the h2 connection and h1 is explicitly requested, handle it: */ if((needle->handler->protocol & PROTO_FAMILY_HTTP) && - (((check->httpversion >= 20) && - (data->state.httpwant < CURL_HTTP_VERSION_2_0)) - || ((check->httpversion >= 30) && - (data->state.httpwant < CURL_HTTP_VERSION_3)))) + (check->httpversion >= 20) && + (data->state.httpwant < CURL_HTTP_VERSION_2_0)) continue; -#ifdef USE_SSH - else if(get_protocol_family(needle->handler) & PROTO_FAMILY_SSH) { + + if(get_protocol_family(needle->handler) == PROTO_FAMILY_SSH) { if(!ssh_config_matches(needle, check)) continue; } -#endif -#ifndef CURL_DISABLE_FTP - else if(get_protocol_family(needle->handler) & PROTO_FAMILY_FTP) { - /* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */ - if(Curl_timestrcmp(needle->proto.ftpc.account, - check->proto.ftpc.account) || - Curl_timestrcmp(needle->proto.ftpc.alternative_to_user, - check->proto.ftpc.alternative_to_user) || - (needle->proto.ftpc.use_ssl != check->proto.ftpc.use_ssl) || - (needle->proto.ftpc.ccc != check->proto.ftpc.ccc)) - continue; - } -#endif if((needle->handler->flags&PROTOPT_SSL) #ifndef CURL_DISABLE_PROXY || !needle->bits.httpproxy || needle->bits.tunnel_proxy #endif ) { - /* The requested connection does not use an HTTP proxy or it uses SSL - or it is a non-SSL protocol tunneled or it is a non-SSL protocol - which is allowed to be upgraded via TLS */ + /* The requested connection does not use a HTTP proxy or it uses SSL or + it is a non-SSL protocol tunneled or it is a non-SSL protocol which + is allowed to be upgraded via TLS */ if((strcasecompare(needle->handler->scheme, check->handler->scheme) || (get_protocol_family(check->handler) == @@ -1342,8 +1403,16 @@ ConnectionExists(struct Curl_easy *data, if(!Curl_ssl_config_matches(&needle->ssl_config, &check->ssl_config)) { DEBUGF(infof(data, - "Connection #%" CURL_FORMAT_CURL_OFF_T - " has different SSL parameters, can't reuse", + "Connection #%ld has different SSL parameters, " + "can't reuse", + check->connection_id)); + continue; + } + if(check->ssl[FIRSTSOCKET].state != ssl_connection_complete) { + foundPendingCandidate = TRUE; + DEBUGF(infof(data, + "Connection #%ld has not started SSL connect, " + "can't reuse", check->connection_id)); continue; } @@ -1429,8 +1498,9 @@ ConnectionExists(struct Curl_easy *data, #ifdef USE_NGHTTP2 /* If multiplexed, make sure we don't go over concurrency limit */ if(check->bits.multiplex) { - if(multiplexed >= Curl_conn_get_max_concurrent(data, check, - FIRSTSOCKET)) { + /* Multiplexed connections can only be HTTP/2 for now */ + struct http_conn *httpc = &check->proto.httpc; + if(multiplexed >= httpc->settings.max_concurrent_streams) { infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)", multiplexed); continue; @@ -1484,16 +1554,121 @@ void Curl_verboseconnect(struct Curl_easy *data, struct connectdata *conn) { if(data->set.verbose) - infof(data, "Connected to %s (%s) port %u", + infof(data, "Connected to %s (%s) port %u (#%ld)", #ifndef CURL_DISABLE_PROXY conn->bits.socksproxy ? conn->socks_proxy.host.dispname : conn->bits.httpproxy ? conn->http_proxy.host.dispname : #endif conn->bits.conn_to_host ? conn->conn_to_host.dispname : conn->host.dispname, - conn->primary_ip, conn->port); + conn->primary_ip, conn->port, conn->connection_id); +} +#endif + +/* + * Helpers for IDNA conversions. + */ +bool Curl_is_ASCII_name(const char *hostname) +{ + /* get an UNSIGNED local version of the pointer */ + const unsigned char *ch = (const unsigned char *)hostname; + + if(!hostname) /* bad input, consider it ASCII! */ + return TRUE; + + while(*ch) { + if(*ch++ & 0x80) + return FALSE; + } + return TRUE; +} + +/* + * Perform any necessary IDN conversion of hostname + */ +CURLcode Curl_idnconvert_hostname(struct Curl_easy *data, + struct hostname *host) +{ +#ifndef USE_LIBIDN2 + (void)data; + (void)data; +#elif defined(CURL_DISABLE_VERBOSE_STRINGS) + (void)data; +#endif + + /* set the name we use to display the host name */ + host->dispname = host->name; + + /* Check name for non-ASCII and convert hostname to ACE form if we can */ + if(!Curl_is_ASCII_name(host->name)) { +#ifdef USE_LIBIDN2 + if(idn2_check_version(IDN2_VERSION)) { + char *ace_hostname = NULL; +#if IDN2_VERSION_NUMBER >= 0x00140000 + /* IDN2_NFC_INPUT: Normalize input string using normalization form C. + IDN2_NONTRANSITIONAL: Perform Unicode TR46 non-transitional + processing. */ + int flags = IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL; +#else + int flags = IDN2_NFC_INPUT; +#endif + int rc = IDN2_LOOKUP(host->name, &ace_hostname, flags); + if(rc != IDN2_OK) + /* fallback to TR46 Transitional mode for better IDNA2003 + compatibility */ + rc = IDN2_LOOKUP(host->name, &ace_hostname, + IDN2_TRANSITIONAL); + if(rc == IDN2_OK) { + host->encalloc = (char *)ace_hostname; + /* change the name pointer to point to the encoded hostname */ + host->name = host->encalloc; + } + else { + failf(data, "Failed to convert %s to ACE; %s", host->name, + idn2_strerror(rc)); + return CURLE_URL_MALFORMAT; + } + } +#elif defined(USE_WIN32_IDN) + char *ace_hostname = NULL; + + if(Curl_win32_idn_to_ascii(host->name, &ace_hostname)) { + host->encalloc = ace_hostname; + /* change the name pointer to point to the encoded hostname */ + host->name = host->encalloc; + } + else { + char buffer[STRERROR_LEN]; + failf(data, "Failed to convert %s to ACE; %s", host->name, + Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); + return CURLE_URL_MALFORMAT; + } +#else + infof(data, "IDN support not present, can't parse Unicode domains"); +#endif + } + return CURLE_OK; } + +/* + * Frees data allocated by idnconvert_hostname() + */ +void Curl_free_idnconverted_hostname(struct hostname *host) +{ +#if defined(USE_LIBIDN2) + if(host->encalloc) { + idn2_free(host->encalloc); /* must be freed with idn2_free() since this was + allocated by libidn */ + host->encalloc = NULL; + } +#elif defined(USE_WIN32_IDN) + free(host->encalloc); /* must be freed with free() since this was + allocated by Curl_win32_idn_to_ascii */ + host->encalloc = NULL; +#else + (void)host; #endif +} /* * Allocate and initialize a new connectdata object. @@ -1504,13 +1679,62 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) if(!conn) return NULL; +#ifdef USE_SSL + /* The SSL backend-specific data (ssl_backend_data) objects are allocated as + a separate array to ensure suitable alignment. + Note that these backend pointers can be swapped by vtls (eg ssl backend + data becomes proxy backend data). */ + { + size_t onesize = Curl_ssl->sizeof_ssl_backend_data; + size_t totalsize = onesize; + char *ssl; + +#ifndef CURL_DISABLE_FTP + totalsize *= 2; +#endif +#ifndef CURL_DISABLE_PROXY + totalsize *= 2; +#endif + + ssl = calloc(1, totalsize); + if(!ssl) { + free(conn); + return NULL; + } + conn->ssl_extra = ssl; + conn->ssl[FIRSTSOCKET].backend = (void *)ssl; +#ifndef CURL_DISABLE_FTP + ssl += onesize; + conn->ssl[SECONDARYSOCKET].backend = (void *)ssl; +#endif +#ifndef CURL_DISABLE_PROXY + ssl += onesize; + conn->proxy_ssl[FIRSTSOCKET].backend = (void *)ssl; +#ifndef CURL_DISABLE_FTP + ssl += onesize; + conn->proxy_ssl[SECONDARYSOCKET].backend = (void *)ssl; +#endif +#endif + } +#endif + + conn->handler = &Curl_handler_dummy; /* Be sure we have a handler defined + already from start to avoid NULL + situations and checks */ + /* and we setup a few fields in case we end up actually using this struct */ conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */ conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */ + conn->tempsock[0] = CURL_SOCKET_BAD; /* no file descriptor */ + conn->tempsock[1] = CURL_SOCKET_BAD; /* no file descriptor */ conn->connection_id = -1; /* no ID */ conn->port = -1; /* unknown at this point */ conn->remote_port = -1; /* unknown at this point */ +#if defined(USE_RECV_BEFORE_SEND_WORKAROUND) && defined(DEBUGBUILD) + conn->postponed[0].bindsock = CURL_SOCKET_BAD; /* no file descriptor */ + conn->postponed[1].bindsock = CURL_SOCKET_BAD; /* no file descriptor */ +#endif /* USE_RECV_BEFORE_SEND_WORKAROUND && DEBUGBUILD */ /* Default protocol-independent behavior doesn't support persistent connections, so we set this to force-close. Protocols that support @@ -1521,7 +1745,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->created = Curl_now(); /* Store current time to give a baseline to keepalive connection times. */ - conn->keepalive = conn->created; + conn->keepalive = Curl_now(); #ifndef CURL_DISABLE_PROXY conn->http_proxy.proxytype = data->set.proxytype; @@ -1534,8 +1758,8 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->bits.httpproxy = (conn->bits.proxy && (conn->http_proxy.proxytype == CURLPROXY_HTTP || conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0 || - IS_HTTPS_PROXY(conn->http_proxy.proxytype))) ? - TRUE : FALSE; + conn->http_proxy.proxytype == CURLPROXY_HTTPS)) ? + TRUE : FALSE; conn->bits.socksproxy = (conn->bits.proxy && !conn->bits.httpproxy) ? TRUE : FALSE; @@ -1594,13 +1818,16 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) it may live on without (this specific) Curl_easy */ conn->fclosesocket = data->set.fclosesocket; conn->closesocket_client = data->set.closesocket_client; - conn->lastused = conn->created; - conn->gssapi_delegation = data->set.gssapi_delegation; + conn->lastused = Curl_now(); /* used now */ return conn; -error: + error: + Curl_llist_destroy(&conn->easyq, NULL); free(conn->localdev); +#ifdef USE_SSL + free(conn->ssl_extra); +#endif free(conn); return NULL; } @@ -1766,13 +1993,14 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, if(!use_set_uh) { char *newurl; uc = curl_url_set(uh, CURLUPART_URL, data->state.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)); + CURLU_GUESS_SCHEME | + CURLU_NON_SUPPORT_SCHEME | + (data->set.disallow_username_in_url ? + CURLU_DISALLOW_USER : 0) | + (data->set.path_as_is ? CURLU_PATH_AS_IS : 0)); if(uc) { - failf(data, "URL rejected: %s", curl_url_strerror(uc)); + DEBUGF(infof(data, "curl_url_set rejected %s: %s", data->state.url, + curl_url_strerror(uc))); return Curl_uc_to_curlcode(uc); } @@ -1823,9 +2051,26 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, /************************************************************* * IDN-convert the hostnames *************************************************************/ - result = Curl_idnconvert_hostname(&conn->host); + result = Curl_idnconvert_hostname(data, &conn->host); if(result) return result; + if(conn->bits.conn_to_host) { + result = Curl_idnconvert_hostname(data, &conn->conn_to_host); + if(result) + return result; + } +#ifndef CURL_DISABLE_PROXY + if(conn->bits.httpproxy) { + result = Curl_idnconvert_hostname(data, &conn->http_proxy.host); + if(result) + return result; + } + if(conn->bits.socksproxy) { + result = Curl_idnconvert_hostname(data, &conn->socks_proxy.host); + if(result) + return result; + } +#endif #ifndef CURL_DISABLE_HSTS /* HSTS upgrade */ @@ -1864,7 +2109,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, * User name and password set with their own options override the * credentials possibly set in the URL. */ - if(!data->set.str[STRING_PASSWORD]) { + if(!data->state.aptr.passwd) { uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0); if(!uc) { char *decoded; @@ -2161,12 +2406,8 @@ static CURLcode parse_proxy(struct Curl_easy *data, goto error; } - if(strcasecompare("https", scheme)) { - if(proxytype != CURLPROXY_HTTPS2) - proxytype = CURLPROXY_HTTPS; - else - proxytype = CURLPROXY_HTTPS2; - } + if(strcasecompare("https", scheme)) + proxytype = CURLPROXY_HTTPS; else if(strcasecompare("socks5h", scheme)) proxytype = CURLPROXY_SOCKS5_HOSTNAME; else if(strcasecompare("socks5", scheme)) @@ -2186,18 +2427,17 @@ static CURLcode parse_proxy(struct Curl_easy *data, } } else { - failf(data, "Unsupported proxy syntax in \'%s\': %s", proxy, - curl_url_strerror(uc)); + failf(data, "Unsupported proxy syntax in \'%s\'", proxy); result = CURLE_COULDNT_RESOLVE_PROXY; goto error; } #ifdef USE_SSL - if(!Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) + if(!(Curl_ssl->supports & SSLSUPP_HTTPS_PROXY)) #endif - if(IS_HTTPS_PROXY(proxytype)) { + if(proxytype == CURLPROXY_HTTPS) { failf(data, "Unsupported proxy \'%s\', libcurl is built without the " - "HTTPS-proxy support.", proxy); + "HTTPS-proxy support.", proxy); result = CURLE_NOT_BUILT_IN; goto error; } @@ -2209,7 +2449,7 @@ static CURLcode parse_proxy(struct Curl_easy *data, proxytype == CURLPROXY_SOCKS4; proxyinfo = sockstype ? &conn->socks_proxy : &conn->http_proxy; - proxyinfo->proxytype = (unsigned char)proxytype; + proxyinfo->proxytype = proxytype; /* Is there a username and password given in this proxy url? */ uc = curl_url_get(uhp, CURLUPART_USER, &proxyuser, CURLU_URLDECODE); @@ -2254,7 +2494,7 @@ static CURLcode parse_proxy(struct Curl_easy *data, given */ port = (int)data->set.proxyport; else { - if(IS_HTTPS_PROXY(proxytype)) + if(proxytype == CURLPROXY_HTTPS) port = CURL_DEFAULT_HTTPS_PROXY_PORT; else port = CURL_DEFAULT_PROXY_PORT; @@ -2279,7 +2519,7 @@ static CURLcode parse_proxy(struct Curl_easy *data, result = CURLE_OUT_OF_MEMORY; goto error; } - /* path will be "/", if no path was found */ + /* path will be "/", if no path was was found */ if(strcmp("/", path)) { is_unix_proxy = TRUE; free(host); @@ -2312,7 +2552,7 @@ static CURLcode parse_proxy(struct Curl_easy *data, } #endif -error: + error: free(proxyuser); free(proxypasswd); free(host); @@ -2334,17 +2574,22 @@ static CURLcode parse_proxy_auth(struct Curl_easy *data, data->state.aptr.proxyuser : ""; const char *proxypasswd = data->state.aptr.proxypasswd ? data->state.aptr.proxypasswd : ""; - CURLcode result = Curl_urldecode(proxyuser, 0, &conn->http_proxy.user, NULL, - REJECT_ZERO); - if(!result) - result = Curl_setstropt(&data->state.aptr.proxyuser, - conn->http_proxy.user); - if(!result) + CURLcode result = CURLE_OK; + + if(proxyuser) { + result = Curl_urldecode(proxyuser, 0, &conn->http_proxy.user, NULL, + REJECT_ZERO); + if(!result) + result = Curl_setstropt(&data->state.aptr.proxyuser, + conn->http_proxy.user); + } + if(!result && proxypasswd) { result = Curl_urldecode(proxypasswd, 0, &conn->http_proxy.passwd, NULL, REJECT_ZERO); - if(!result) - result = Curl_setstropt(&data->state.aptr.proxypasswd, - conn->http_proxy.passwd); + if(!result) + result = Curl_setstropt(&data->state.aptr.proxypasswd, + conn->http_proxy.passwd); + } return result; } @@ -2357,7 +2602,6 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, char *socksproxy = NULL; char *no_proxy = NULL; CURLcode result = CURLE_OK; - bool spacesep = FALSE; /************************************************************* * Extract the user and password from the authentication string @@ -2404,8 +2648,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, } if(Curl_check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY] ? - data->set.str[STRING_NOPROXY] : no_proxy, - &spacesep)) { + data->set.str[STRING_NOPROXY] : no_proxy)) { Curl_safefree(proxy); Curl_safefree(socksproxy); } @@ -2414,8 +2657,6 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, /* if the host is not in the noproxy list, detect proxy. */ proxy = detect_proxy(data, conn); #endif /* CURL_DISABLE_HTTP */ - if(spacesep) - infof(data, "space-separated NOPROXY patterns are deprecated"); Curl_safefree(no_proxy); @@ -2463,7 +2704,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, if(conn->http_proxy.host.rawalloc) { #ifdef CURL_DISABLE_HTTP - /* asking for an HTTP proxy is a bit funny when HTTP is disabled... */ + /* asking for a HTTP proxy is a bit funny when HTTP is disabled... */ result = CURLE_UNSUPPORTED_PROTOCOL; goto out; #else @@ -2480,7 +2721,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, #endif } else { - conn->bits.httpproxy = FALSE; /* not an HTTP proxy */ + conn->bits.httpproxy = FALSE; /* not a HTTP proxy */ conn->bits.tunnel_proxy = FALSE; /* no tunneling if not HTTP */ } @@ -2569,13 +2810,29 @@ CURLcode Curl_parse_login_details(const char *login, const size_t len, size_t plen; size_t olen; + /* the input length check is because this is called directly from setopt + and isn't going through the regular string length check */ + size_t llen = strlen(login); + if(llen > CURL_MAX_INPUT_LENGTH) + return CURLE_BAD_FUNCTION_ARGUMENT; + /* Attempt to find the password separator */ - if(passwdp) - psep = memchr(login, ':', len); + if(passwdp) { + psep = strchr(login, ':'); + + /* Within the constraint of the login string */ + if(psep >= login + len) + psep = NULL; + } /* Attempt to find the options separator */ - if(optionsp) - osep = memchr(login, ';', len); + if(optionsp) { + osep = strchr(login, ';'); + + /* Within the constraint of the login string */ + if(osep >= login + len) + osep = NULL; + } /* Calculate the portion lengths */ ulen = (psep ? @@ -2728,7 +2985,7 @@ static CURLcode override_login(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; } /* no user was set but a password, set a blank user */ - if(!*userp && *passwdp) { + if(userp && !*userp && *passwdp) { *userp = strdup(""); if(!*userp) return CURLE_OUT_OF_MEMORY; @@ -2900,16 +3157,17 @@ static CURLcode parse_connect_to_host_port(struct Curl_easy *data, } /* now, clone the cleaned host name */ - DEBUGASSERT(hostptr); - *hostname_result = strdup(hostptr); - if(!*hostname_result) { - result = CURLE_OUT_OF_MEMORY; - goto error; + if(hostptr) { + *hostname_result = strdup(hostptr); + if(!*hostname_result) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } } *port_result = port; -error: + error: free(host_dup); return result; } @@ -3269,79 +3527,80 @@ static CURLcode resolve_server(struct Curl_easy *data, } /* - * Cleanup the connection `temp`, just allocated for `data`, before using the - * previously `existing` one for `data`. All relevant info is copied over - * and `temp` is freed. + * Cleanup the connection just allocated before we can move along and use the + * previously existing one. All relevant data is copied over and old_conn is + * ready for freeing once this function returns. */ static void reuse_conn(struct Curl_easy *data, - struct connectdata *temp, - struct connectdata *existing) + struct connectdata *old_conn, + struct connectdata *conn) { - /* get the user+password information from the temp struct since it may + /* 'local_ip' and 'local_port' get filled with local's numerical + ip address and port number whenever an outgoing connection is + **established** from the primary socket to a remote address. */ + char local_ip[MAX_IPADR_LEN] = ""; + int local_port = -1; + + /* get the user+password information from the old_conn struct since it may * be new for this request even when we re-use an existing connection */ - if(temp->user) { + if(old_conn->user) { /* use the new user name and password though */ - Curl_safefree(existing->user); - Curl_safefree(existing->passwd); - existing->user = temp->user; - existing->passwd = temp->passwd; - temp->user = NULL; - temp->passwd = NULL; + Curl_safefree(conn->user); + Curl_safefree(conn->passwd); + conn->user = old_conn->user; + conn->passwd = old_conn->passwd; + old_conn->user = NULL; + old_conn->passwd = NULL; } #ifndef CURL_DISABLE_PROXY - existing->bits.proxy_user_passwd = temp->bits.proxy_user_passwd; - if(existing->bits.proxy_user_passwd) { + conn->bits.proxy_user_passwd = old_conn->bits.proxy_user_passwd; + if(conn->bits.proxy_user_passwd) { /* use the new proxy user name and proxy password though */ - Curl_safefree(existing->http_proxy.user); - Curl_safefree(existing->socks_proxy.user); - Curl_safefree(existing->http_proxy.passwd); - Curl_safefree(existing->socks_proxy.passwd); - existing->http_proxy.user = temp->http_proxy.user; - existing->socks_proxy.user = temp->socks_proxy.user; - existing->http_proxy.passwd = temp->http_proxy.passwd; - existing->socks_proxy.passwd = temp->socks_proxy.passwd; - temp->http_proxy.user = NULL; - temp->socks_proxy.user = NULL; - temp->http_proxy.passwd = NULL; - temp->socks_proxy.passwd = NULL; - } -#endif - - /* Finding a connection for reuse in the cache matches, among other - * things on the "remote-relevant" hostname. This is not necessarily - * the authority of the URL, e.g. conn->host. For example: - * - we use a proxy (not tunneling). we want to send all requests - * that use the same proxy on this connection. - * - we have a "connect-to" setting that may redirect the hostname of - * a new request to the same remote endpoint of an existing conn. - * We want to reuse an existing conn to the remote endpoint. - * Since connection reuse does not match on conn->host necessarily, we - * switch `existing` conn to `temp` conn's host settings. - * TODO: is this correct in the case of TLS connections that have - * used the original hostname in SNI to negotiate? Do we send - * requests for another host through the different SNI? - */ - Curl_free_idnconverted_hostname(&existing->host); - Curl_free_idnconverted_hostname(&existing->conn_to_host); - Curl_safefree(existing->host.rawalloc); - Curl_safefree(existing->conn_to_host.rawalloc); - existing->host = temp->host; - temp->host.rawalloc = NULL; - temp->host.encalloc = NULL; - existing->conn_to_host = temp->conn_to_host; - temp->conn_to_host.rawalloc = NULL; - existing->conn_to_port = temp->conn_to_port; - existing->remote_port = temp->remote_port; - Curl_safefree(existing->hostname_resolve); - - existing->hostname_resolve = temp->hostname_resolve; - temp->hostname_resolve = NULL; + Curl_safefree(conn->http_proxy.user); + Curl_safefree(conn->socks_proxy.user); + Curl_safefree(conn->http_proxy.passwd); + Curl_safefree(conn->socks_proxy.passwd); + conn->http_proxy.user = old_conn->http_proxy.user; + conn->socks_proxy.user = old_conn->socks_proxy.user; + conn->http_proxy.passwd = old_conn->http_proxy.passwd; + conn->socks_proxy.passwd = old_conn->socks_proxy.passwd; + old_conn->http_proxy.user = NULL; + old_conn->socks_proxy.user = NULL; + old_conn->http_proxy.passwd = NULL; + old_conn->socks_proxy.passwd = NULL; + } +#endif + + Curl_free_idnconverted_hostname(&conn->host); + Curl_free_idnconverted_hostname(&conn->conn_to_host); + Curl_safefree(conn->host.rawalloc); + Curl_safefree(conn->conn_to_host.rawalloc); + conn->host = old_conn->host; + old_conn->host.rawalloc = NULL; + old_conn->host.encalloc = NULL; + conn->conn_to_host = old_conn->conn_to_host; + old_conn->conn_to_host.rawalloc = NULL; + conn->conn_to_port = old_conn->conn_to_port; + conn->remote_port = old_conn->remote_port; + Curl_safefree(conn->hostname_resolve); + + conn->hostname_resolve = old_conn->hostname_resolve; + old_conn->hostname_resolve = NULL; + + /* persist connection info in session handle */ + if(conn->transport == TRNSPRT_TCP) { + Curl_conninfo_local(data, conn->sock[FIRSTSOCKET], + local_ip, &local_port); + } + Curl_persistconninfo(data, conn, local_ip, local_port); + + conn_reset_all_postponed_data(old_conn); /* free buffers */ /* re-use init */ - existing->bits.reuse = TRUE; /* yes, we're re-using here */ + conn->bits.reuse = TRUE; /* yes, we're re-using here */ - conn_free(data, temp); + conn_free(old_conn); } /** @@ -3365,7 +3624,7 @@ static CURLcode create_conn(struct Curl_easy *data, { CURLcode result = CURLE_OK; struct connectdata *conn; - struct connectdata *existing = NULL; + struct connectdata *conn_temp = NULL; bool reuse; bool connections_available = TRUE; bool force_reuse = FALSE; @@ -3471,26 +3730,6 @@ static CURLcode create_conn(struct Curl_easy *data, if(result) goto out; - /************************************************************* - * IDN-convert the proxy hostnames - *************************************************************/ -#ifndef CURL_DISABLE_PROXY - if(conn->bits.httpproxy) { - result = Curl_idnconvert_hostname(&conn->http_proxy.host); - if(result) - return result; - } - if(conn->bits.socksproxy) { - result = Curl_idnconvert_hostname(&conn->socks_proxy.host); - if(result) - return result; - } -#endif - if(conn->bits.conn_to_host) { - result = Curl_idnconvert_hostname(&conn->conn_to_host); - if(result) - return result; - } /************************************************************* * Check whether the host and the "connect to host" are equal. @@ -3527,6 +3766,13 @@ static CURLcode create_conn(struct Curl_easy *data, if(result) goto out; + conn->recv[FIRSTSOCKET] = Curl_recv_plain; + conn->send[FIRSTSOCKET] = Curl_send_plain; + conn->recv[SECONDARYSOCKET] = Curl_recv_plain; + conn->send[SECONDARYSOCKET] = Curl_send_plain; + + conn->bits.tcp_fastopen = data->set.tcp_fastopen; + /*********************************************************************** * file: is a special case in that it doesn't need a network connection ***********************************************************************/ @@ -3541,6 +3787,8 @@ static CURLcode create_conn(struct Curl_easy *data, /* Setup a "faked" transfer that'll do nothing */ if(!result) { + conn->bits.tcpconnect[FIRSTSOCKET] = TRUE; /* we are "connected */ + Curl_attach_connection(data, conn); result = Curl_conncache_add_conn(data); if(result) @@ -3566,13 +3814,6 @@ static CURLcode create_conn(struct Curl_easy *data, } #endif - /* Setup filter for network connections */ - conn->recv[FIRSTSOCKET] = Curl_conn_recv; - conn->send[FIRSTSOCKET] = Curl_conn_send; - conn->recv[SECONDARYSOCKET] = Curl_conn_recv; - conn->send[SECONDARYSOCKET] = Curl_conn_send; - conn->bits.tcp_fastopen = data->set.tcp_fastopen; - /* Get a cloned copy of the SSL config situation stored in the connection struct. But to get this going nicely, we must first make sure that the strings in the master copy are pointing to the correct @@ -3666,33 +3907,34 @@ static CURLcode create_conn(struct Curl_easy *data, /* 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 an HTTP + already (which happens due to follow-location or during a HTTP authentication phase). CONNECT_ONLY transfers also refuse reuse. */ - if((data->set.reuse_fresh && !data->state.followlocation) || + if((data->set.reuse_fresh && !data->state.this_is_a_follow) || data->set.connect_only) reuse = FALSE; else - reuse = ConnectionExists(data, conn, &existing, &force_reuse, &waitpipe); + reuse = ConnectionExists(data, conn, &conn_temp, &force_reuse, &waitpipe); if(reuse) { /* * We already have a connection for this, we got the former connection in - * `existing` and thus we need to cleanup the one we just - * allocated before we can move along and use `existing`. + * the conn_temp variable and thus we need to cleanup the one we just + * allocated before we can move along and use the previously existing one. */ - reuse_conn(data, conn, existing); - conn = existing; + reuse_conn(data, conn, conn_temp); + conn = conn_temp; *in_connect = conn; #ifndef CURL_DISABLE_PROXY - infof(data, "Re-using existing connection with %s %s", + infof(data, "Re-using existing connection #%ld with %s %s", + conn->connection_id, conn->bits.proxy?"proxy":"host", conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname : conn->http_proxy.host.name ? conn->http_proxy.host.dispname : conn->host.dispname); #else - infof(data, "Re-using existing connection with host %s", - conn->host.dispname); + infof(data, "Re-using existing connection #%ld with host %s", + conn->connection_id, conn->host.dispname); #endif } else { @@ -3755,7 +3997,7 @@ static CURLcode create_conn(struct Curl_easy *data, if(!connections_available) { infof(data, "No connections available."); - conn_free(data, conn); + conn_free(conn); *in_connect = NULL; result = CURLE_NO_CONNECTION_AVAILABLE; @@ -3815,13 +4057,6 @@ static CURLcode create_conn(struct Curl_easy *data, * Resolve the address of the server or proxy *************************************************************/ result = resolve_server(data, conn, async); - if(result) - goto out; - - /* Everything general done, inform filters that they need - * to prepare for a data transfer. - */ - result = Curl_conn_ev_data_setup(data); out: return result; @@ -3845,6 +4080,7 @@ CURLcode Curl_setup_conn(struct Curl_easy *data, *protocol_done = TRUE; return result; } + *protocol_done = FALSE; /* default to not done */ #ifndef CURL_DISABLE_PROXY /* set proxy_connect_closed to false unconditionally already here since it @@ -3861,11 +4097,26 @@ CURLcode Curl_setup_conn(struct Curl_easy *data, /* set start time here for timeout purposes in the connect procedure, it is later set again for the progress meter purpose */ conn->now = Curl_now(); - if(!conn->bits.reuse) - result = Curl_conn_setup(data, conn, FIRSTSOCKET, conn->dns_entry, - CURL_CF_SSL_DEFAULT); - /* not sure we need this flag to be passed around any more */ - *protocol_done = FALSE; + + if(CURL_SOCKET_BAD == conn->sock[FIRSTSOCKET]) { + conn->bits.tcpconnect[FIRSTSOCKET] = FALSE; + result = Curl_connecthost(data, conn, conn->dns_entry); + if(result) + return result; + } + else { + Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */ + if(conn->ssl[FIRSTSOCKET].use || + (conn->handler->protocol & PROTO_FAMILY_SSH)) + Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */ + conn->bits.tcpconnect[FIRSTSOCKET] = TRUE; + *protocol_done = TRUE; + Curl_updateconninfo(data, conn, conn->sock[FIRSTSOCKET]); + Curl_verboseconnect(data, conn); + } + + conn->now = Curl_now(); /* time this *after* the connect is done, we set + this here perhaps a second time */ return result; } @@ -3882,7 +4133,6 @@ CURLcode Curl_connect(struct Curl_easy *data, Curl_free_request_state(data); memset(&data->req, 0, sizeof(struct SingleRequest)); data->req.size = data->req.maxdownload = -1; - data->req.no_body = data->set.opt_no_body; /* call the stuff that needs to be called */ result = create_conn(data, &conn, asyncp); @@ -3944,11 +4194,12 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn) data->state.done = FALSE; /* *_done() is not called yet */ data->state.expect100header = FALSE; - if(data->req.no_body) + if(data->set.opt_no_body) /* in HTTP lingo, no body means using the HEAD request... */ data->state.httpreq = HTTPREQ_HEAD; k->start = Curl_now(); /* start time */ + k->now = k->start; /* current time is now */ k->header = TRUE; /* assume header */ k->bytecount = 0; k->ignorebody = FALSE; @@ -3959,102 +4210,3 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn) return CURLE_OK; } - -#if defined(USE_HTTP2) || defined(USE_HTTP3) - -#ifdef USE_NGHTTP2 - -static void priority_remove_child(struct Curl_easy *parent, - struct Curl_easy *child) -{ - struct Curl_data_prio_node **pnext = &parent->set.priority.children; - struct Curl_data_prio_node *pnode = parent->set.priority.children; - - DEBUGASSERT(child->set.priority.parent == parent); - while(pnode && pnode->data != child) { - pnext = &pnode->next; - pnode = pnode->next; - } - - DEBUGASSERT(pnode); - if(pnode) { - *pnext = pnode->next; - free(pnode); - } - - child->set.priority.parent = 0; - child->set.priority.exclusive = FALSE; -} - -CURLcode Curl_data_priority_add_child(struct Curl_easy *parent, - struct Curl_easy *child, - bool exclusive) -{ - if(child->set.priority.parent) { - priority_remove_child(child->set.priority.parent, child); - } - - if(parent) { - struct Curl_data_prio_node **tail; - struct Curl_data_prio_node *pnode; - - pnode = calloc(1, sizeof(*pnode)); - if(!pnode) - return CURLE_OUT_OF_MEMORY; - pnode->data = child; - - if(parent->set.priority.children && exclusive) { - /* exclusive: move all existing children underneath the new child */ - struct Curl_data_prio_node *node = parent->set.priority.children; - while(node) { - node->data->set.priority.parent = child; - node = node->next; - } - - tail = &child->set.priority.children; - while(*tail) - tail = &(*tail)->next; - - DEBUGASSERT(!*tail); - *tail = parent->set.priority.children; - parent->set.priority.children = 0; - } - - tail = &parent->set.priority.children; - while(*tail) { - (*tail)->data->set.priority.exclusive = FALSE; - tail = &(*tail)->next; - } - - DEBUGASSERT(!*tail); - *tail = pnode; - } - - child->set.priority.parent = parent; - child->set.priority.exclusive = exclusive; - return CURLE_OK; -} - -#endif /* USE_NGHTTP2 */ - -#ifdef USE_NGHTTP2 -static void data_priority_cleanup(struct Curl_easy *data) -{ - while(data->set.priority.children) { - struct Curl_easy *tmp = data->set.priority.children->data; - priority_remove_child(data, tmp); - if(data->set.priority.parent) - Curl_data_priority_add_child(data->set.priority.parent, tmp, FALSE); - } - - if(data->set.priority.parent) - priority_remove_child(data->set.priority.parent, data); -} -#endif - -void Curl_data_priority_clear_state(struct Curl_easy *data) -{ - memset(&data->state.priority, 0, sizeof(data->state.priority)); -} - -#endif /* defined(USE_HTTP2) || defined(USE_HTTP3) */ diff --git a/contrib/libs/curl/lib/url.h b/contrib/libs/curl/lib/url.h index f6a5b25737..ba4270d523 100644 --- a/contrib/libs/curl/lib/url.h +++ b/contrib/libs/curl/lib/url.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -49,6 +49,11 @@ CURLcode Curl_parse_login_details(const char *login, const size_t len, const struct Curl_handler *Curl_builtin_scheme(const char *scheme, size_t schemelen); +bool Curl_is_ASCII_name(const char *hostname); +CURLcode Curl_idnconvert_hostname(struct Curl_easy *data, + struct hostname *host); +void Curl_free_idnconverted_hostname(struct hostname *host); + #define CURL_DEFAULT_PROXY_PORT 1080 /* default proxy port unless specified */ #define CURL_DEFAULT_HTTPS_PROXY_PORT 443 /* default https proxy port unless specified */ @@ -59,18 +64,21 @@ const struct Curl_handler *Curl_builtin_scheme(const char *scheme, void Curl_verboseconnect(struct Curl_easy *data, struct connectdata *conn); #endif -#if defined(USE_HTTP2) || defined(USE_HTTP3) -void Curl_data_priority_clear_state(struct Curl_easy *data); +#ifdef CURL_DISABLE_PROXY +#define CONNECT_PROXY_SSL() FALSE #else -#define Curl_data_priority_clear_state(x) -#endif /* !(defined(USE_HTTP2) || defined(USE_HTTP3)) */ -#ifdef USE_NGHTTP2 -CURLcode Curl_data_priority_add_child(struct Curl_easy *parent, - struct Curl_easy *child, - bool exclusive); -#else -#define Curl_data_priority_add_child(x, y, z) CURLE_NOT_BUILT_IN -#endif +#define CONNECT_PROXY_SSL()\ + (conn->http_proxy.proxytype == CURLPROXY_HTTPS &&\ + !conn->bits.proxy_ssl_connected[sockindex]) + +#define CONNECT_FIRSTSOCKET_PROXY_SSL()\ + (conn->http_proxy.proxytype == CURLPROXY_HTTPS &&\ + !conn->bits.proxy_ssl_connected[FIRSTSOCKET]) + +#define CONNECT_SECONDARYSOCKET_PROXY_SSL()\ + (conn->http_proxy.proxytype == CURLPROXY_HTTPS &&\ + !conn->bits.proxy_ssl_connected[SECONDARYSOCKET]) +#endif /* !CURL_DISABLE_PROXY */ #endif /* HEADER_CURL_URL_H */ diff --git a/contrib/libs/curl/lib/urlapi-int.h b/contrib/libs/curl/lib/urlapi-int.h index d6e240aa36..43a83ef6e4 100644 --- a/contrib/libs/curl/lib/urlapi-int.h +++ b/contrib/libs/curl/lib/urlapi-int.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -28,9 +28,6 @@ size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen, bool guess_scheme); -CURLUcode Curl_url_set_authority(CURLU *u, const char *authority, - unsigned int flags); - #ifdef DEBUGBUILD CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, bool has_scheme); diff --git a/contrib/libs/curl/lib/urlapi.c b/contrib/libs/curl/lib/urlapi.c index e0c547605a..7dac81c85c 100644 --- a/contrib/libs/curl/lib/urlapi.c +++ b/contrib/libs/curl/lib/urlapi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,8 +33,6 @@ #include "inet_pton.h" #include "inet_ntop.h" #include "strdup.h" -#include "idn.h" -#include "curl_memrchr.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -58,15 +56,6 @@ /* scheme is not URL encoded, the longest libcurl supported ones are... */ #define MAX_SCHEME_LEN 40 -/* - * If ENABLE_IPV6 is disabled, we still want to parse IPv6 addresses, so make - * sure we have _some_ value for AF_INET6 without polluting our fake value - * everywhere. - */ -#if !defined(ENABLE_IPV6) && !defined(AF_INET6) -#define AF_INET6 (AF_INET + 1) -#endif - /* Internal representation of CURLU. Point to URL-encoded strings. */ struct Curl_URL { char *scheme; @@ -127,11 +116,14 @@ static const char *find_host_sep(const char *url) } /* - * Decide whether a character in a URL must be escaped. + * Decide in an encoding-independent manner whether a character in a URL must + * be escaped. This is used in urlencode_str(). */ -#define urlchar_needs_escaping(c) (!(ISCNTRL(c) || ISSPACE(c) || ISGRAPH(c))) +static bool urlchar_needs_escaping(int c) +{ + return !(ISCNTRL(c) || ISSPACE(c) || ISGRAPH(c)); +} -static const char hexdigits[] = "0123456789abcdef"; /* urlencode_str() writes data into an output dynbuf and URL-encodes the * spaces in the source URL accordingly. * @@ -175,10 +167,7 @@ static CURLUcode urlencode_str(struct dynbuf *o, const char *url, left = FALSE; if(urlchar_needs_escaping(*iptr)) { - char out[3]={'%'}; - out[1] = hexdigits[*iptr>>4]; - out[2] = hexdigits[*iptr & 0xf]; - if(Curl_dyn_addn(o, out, 3)) + if(Curl_dyn_addf(o, "%%%02x", *iptr)) return CURLUE_OUT_OF_MEMORY; } else { @@ -201,7 +190,7 @@ static CURLUcode urlencode_str(struct dynbuf *o, const char *url, size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen, bool guess_scheme) { - int i = 0; + int i; DEBUGASSERT(!buf || (buflen > MAX_SCHEME_LEN)); (void)buflen; /* only used in debug-builds */ if(buf) @@ -210,18 +199,17 @@ size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen, if(guess_scheme && STARTS_WITH_DRIVE_PREFIX(url)) return 0; #endif - if(ISALPHA(url[0])) - for(i = 1; i < MAX_SCHEME_LEN; ++i) { - char s = url[i]; - if(s && (ISALNUM(s) || (s == '+') || (s == '-') || (s == '.') )) { - /* RFC 3986 3.1 explains: - scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) - */ - } - else { - break; - } + for(i = 0; i < MAX_SCHEME_LEN; ++i) { + char s = url[i]; + if(s && (ISALNUM(s) || (s == '+') || (s == '-') || (s == '.') )) { + /* RFC 3986 3.1 explains: + scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + */ + } + else { + break; } + } if(i && (url[i] == ':') && ((url[i + 1] == '/') || !guess_scheme)) { /* If this does not guess scheme, the scheme always ends with the colon so that this also detects data: URLs etc. In guessing mode, data: could @@ -377,30 +365,27 @@ static char *concat_url(char *base, const char *relurl) return Curl_dyn_ptr(&newest); } -/* scan for byte values <= 31, 127 and sometimes space */ -static CURLUcode junkscan(const char *url, size_t *urllen, unsigned int flags) +/* scan for byte values < 31 or 127 */ +static bool junkscan(const char *part, unsigned int flags) { - static const 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 /* null-terminate */ - }; - size_t n = strlen(url); - size_t nfine; - - if(n > CURL_MAX_INPUT_LENGTH) - /* excessive input length */ - return CURLUE_MALFORMED_INPUT; - - nfine = strcspn(url, badbytes); - if((nfine != n) || - (!(flags & CURLU_ALLOW_SPACE) && strchr(url, ' '))) - return CURLUE_MALFORMED_INPUT; - - *urllen = n; - return CURLUE_OK; + if(part) { + static const 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 /* null-terminate */ + }; + 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 TRUE; + if(!(flags & CURLU_ALLOW_SPACE) && strchr(part, ' ')) + return TRUE; + } + return FALSE; } /* @@ -411,10 +396,8 @@ static CURLUcode junkscan(const char *url, size_t *urllen, unsigned int flags) * */ static CURLUcode parse_hostname_login(struct Curl_URL *u, - const char *login, - size_t len, - unsigned int flags, - size_t *offset) /* to the host name */ + struct dynbuf *host, + unsigned int flags) { CURLUcode result = CURLUE_OK; CURLcode ccode; @@ -430,12 +413,13 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, * * We need somewhere to put the embedded details, so do that first. */ + + char *login = Curl_dyn_ptr(host); char *ptr; DEBUGASSERT(login); - *offset = 0; - ptr = memchr(login, '@', len); + ptr = strchr(login, '@'); if(!ptr) goto out; @@ -465,25 +449,35 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, result = CURLUE_USER_NOT_ALLOWED; goto out; } - free(u->user); + if(junkscan(userp, flags)) { + result = CURLUE_BAD_USER; + goto out; + } u->user = userp; } if(passwdp) { - free(u->password); + if(junkscan(passwdp, flags)) { + result = CURLUE_BAD_PASSWORD; + goto out; + } u->password = passwdp; } if(optionsp) { - free(u->options); + if(junkscan(optionsp, flags)) { + result = CURLUE_BAD_LOGIN; + goto out; + } u->options = optionsp; } - /* the host name starts at this offset */ - *offset = ptr - login; - return CURLUE_OK; + /* move the name to the start of the host buffer */ + if(Curl_dyn_tail(host, strlen(ptr))) + return CURLUE_OUT_OF_MEMORY; -out: + return CURLUE_OK; + out: free(userp); free(passwdp); @@ -498,20 +492,35 @@ out: UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, bool has_scheme) { - char *portptr; + char *portptr = NULL; + char endbracket; + int len; char *hostname = Curl_dyn_ptr(host); /* - * Find the end of an IPv6 address on the ']' ending bracket. + * Find the end of an IPv6 address, either on the ']' ending bracket or + * a percent-encoded zone index. */ - if(hostname[0] == '[') { - portptr = strchr(hostname, ']'); - if(!portptr) + if(1 == sscanf(hostname, "[%*45[0123456789abcdefABCDEF:.]%c%n", + &endbracket, &len)) { + if(']' == endbracket) + portptr = &hostname[len]; + else if('%' == endbracket) { + int zonelen = len; + if(1 == sscanf(hostname + zonelen, "%*[^]]%c%n", &endbracket, &len)) { + if(']' != endbracket) + return CURLUE_BAD_IPV6; + portptr = &hostname[--zonelen + len + 1]; + } + else + return CURLUE_BAD_IPV6; + } + else return CURLUE_BAD_IPV6; - portptr++; + /* this is a RFC2732-style specified IP-address */ - if(*portptr) { + if(portptr && *portptr) { if(*portptr != ':') - return CURLUE_BAD_PORT_NUMBER; + return CURLUE_BAD_IPV6; } else portptr = NULL; @@ -522,6 +531,7 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, if(portptr) { char *rest; long port; + char portbuf[7]; size_t keep = portptr - hostname; /* Browser behavior adaptation. If there's a colon with no digits after, @@ -547,10 +557,11 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, if(rest[0]) return CURLUE_BAD_PORT_NUMBER; - u->portnum = port; + *rest = 0; /* generate a new port number string to get rid of leading zeroes etc */ - free(u->port); - u->port = aprintf("%ld", port); + msnprintf(portbuf, sizeof(portbuf), "%ld", port); + u->portnum = port; + u->port = strdup(portbuf); if(!u->port) return CURLUE_OUT_OF_MEMORY; } @@ -558,79 +569,74 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, return CURLUE_OK; } -/* this assumes 'hostname' now starts with [ */ -static CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname, - size_t hlen) /* length of hostname */ +static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, + size_t hlen) /* length of hostname */ { size_t len; - DEBUGASSERT(*hostname == '['); - if(hlen < 4) /* '[::]' is the shortest possible valid string */ - return CURLUE_BAD_IPV6; - hostname++; - hlen -= 2; - - /* only valid IPv6 letters are ok */ - len = strspn(hostname, "0123456789abcdefABCDEF:."); - - if(hlen != len) { - hlen = len; - if(hostname[len] == '%') { - /* this could now be '%[zone id]' */ - char zoneid[16]; - int i = 0; - char *h = &hostname[len + 1]; - /* pass '25' if present and is a url encoded percent sign */ - if(!strncmp(h, "25", 2) && h[2] && (h[2] != ']')) - h += 2; - while(*h && (*h != ']') && (i < 15)) - zoneid[i++] = *h++; - if(!i || (']' != *h)) - return CURLUE_BAD_IPV6; - zoneid[i] = 0; - u->zoneid = strdup(zoneid); - if(!u->zoneid) - return CURLUE_OUT_OF_MEMORY; - hostname[len] = ']'; /* insert end bracket */ - hostname[len + 1] = 0; /* terminate the hostname */ - } - else + DEBUGASSERT(hostname); + + if(!hostname[0]) + return CURLUE_NO_HOST; + else if(hostname[0] == '[') { + const char *l = "0123456789abcdefABCDEF:."; + if(hlen < 4) /* '[::]' is the shortest possible valid string */ return CURLUE_BAD_IPV6; - /* hostname is fine */ - } + hostname++; + hlen -= 2; - /* Check the IPv6 address. */ - { - char dest[16]; /* fits a binary IPv6 address */ - char norm[MAX_IPADR_LEN]; - hostname[hlen] = 0; /* end the address there */ - if(1 != Curl_inet_pton(AF_INET6, hostname, dest)) + if(hostname[hlen] != ']') return CURLUE_BAD_IPV6; - /* check if it can be done shorter */ - if(Curl_inet_ntop(AF_INET6, dest, norm, sizeof(norm)) && - (strlen(norm) < hlen)) { - strcpy(hostname, norm); - hlen = strlen(norm); - hostname[hlen + 1] = 0; + /* only valid letters are ok */ + len = strspn(hostname, l); + if(hlen != len) { + hlen = len; + if(hostname[len] == '%') { + /* this could now be '%[zone id]' */ + char zoneid[16]; + int i = 0; + char *h = &hostname[len + 1]; + /* pass '25' if present and is a url encoded percent sign */ + if(!strncmp(h, "25", 2) && h[2] && (h[2] != ']')) + h += 2; + while(*h && (*h != ']') && (i < 15)) + zoneid[i++] = *h++; + if(!i || (']' != *h)) + /* impossible to reach? */ + return CURLUE_MALFORMED_INPUT; + zoneid[i] = 0; + u->zoneid = strdup(zoneid); + if(!u->zoneid) + return CURLUE_OUT_OF_MEMORY; + hostname[len] = ']'; /* insert end bracket */ + hostname[len + 1] = 0; /* terminate the hostname */ + } + else + return CURLUE_BAD_IPV6; + /* hostname is fine */ } - hostname[hlen] = ']'; /* restore ending bracket */ - } - return CURLUE_OK; -} - -static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, - size_t hlen) /* length of hostname */ -{ - size_t len; - DEBUGASSERT(hostname); +#ifdef ENABLE_IPV6 + { + char dest[16]; /* fits a binary IPv6 address */ + char norm[MAX_IPADR_LEN]; + hostname[hlen] = 0; /* end the address there */ + if(1 != Curl_inet_pton(AF_INET6, hostname, dest)) + return CURLUE_BAD_IPV6; - if(!hlen) - return CURLUE_NO_HOST; - else if(hostname[0] == '[') - return ipv6_parse(u, hostname, hlen); + /* check if it can be done shorter */ + if(Curl_inet_ntop(AF_INET6, dest, norm, sizeof(norm)) && + (strlen(norm) < hlen)) { + strcpy(hostname, norm); + hlen = strlen(norm); + hostname[hlen + 1] = 0; + } + hostname[hlen] = ']'; /* restore ending bracket */ + } +#endif + } else { /* letters from the second string are not ok */ - len = strcspn(hostname, " \r\n\t/:#?!@{}[]\\$\'\"^`*<>=;,+&()%"); + len = strcspn(hostname, " \r\n\t/:#?!@{}[]\\$\'\"^`*<>=;,"); if(hlen != len) /* hostname with bad content */ return CURLUE_BAD_HOSTNAME; @@ -638,52 +644,50 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, return CURLUE_OK; } +#define HOSTNAME_END(x) (((x) == '/') || ((x) == '?') || ((x) == '#')) + /* * Handle partial IPv4 numerical addresses and different bases, like * '16843009', '0x7f', '0x7f.1' '0177.1.1.1' etc. * - * If the given input string is syntactically wrong IPv4 or any part for - * example is too big, this function returns HOST_NAME. + * If the given input string is syntactically wrong or any part for example is + * too big, this function returns FALSE and doesn't create any output. * * Output the "normalized" version of that input string in plain quad decimal - * integers. - * - * Returns the host type. + * integers and return TRUE. */ - -#define HOST_ERROR -1 /* out of memory */ -#define HOST_BAD -2 /* bad IPv4 address */ - -#define HOST_NAME 1 -#define HOST_IPV4 2 -#define HOST_IPV6 3 - -static int ipv4_normalize(struct dynbuf *host) +static bool ipv4_normalize(const char *hostname, char *outp, size_t olen) { bool done = FALSE; int n = 0; - const char *c = Curl_dyn_ptr(host); + const char *c = hostname; unsigned long parts[4] = {0, 0, 0, 0}; - CURLcode result = CURLE_OK; - - if(*c == '[') - return HOST_IPV6; while(!done) { char *endp; unsigned long l; - if(!ISDIGIT(*c)) + if((*c < '0') || (*c > '9')) /* most importantly this doesn't allow a leading plus or minus */ - return HOST_NAME; + return FALSE; l = strtoul(c, &endp, 0); + /* overflow or nothing parsed at all */ + if(((l == ULONG_MAX) && (errno == ERANGE)) || (endp == c)) + return FALSE; + +#if SIZEOF_LONG > 4 + /* a value larger than 32 bits */ + if(l > UINT_MAX) + return FALSE; +#endif + parts[n] = l; c = endp; - switch(*c) { - case '.': + switch (*c) { + case '.' : if(n == 3) - return HOST_NAME; + return FALSE; n++; c++; break; @@ -693,63 +697,51 @@ static int ipv4_normalize(struct dynbuf *host) break; default: - return HOST_NAME; + return FALSE; } - - /* overflow */ - if((l == ULONG_MAX) && (errno == ERANGE)) - return HOST_NAME; - -#if SIZEOF_LONG > 4 - /* a value larger than 32 bits */ - if(l > UINT_MAX) - return HOST_NAME; -#endif } + /* this is deemed a valid IPv4 numerical address */ + switch(n) { case 0: /* a -- 32 bits */ - Curl_dyn_reset(host); - - result = Curl_dyn_addf(host, "%u.%u.%u.%u", - parts[0] >> 24, (parts[0] >> 16) & 0xff, - (parts[0] >> 8) & 0xff, parts[0] & 0xff); + msnprintf(outp, olen, "%u.%u.%u.%u", + parts[0] >> 24, (parts[0] >> 16) & 0xff, + (parts[0] >> 8) & 0xff, parts[0] & 0xff); break; case 1: /* a.b -- 8.24 bits */ if((parts[0] > 0xff) || (parts[1] > 0xffffff)) - return HOST_NAME; - Curl_dyn_reset(host); - result = Curl_dyn_addf(host, "%u.%u.%u.%u", - parts[0], (parts[1] >> 16) & 0xff, - (parts[1] >> 8) & 0xff, parts[1] & 0xff); + return FALSE; + msnprintf(outp, olen, "%u.%u.%u.%u", + parts[0], (parts[1] >> 16) & 0xff, + (parts[1] >> 8) & 0xff, parts[1] & 0xff); break; case 2: /* a.b.c -- 8.8.16 bits */ if((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xffff)) - return HOST_NAME; - Curl_dyn_reset(host); - result = Curl_dyn_addf(host, "%u.%u.%u.%u", - parts[0], parts[1], (parts[2] >> 8) & 0xff, - parts[2] & 0xff); + return FALSE; + msnprintf(outp, olen, "%u.%u.%u.%u", + parts[0], parts[1], (parts[2] >> 8) & 0xff, + parts[2] & 0xff); break; case 3: /* a.b.c.d -- 8.8.8.8 bits */ if((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xff) || (parts[3] > 0xff)) - return HOST_NAME; - Curl_dyn_reset(host); - result = Curl_dyn_addf(host, "%u.%u.%u.%u", - parts[0], parts[1], parts[2], parts[3]); + return FALSE; + msnprintf(outp, olen, "%u.%u.%u.%u", + parts[0], parts[1], parts[2], parts[3]); break; } - if(result) - return HOST_ERROR; - return HOST_IPV4; + return TRUE; } /* if necessary, replace the host content with a URL decoded version */ -static CURLUcode urldecode_host(struct dynbuf *host) +static CURLUcode decode_host(struct dynbuf *host) { char *per = NULL; const char *hostname = Curl_dyn_ptr(host); + if(hostname[0] == '[') + /* only decode if not an ipv6 numerical */ + return CURLUE_OK; per = strchr(hostname, '%'); if(!per) /* nothing to decode */ @@ -772,78 +764,6 @@ static CURLUcode urldecode_host(struct dynbuf *host) return CURLUE_OK; } -static CURLUcode parse_authority(struct Curl_URL *u, - const char *auth, size_t authlen, - unsigned int flags, - struct dynbuf *host, - bool has_scheme) -{ - size_t offset; - CURLUcode result; - - /* - * Parse the login details and strip them out of the host name. - */ - result = parse_hostname_login(u, auth, authlen, flags, &offset); - if(result) - goto out; - - if(Curl_dyn_addn(host, auth + offset, authlen - offset)) { - result = CURLUE_OUT_OF_MEMORY; - goto out; - } - - result = Curl_parse_port(u, host, has_scheme); - if(result) - goto out; - - if(!Curl_dyn_len(host)) - return CURLUE_NO_HOST; - - switch(ipv4_normalize(host)) { - case HOST_IPV4: - break; - case HOST_IPV6: - result = ipv6_parse(u, Curl_dyn_ptr(host), Curl_dyn_len(host)); - break; - case HOST_NAME: - result = urldecode_host(host); - if(!result) - result = hostname_check(u, Curl_dyn_ptr(host), Curl_dyn_len(host)); - break; - case HOST_ERROR: - result = CURLUE_OUT_OF_MEMORY; - break; - case HOST_BAD: - default: - result = CURLUE_BAD_HOSTNAME; /* Bad IPv4 address even */ - break; - } - -out: - return result; -} - -CURLUcode Curl_url_set_authority(CURLU *u, const char *authority, - unsigned int flags) -{ - CURLUcode result; - struct dynbuf host; - - DEBUGASSERT(authority); - Curl_dyn_init(&host, CURL_MAX_INPUT_LENGTH); - - result = parse_authority(u, authority, strlen(authority), flags, - &host, !!u->scheme); - if(result) - Curl_dyn_free(&host); - else { - free(u->host); - u->host = Curl_dyn_ptr(&host); - } - return result; -} - /* * "Remove Dot Segments" * https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4 @@ -862,27 +782,32 @@ CURLUcode Curl_url_set_authority(CURLU *u, const char *authority, * * RETURNS * - * Zero for success and 'out' set to an allocated dedotdotified string. + * an allocated dedotdotified output string */ -UNITTEST int dedotdotify(const char *input, size_t clen, char **outp); -UNITTEST int dedotdotify(const char *input, size_t clen, char **outp) +UNITTEST char *dedotdotify(const char *input, size_t clen); +UNITTEST char *dedotdotify(const char *input, size_t clen) { + char *out = malloc(clen + 1); char *outptr; - const char *endp = &input[clen]; - char *out; - - *outp = NULL; - /* the path always starts with a slash, and a slash has not dot */ - if((clen < 2) || !memchr(input, '.', clen)) - return 0; - - out = malloc(clen + 1); + const char *orginput = input; + char *queryp; if(!out) - return 1; /* out of memory */ + return NULL; /* out of memory */ *out = 0; /* null-terminates, for inputs like "./" */ outptr = out; + if(!*input) + /* zero length input string, return that */ + return out; + + /* + * To handle query-parts properly, we must find it and remove it during the + * dotdot-operation and then append it again at the end to the output + * string. + */ + queryp = strchr(input, '?'); + do { bool dotdot = TRUE; if(*input == '.') { @@ -968,20 +893,30 @@ UNITTEST int dedotdotify(const char *input, size_t clen, char **outp) *outptr = 0; } - /* continue until end of path */ - } while(input < endp); + /* continue until end of input string OR, if there is a terminating + query part, stop there */ + } while(*input && (!queryp || (input < queryp))); + + if(queryp) { + size_t qlen; + /* There was a query part, append that to the output. */ + size_t oindex = queryp - orginput; + qlen = strlen(&orginput[oindex]); + memcpy(outptr, &orginput[oindex], qlen + 1); /* include zero byte */ + } - *outp = out; - return 0; /* success */ + return out; } static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) { const char *path; size_t pathlen; + bool uncpath = FALSE; char *query = NULL; char *fragment = NULL; char schemebuf[MAX_SCHEME_LEN + 1]; + const char *schemep = NULL; size_t schemelen = 0; size_t urllen; CURLUcode result = CURLUE_OK; @@ -992,9 +927,16 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) Curl_dyn_init(&host, CURL_MAX_INPUT_LENGTH); - result = junkscan(url, &urllen, flags); - if(result) + /************************************************************* + * Parse the URL. + ************************************************************/ + /* allocate scratch area */ + urllen = strlen(url); + if(urllen > CURL_MAX_INPUT_LENGTH) { + /* excessive input length */ + result = CURLUE_MALFORMED_INPUT; goto fail; + } schemelen = Curl_is_absolute_url(url, schemebuf, sizeof(schemebuf), flags & (CURLU_GUESS_SCHEME| @@ -1002,7 +944,6 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) /* handle the file: scheme */ if(schemelen && !strcmp(schemebuf, "file")) { - bool uncpath = FALSE; if(urllen <= 6) { /* file:/ is not enough to actually be a complete file: URL */ result = CURLUE_BAD_FILE_URL; @@ -1011,9 +952,8 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) /* path has been allocated large enough to hold this */ path = (char *)&url[5]; - pathlen = urllen - 5; - u->scheme = strdup("file"); + schemep = u->scheme = strdup("file"); if(!u->scheme) { result = CURLUE_OUT_OF_MEMORY; goto fail; @@ -1088,7 +1028,6 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) } path = ptr; - pathlen = urllen - (ptr - url); } if(!uncpath) @@ -1115,14 +1054,14 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) } else { /* clear path */ - const char *schemep = NULL; + const char *p; const char *hostp; - size_t hostlen; + size_t len; if(schemelen) { int i = 0; - const char *p = &url[schemelen + 1]; - while((*p == '/') && (i < 4)) { + p = &url[schemelen + 1]; + while(p && (*p == '/') && (i < 4)) { p++; i++; } @@ -1134,12 +1073,15 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) goto fail; } - if((i < 1) || (i > 3)) { + if((i < 1) || (i>3)) { /* less than one or more than three slashes */ result = CURLUE_BAD_SLASHES; goto fail; } - hostp = p; /* host name starts here */ + if(junkscan(schemep, flags)) { + result = CURLUE_BAD_SCHEME; + goto fail; + } } else { /* no scheme! */ @@ -1154,101 +1096,63 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) /* * The URL was badly formatted, let's try without scheme specified. */ - hostp = url; + p = url; } + hostp = p; /* host name starts here */ - if(schemep) { - u->scheme = strdup(schemep); - if(!u->scheme) { + /* find the end of the host name + port number */ + while(*p && !HOSTNAME_END(*p)) + p++; + + len = p - hostp; + if(len) { + if(Curl_dyn_addn(&host, hostp, len)) { result = CURLUE_OUT_OF_MEMORY; goto fail; } } - - /* find the end of the host name + port number */ - hostlen = strcspn(hostp, "/?#"); - path = &hostp[hostlen]; - - /* this pathlen also contains the query and the fragment */ - pathlen = urllen - (path - url); - if(hostlen) { - - result = parse_authority(u, hostp, hostlen, flags, &host, schemelen); - if(result) + else { + if(!(flags & CURLU_NO_AUTHORITY)) { + result = CURLUE_NO_HOST; goto fail; - - if((flags & CURLU_GUESS_SCHEME) && !schemep) { - const char *hostname = Curl_dyn_ptr(&host); - /* legacy curl-style guess based on host name */ - if(checkprefix("ftp.", hostname)) - schemep = "ftp"; - else if(checkprefix("dict.", hostname)) - schemep = "dict"; - else if(checkprefix("ldap.", hostname)) - schemep = "ldap"; - else if(checkprefix("imap.", hostname)) - schemep = "imap"; - else if(checkprefix("smtp.", hostname)) - schemep = "smtp"; - else if(checkprefix("pop3.", hostname)) - schemep = "pop3"; - else - schemep = "http"; - - u->scheme = strdup(schemep); - if(!u->scheme) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } } } - else if(flags & CURLU_NO_AUTHORITY) { - /* allowed to be empty. */ - if(Curl_dyn_add(&host, "")) { + + path = (char *)p; + + if(schemep) { + u->scheme = strdup(schemep); + if(!u->scheme) { result = CURLUE_OUT_OF_MEMORY; goto fail; } } - else { - result = CURLUE_NO_HOST; - goto fail; - } } fragment = strchr(path, '#'); if(fragment) { - fraglen = pathlen - (fragment - path); + fraglen = strlen(fragment); if(fraglen > 1) { /* skip the leading '#' in the copy but include the terminating null */ - if(flags & CURLU_URLENCODE) { - struct dynbuf enc; - Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - if(urlencode_str(&enc, fragment + 1, fraglen, TRUE, FALSE)) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - u->fragment = Curl_dyn_ptr(&enc); + u->fragment = Curl_memdup(fragment + 1, fraglen); + if(!u->fragment) { + result = CURLUE_OUT_OF_MEMORY; + goto fail; } - else { - u->fragment = Curl_memdup(fragment + 1, fraglen); - if(!u->fragment) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } + + if(junkscan(u->fragment, flags)) { + result = CURLUE_BAD_FRAGMENT; + goto fail; } } - /* after this, pathlen still contains the query */ - pathlen -= fraglen; } - DEBUGASSERT(pathlen < urllen); - query = memchr(path, '?', pathlen); - if(query) { - size_t qlen = fragment ? (size_t)(fragment - query) : - pathlen - (query - path); - pathlen -= qlen; + query = strchr(path, '?'); + if(query && (!fragment || (query < fragment))) { + size_t qlen = strlen(query) - fraglen; /* includes '?' */ + pathlen = strlen(path) - qlen - fraglen; if(qlen > 1) { - if(flags & CURLU_URLENCODE) { + if(qlen && (flags & CURLU_URLENCODE)) { struct dynbuf enc; Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); /* skip the leading question mark */ @@ -1266,6 +1170,11 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) } u->query[qlen - 1] = 0; } + + if(junkscan(u->query, flags)) { + result = CURLUE_BAD_QUERY; + goto fail; + } } else { /* single byte query */ @@ -1276,6 +1185,8 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) } } } + else + pathlen = strlen(path) - fraglen; if(pathlen && (flags & CURLU_URLENCODE)) { struct dynbuf enc; @@ -1288,8 +1199,8 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) path = u->path = Curl_dyn_ptr(&enc); } - if(pathlen <= 1) { - /* there is no path left or just the slash, unset */ + if(!pathlen) { + /* there is no path left, unset */ path = NULL; } else { @@ -1306,25 +1217,93 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) /* it might have encoded more than just the path so cut it */ u->path[pathlen] = 0; + if(junkscan(u->path, flags)) { + result = CURLUE_BAD_PATH; + goto fail; + } + if(!(flags & CURLU_PATH_AS_IS)) { /* remove ../ and ./ sequences according to RFC3986 */ - char *dedot; - int err = dedotdotify((char *)path, pathlen, &dedot); - if(err) { + char *newp = dedotdotify((char *)path, pathlen); + if(!newp) { result = CURLUE_OUT_OF_MEMORY; goto fail; } - if(dedot) { - free(u->path); - u->path = dedot; + free(u->path); + u->path = newp; + } + } + + if(Curl_dyn_len(&host)) { + char normalized_ipv4[sizeof("255.255.255.255") + 1]; + + /* + * Parse the login details and strip them out of the host name. + */ + result = parse_hostname_login(u, &host, flags); + if(!result) + result = Curl_parse_port(u, &host, schemelen); + if(result) + goto fail; + + if(junkscan(Curl_dyn_ptr(&host), flags)) { + result = CURLUE_BAD_HOSTNAME; + goto fail; + } + + if(ipv4_normalize(Curl_dyn_ptr(&host), + normalized_ipv4, sizeof(normalized_ipv4))) { + Curl_dyn_reset(&host); + if(Curl_dyn_add(&host, normalized_ipv4)) { + result = CURLUE_OUT_OF_MEMORY; + goto fail; } } + else { + result = decode_host(&host); + if(!result) + result = hostname_check(u, Curl_dyn_ptr(&host), Curl_dyn_len(&host)); + if(result) + goto fail; + } + + if((flags & CURLU_GUESS_SCHEME) && !schemep) { + const char *hostname = Curl_dyn_ptr(&host); + /* legacy curl-style guess based on host name */ + if(checkprefix("ftp.", hostname)) + schemep = "ftp"; + else if(checkprefix("dict.", hostname)) + schemep = "dict"; + else if(checkprefix("ldap.", hostname)) + schemep = "ldap"; + else if(checkprefix("imap.", hostname)) + schemep = "imap"; + else if(checkprefix("smtp.", hostname)) + schemep = "smtp"; + else if(checkprefix("pop3.", hostname)) + schemep = "pop3"; + else + schemep = "http"; + + u->scheme = strdup(schemep); + if(!u->scheme) { + result = CURLUE_OUT_OF_MEMORY; + goto fail; + } + } + } + else if(flags & CURLU_NO_AUTHORITY) { + /* allowed to be empty. */ + if(Curl_dyn_add(&host, "")) { + result = CURLUE_OUT_OF_MEMORY; + goto fail; + } } u->host = Curl_dyn_ptr(&host); return result; -fail: + fail: Curl_dyn_free(&host); free_urlhandle(u); return result; @@ -1371,7 +1350,7 @@ void curl_url_cleanup(CURLU *u) } \ } while(0) -CURLU *curl_url_dup(const CURLU *in) +CURLU *curl_url_dup(CURLU *in) { struct Curl_URL *u = calloc(sizeof(struct Curl_URL), 1); if(u) { @@ -1387,20 +1366,19 @@ CURLU *curl_url_dup(const CURLU *in) u->portnum = in->portnum; } return u; -fail: + fail: curl_url_cleanup(u); return NULL; } -CURLUcode curl_url_get(const CURLU *u, CURLUPart what, +CURLUcode curl_url_get(CURLU *u, CURLUPart what, char **part, unsigned int flags) { - const char *ptr; + char *ptr; CURLUcode ifmissing = CURLUE_UNKNOWN_PART; char portbuf[7]; bool urldecode = (flags & CURLU_URLDECODE)?1:0; bool urlencode = (flags & CURLU_URLENCODE)?1:0; - bool punycode = FALSE; bool plusdecode = FALSE; (void)flags; if(!u) @@ -1430,7 +1408,6 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, case CURLUPART_HOST: ptr = u->host; ifmissing = CURLUE_NO_HOST; - punycode = (flags & CURLU_PUNYCODE)?1:0; break; case CURLUPART_ZONEID: ptr = u->zoneid; @@ -1462,8 +1439,11 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, break; case CURLUPART_PATH: ptr = u->path; - if(!ptr) - ptr = "/"; + if(!ptr) { + ptr = u->path = strdup("/"); + if(!u->path) + return CURLUE_OUT_OF_MEMORY; + } break; case CURLUPART_QUERY: ptr = u->query; @@ -1480,7 +1460,6 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, char *options = u->options; char *port = u->port; char *allochost = NULL; - punycode = (flags & CURLU_PUNYCODE)?1:0; if(u->scheme && strcasecompare("file", u->scheme)) { url = aprintf("file://%s%s%s", u->path, @@ -1535,19 +1514,39 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, if(!allochost) return CURLUE_OUT_OF_MEMORY; } - else if(punycode) { - if(!Curl_is_ASCII_name(u->host)) { -#ifndef USE_IDN - return CURLUE_LACKS_IDN; -#else - allochost = Curl_idn_decode(u->host); - if(!allochost) - return CURLUE_OUT_OF_MEMORY; -#endif + else { + /* only encode '%' in output host name */ + char *host = u->host; + bool percent = FALSE; + /* first, count number of percents present in the name */ + while(*host) { + if(*host == '%') { + percent = TRUE; + break; + } + host++; + } + /* if there were percent(s), encode the host name */ + if(percent) { + struct dynbuf enc; + CURLcode result; + Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); + host = u->host; + while(*host) { + if(*host == '%') + result = Curl_dyn_addn(&enc, "%25", 3); + else + result = Curl_dyn_addn(&enc, host, 1); + if(result) + return CURLUE_OUT_OF_MEMORY; + host++; + } + free(u->host); + u->host = Curl_dyn_ptr(&enc); } } - url = aprintf("%s://%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + 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 ? ":": "", @@ -1558,6 +1557,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, allochost ? allochost : u->host, port ? ":": "", port ? port : "", + (u->path && (u->path[0] != '/')) ? "/": "", u->path ? u->path : "/", (u->query && u->query[0]) ? "?": "", (u->query && u->query[0]) ? u->query : "", @@ -1611,19 +1611,6 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, free(*part); *part = Curl_dyn_ptr(&enc); } - else if(punycode) { - if(!Curl_is_ASCII_name(u->host)) { -#ifndef USE_IDN - return CURLUE_LACKS_IDN; -#else - char *allochost = Curl_idn_decode(*part); - if(!allochost) - return CURLUE_OUT_OF_MEMORY; - free(*part); - *part = allochost; -#endif - } - } return CURLUE_OK; } @@ -1639,10 +1626,8 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, bool urlencode = (flags & CURLU_URLENCODE)? 1 : 0; bool plusencode = FALSE; bool urlskipslash = FALSE; - bool leadingslash = FALSE; bool appendquery = FALSE; bool equalsencode = FALSE; - size_t nalloc; if(!u) return CURLUE_BAD_HANDLE; @@ -1695,17 +1680,10 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, return CURLUE_OK; } - nalloc = strlen(part); - if(nalloc > CURL_MAX_INPUT_LENGTH) - /* excessive input length */ - return CURLUE_MALFORMED_INPUT; - switch(what) { - case CURLUPART_SCHEME: { - size_t plen = strlen(part); - const char *s = part; - if((plen > MAX_SCHEME_LEN) || (plen < 1)) - /* too long or too short */ + case CURLUPART_SCHEME: + if(strlen(part) > MAX_SCHEME_LEN) + /* too long */ return CURLUE_BAD_SCHEME; if(!(flags & CURLU_NON_SUPPORT_SCHEME) && /* verify that it is a fine scheme */ @@ -1713,19 +1691,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, return CURLUE_UNSUPPORTED_SCHEME; storep = &u->scheme; urlencode = FALSE; /* never */ - if(ISALPHA(*s)) { - /* ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */ - while(--plen) { - if(ISALNUM(*s) || (*s == '+') || (*s == '-') || (*s == '.')) - s++; /* fine */ - else - return CURLUE_BAD_SCHEME; - } - } - else - return CURLUE_BAD_SCHEME; break; - } case CURLUPART_USER: storep = &u->user; break; @@ -1735,10 +1701,15 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, case CURLUPART_OPTIONS: storep = &u->options; break; - case CURLUPART_HOST: + case CURLUPART_HOST: { + size_t len = strcspn(part, " \r\n"); + if(strlen(part) != len) + /* hostname with bad content */ + return CURLUE_BAD_HOSTNAME; storep = &u->host; Curl_safefree(u->zoneid); break; + } case CURLUPART_ZONEID: storep = &u->zoneid; break; @@ -1757,7 +1728,6 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, break; case CURLUPART_PATH: urlskipslash = TRUE; - leadingslash = TRUE; /* enforce */ storep = &u->path; break; case CURLUPART_QUERY: @@ -1806,17 +1776,18 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, } DEBUGASSERT(storep); { - const char *newp; - struct dynbuf enc; - Curl_dyn_init(&enc, nalloc * 3 + 1 + leadingslash); + const char *newp = part; + size_t nalloc = strlen(part); + + if(nalloc > CURL_MAX_INPUT_LENGTH) + /* excessive input length */ + return CURLUE_MALFORMED_INPUT; - if(leadingslash && (part[0] != '/')) { - CURLcode result = Curl_dyn_addn(&enc, "/", 1); - if(result) - return CURLUE_OUT_OF_MEMORY; - } if(urlencode) { const unsigned char *i; + struct dynbuf enc; + + Curl_dyn_init(&enc, nalloc * 3 + 1); for(i = (const unsigned char *)part; *i; i++) { CURLcode result; @@ -1836,21 +1807,19 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, return CURLUE_OUT_OF_MEMORY; } else { - char out[3]={'%'}; - out[1] = hexdigits[*i>>4]; - out[2] = hexdigits[*i & 0xf]; - result = Curl_dyn_addn(&enc, out, 3); + result = Curl_dyn_addf(&enc, "%%%02x", *i); if(result) return CURLUE_OUT_OF_MEMORY; } } + newp = Curl_dyn_ptr(&enc); } else { char *p; - CURLcode result = Curl_dyn_add(&enc, part); - if(result) + newp = strdup(part); + if(!newp) return CURLUE_OUT_OF_MEMORY; - p = Curl_dyn_ptr(&enc); + p = (char *)newp; while(*p) { /* make sure percent encoded are lower case */ if((*p == '%') && ISXDIGIT(p[1]) && ISXDIGIT(p[2]) && @@ -1863,7 +1832,6 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, p++; } } - newp = Curl_dyn_ptr(&enc); if(appendquery) { /* Append the 'newp' string onto the old query. Add a '&' separator if @@ -1872,24 +1840,24 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, size_t querylen = u->query ? strlen(u->query) : 0; bool addamperand = querylen && (u->query[querylen -1] != '&'); if(querylen) { - struct dynbuf qbuf; - Curl_dyn_init(&qbuf, CURL_MAX_INPUT_LENGTH); + struct dynbuf enc; + Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - if(Curl_dyn_addn(&qbuf, u->query, querylen)) /* add original query */ + if(Curl_dyn_addn(&enc, u->query, querylen)) /* add original query */ goto nomem; if(addamperand) { - if(Curl_dyn_addn(&qbuf, "&", 1)) + if(Curl_dyn_addn(&enc, "&", 1)) goto nomem; } - if(Curl_dyn_add(&qbuf, newp)) + if(Curl_dyn_add(&enc, newp)) goto nomem; - Curl_dyn_free(&enc); + free((char *)newp); free(*storep); - *storep = Curl_dyn_ptr(&qbuf); + *storep = Curl_dyn_ptr(&enc); return CURLUE_OK; -nomem: - Curl_dyn_free(&enc); + nomem: + free((char *)newp); return CURLUE_OUT_OF_MEMORY; } } @@ -1900,8 +1868,8 @@ nomem: /* Skip hostname check, it's allowed to be empty. */ } else { - if(!n || hostname_check(u, (char *)newp, n)) { - Curl_dyn_free(&enc); + if(hostname_check(u, (char *)newp, n)) { + free((char *)newp); return CURLUE_BAD_HOSTNAME; } } diff --git a/contrib/libs/curl/lib/urldata.h b/contrib/libs/curl/lib/urldata.h index 76dc38f030..323a53a0d0 100644 --- a/contrib/libs/curl/lib/urldata.h +++ b/contrib/libs/curl/lib/urldata.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -113,7 +113,6 @@ typedef unsigned int curl_prot_t; input easier and better. */ #define CURL_MAX_INPUT_LENGTH 8000000 - #include "cookie.h" #include "psl.h" #include "formdata.h" @@ -134,7 +133,6 @@ typedef unsigned int curl_prot_t; #include "hash.h" #include "splay.h" #include "dynbuf.h" -#include "dynhds.h" /* return the count of bytes sent, or -1 on error */ typedef ssize_t (Curl_send)(struct Curl_easy *data, /* transfer */ @@ -169,10 +167,10 @@ typedef CURLcode (*Curl_datastream)(struct Curl_easy *data, #include "rtsp.h" #include "smb.h" #include "mqtt.h" -#include "ftplistparser.h" +#include "wildcard.h" #include "multihandle.h" +#include "quic.h" #include "c-hyper.h" -#include "cf-socket.h" #ifdef HAVE_GSSAPI # ifdef HAVE_GSSGNU @@ -209,17 +207,8 @@ typedef CURLcode (*Curl_datastream)(struct Curl_easy *data, #define UPLOADBUFFER_MIN CURL_MAX_WRITE_SIZE #define CURLEASY_MAGIC_NUMBER 0xc0dedbadU -#ifdef DEBUGBUILD -/* On a debug build, we want to fail hard on easy handles that - * are not NULL, but no longer have the MAGIC touch. This gives - * us early warning on things only discovered by valgrind otherwise. */ -#define GOOD_EASY_HANDLE(x) \ - (((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER))? TRUE: \ - (DEBUGASSERT(!(x)), FALSE)) -#else #define GOOD_EASY_HANDLE(x) \ ((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) -#endif #ifdef HAVE_GSSAPI /* Types needed for krb5-ftp connections */ @@ -260,7 +249,22 @@ typedef enum { /* SSL backend-specific data; declared differently by each SSL backend */ struct ssl_backend_data; +/* struct for data related to each SSL connection */ +struct ssl_connect_data { + ssl_connection_state state; + ssl_connect_state connecting_state; +#if defined(USE_SSL) + struct ssl_backend_data *backend; +#endif + /* Use ssl encrypted communications TRUE/FALSE. The library is not + necessarily using ssl at the moment but at least asked to or means to use + it. See 'state' for the exact current state of the connection. */ + BIT(use); +}; + struct ssl_primary_config { + long version; /* what version the client wants to use */ + long version_max; /* max supported version the client wants to use*/ char *CApath; /* certificate dir (doesn't work on windows) */ char *CAfile; /* certificate to verify peer against */ char *issuercert; /* optional issuer certificate filename */ @@ -275,11 +279,10 @@ struct ssl_primary_config { #ifdef USE_TLS_SRP char *username; /* TLS username (for, e.g., SRP) */ char *password; /* TLS password (for, e.g., SRP) */ + enum CURL_TLSAUTH authtype; /* TLS authentication type (default SRP) */ #endif char *curves; /* list of curves to use */ unsigned char ssl_options; /* the CURLOPT_SSL_OPTIONS bitmask */ - unsigned int version_max; /* max supported version the client wants to use */ - unsigned char version; /* what version the client wants to use */ BIT(verifypeer); /* set TRUE if this is desired */ BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */ BIT(verifystatus); /* set TRUE if certificate status must be checked */ @@ -298,7 +301,7 @@ struct ssl_config_data { char *key_passwd; /* plain text private key password */ BIT(certinfo); /* gather lots of certificate info */ BIT(falsestart); - BIT(enable_beast); /* allow this flaw for interoperability's sake */ + BIT(enable_beast); /* allow this flaw for interoperability's sake*/ BIT(no_revoke); /* disable SSL certificate revocation checks */ BIT(no_partialchain); /* don't accept partial certificate chains */ BIT(revoke_best_effort); /* ignore SSL revocation offline/missing revocation @@ -310,7 +313,6 @@ struct ssl_config_data { struct ssl_general_config { size_t max_ssl_sessions; /* SSL session id cache size */ - int ca_cache_timeout; /* Certificate store cache timeout (seconds) */ }; /* information stored about one single SSL session */ @@ -474,8 +476,12 @@ struct negotiatedata { * Boolean values that concerns this connection. */ struct ConnectBits { + bool tcpconnect[2]; /* the TCP layer (or similar) is connected, this is set + the first time on the first connect function call */ #ifndef CURL_DISABLE_PROXY - BIT(httpproxy); /* if set, this transfer is done through an HTTP proxy */ + bool proxy_ssl_connected[2]; /* TRUE when SSL initialization for HTTPS proxy + is complete */ + BIT(httpproxy); /* if set, this transfer is done through a http proxy */ BIT(socksproxy); /* if set, this transfer is done through a socks proxy */ BIT(proxy_user_passwd); /* user+password for the proxy? */ BIT(tunnel_proxy); /* if CONNECT is used to "tunnel" through the proxy. @@ -508,6 +514,10 @@ struct ConnectBits { that we are creating a request with an auth header, but it is not the final request in the auth negotiation. */ + BIT(rewindaftersend);/* TRUE when the sending couldn't be stopped even + though it will be discarded. When the whole send + operation is done, we must call the data rewind + callback. */ #ifndef CURL_DISABLE_FTP BIT(ftp_use_epsv); /* As set with CURLOPT_FTP_USE_EPSV, but if we find out EPSV doesn't work we disable it for the forthcoming @@ -640,6 +650,7 @@ struct SingleRequest { curl_off_t pendingheader; /* this many bytes left to send is actually header and not body */ struct curltime start; /* transfer started at this time */ + struct curltime now; /* current time */ enum { HEADER_NORMAL, /* no bad header at all */ HEADER_PARTHEADER, /* part of the chunk is a bad header, the rest @@ -697,12 +708,7 @@ struct SingleRequest { #ifndef CURL_DISABLE_DOH struct dohdata *doh; /* DoH specific data for this request */ #endif -#if defined(WIN32) && defined(USE_WINSOCK) - struct curltime last_sndbuf_update; /* last time readwrite_upload called - win_update_buffer_size */ -#endif unsigned char setcookies; - unsigned char writer_stack_depth; /* Unencoding stack depth. */ BIT(header); /* incoming data has HTTP header */ BIT(content_range); /* set TRUE if Content-Range: was found */ BIT(upload_done); /* set to TRUE when doing chunked transfer-encoding @@ -718,7 +724,6 @@ struct SingleRequest { BIT(forbidchunk); /* used only to explicitly forbid chunk-upload for specific upload buffers. See readmoredata() in http.c for details. */ - BIT(no_body); /* the response has no body */ }; /* @@ -779,8 +784,8 @@ struct Curl_handler { /* This function *MAY* be set to a protocol-dependent function that is run * by the curl_disconnect(), as a step in the disconnection. If the handler * is called because the connection has been considered dead, - * dead_connection is set to TRUE. The connection is (again) associated with - * the transfer here. + * dead_connection is set to TRUE. The connection is already disassociated + * from the transfer here. */ CURLcode (*disconnect)(struct Curl_easy *, struct connectdata *, bool dead_connection); @@ -826,7 +831,7 @@ struct Curl_handler { #define PROTOPT_CREDSPERREQUEST (1<<7) /* requires login credentials per request instead of per connection */ #define PROTOPT_ALPN (1<<8) /* set ALPN for this */ -/* (1<<9) was PROTOPT_STREAM, now free */ +#define PROTOPT_STREAM (1<<9) /* a protocol with individual logical streams */ #define PROTOPT_URLOPTIONS (1<<10) /* allow options part in the userinfo field of the URL */ #define PROTOPT_PROXY_AS_HTTP (1<<11) /* allow this non-HTTP scheme over a @@ -844,9 +849,23 @@ struct Curl_handler { #define CONNRESULT_NONE 0 /* No extra information. */ #define CONNRESULT_DEAD (1<<0) /* The connection is dead. */ +#ifdef USE_RECV_BEFORE_SEND_WORKAROUND +struct postponed_data { + char *buffer; /* Temporal store for received data during + sending, must be freed */ + size_t allocated_size; /* Size of temporal store */ + size_t recv_size; /* Size of received data during sending */ + size_t recv_processed; /* Size of processed part of postponed data */ +#ifdef DEBUGBUILD + curl_socket_t bindsock;/* Structure must be bound to specific socket, + used only for DEBUGASSERT */ +#endif /* DEBUGBUILD */ +}; +#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */ + struct proxy_info { struct hostname host; - int port; + long port; unsigned char proxytype; /* curl_proxytype: what kind of proxy that is in use */ char *user; /* proxy user name string, allocated */ @@ -854,6 +873,38 @@ struct proxy_info { }; struct ldapconninfo; +struct http_connect_state; + +/* for the (SOCKS) connect state machine */ +enum connect_t { + CONNECT_INIT, + CONNECT_SOCKS_INIT, /* 1 */ + CONNECT_SOCKS_SEND, /* 2 waiting to send more first data */ + CONNECT_SOCKS_READ_INIT, /* 3 set up read */ + CONNECT_SOCKS_READ, /* 4 read server response */ + CONNECT_GSSAPI_INIT, /* 5 */ + CONNECT_AUTH_INIT, /* 6 setup outgoing auth buffer */ + CONNECT_AUTH_SEND, /* 7 send auth */ + CONNECT_AUTH_READ, /* 8 read auth response */ + CONNECT_REQ_INIT, /* 9 init SOCKS "request" */ + CONNECT_RESOLVING, /* 10 */ + CONNECT_RESOLVED, /* 11 */ + CONNECT_RESOLVE_REMOTE, /* 12 */ + CONNECT_REQ_SEND, /* 13 */ + CONNECT_REQ_SENDING, /* 14 */ + CONNECT_REQ_READ, /* 15 */ + CONNECT_REQ_READ_MORE, /* 16 */ + CONNECT_DONE /* 17 connected fine to the remote or the SOCKS proxy */ +}; + +#define SOCKS_STATE(x) (((x) >= CONNECT_SOCKS_INIT) && \ + ((x) < CONNECT_DONE)) + +struct connstate { + enum connect_t state; + ssize_t outstanding; /* send this many bytes more */ + unsigned char *outp; /* send from this pointer */ +}; #define TRNSPRT_TCP 3 #define TRNSPRT_UDP 4 @@ -865,11 +916,12 @@ struct ldapconninfo; * unique for an entire connection. */ struct connectdata { + struct connstate cnnct; struct Curl_llist_element bundle_node; /* conncache */ /* chunk is for HTTP chunked encoding, but is in the general connectdata - struct only because we can do just about any protocol through an HTTP - proxy and an HTTP proxy may in fact respond using chunked encoding */ + struct only because we can do just about any protocol through a HTTP proxy + and a HTTP proxy may in fact respond using chunked encoding */ struct Curl_chunker chunk; curl_closesocket_callback fclosesocket; /* function closing the socket(s) */ @@ -882,8 +934,8 @@ struct connectdata { #define CONN_INUSE(c) ((c)->easyq.size) /**** Fields set when inited and not modified again */ - curl_off_t connection_id; /* Contains a unique number to make it easier to - track the connections in the log output */ + long connection_id; /* Contains a unique number to make it easier to + track the connections in the log output */ /* 'dns_entry' is the particular host we use. This points to an entry in the DNS cache and it will not get pruned while locked. It gets unlocked in @@ -891,9 +943,16 @@ struct connectdata { there is no name resolve done. */ struct Curl_dns_entry *dns_entry; - /* 'remote_addr' is the particular IP we connected to. it is owned, set - * and NULLed by the connected socket filter (if there is one). */ - const struct Curl_sockaddr_ex *remote_addr; + /* 'ip_addr' is the particular IP we connected to. It points to a struct + within the DNS cache, so this pointer is only valid as long as the DNS + cache entry remains locked. It gets unlocked in multi_done() */ + struct Curl_addrinfo *ip_addr; + struct Curl_addrinfo *tempaddr[2]; /* for happy eyeballs */ + +#ifdef ENABLE_QUIC + struct quicsocket hequic[2]; /* two, for happy eyeballs! */ + struct quicsocket *quic; +#endif struct hostname host; char *hostname_resolve; /* host name to resolve to address, allocated */ @@ -922,16 +981,37 @@ struct connectdata { struct curltime lastused; /* when returned to the connection cache */ curl_socket_t sock[2]; /* two sockets, the second is used for the data transfer when doing FTP */ + curl_socket_t tempsock[2]; /* temporary sockets for happy eyeballs */ + int tempfamily[2]; /* family used for the temp sockets */ Curl_recv *recv[2]; Curl_send *send[2]; - struct Curl_cfilter *cfilter[2]; /* connection filters */ +#ifdef USE_RECV_BEFORE_SEND_WORKAROUND + struct postponed_data postponed[2]; /* two buffers for two sockets */ +#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */ + struct ssl_connect_data ssl[2]; /* this is for ssl-stuff */ +#ifndef CURL_DISABLE_PROXY + struct ssl_connect_data proxy_ssl[2]; /* this is for proxy ssl-stuff */ +#endif +#ifdef USE_SSL + void *ssl_extra; /* separately allocated backend-specific data */ +#endif struct ssl_primary_config ssl_config; #ifndef CURL_DISABLE_PROXY struct ssl_primary_config proxy_ssl_config; #endif struct ConnectBits bits; /* various state-flags for this connection */ + /* connecttime: when connect() is called on the current IP address. Used to + be able to track when to move on to try next IP - but only when the multi + interface is used. */ + struct curltime connecttime; + + /* The field below gets set in Curl_connecthost */ + /* how long time in milliseconds to spend on trying to connect to each IP + address, per family */ + timediff_t timeoutms_per_addr[2]; + const struct Curl_handler *handler; /* Connection's protocol handler */ const struct Curl_handler *given; /* The protocol first given */ @@ -994,15 +1074,16 @@ struct connectdata { struct negotiatedata proxyneg; /* state data for proxy Negotiate auth */ #endif -#ifndef CURL_DISABLE_HTTP /* for chunked-encoded trailer */ struct dynbuf trailer; -#endif union { #ifndef CURL_DISABLE_FTP struct ftp_conn ftpc; #endif +#ifndef CURL_DISABLE_HTTP + struct http_conn httpc; +#endif #ifdef USE_SSH struct ssh_conn sshc; #endif @@ -1029,11 +1110,9 @@ struct connectdata { #ifndef CURL_DISABLE_MQTT struct mqtt_conn mqtt; #endif -#ifdef USE_WEBSOCKETS - struct websocket *ws; -#endif } proto; + struct http_connect_state *connect_state; /* for HTTP CONNECT */ struct connectbundle *bundle; /* The bundle we are member of */ #ifdef USE_UNIX_SOCKETS char *unix_domain_socket; @@ -1048,12 +1127,14 @@ struct connectdata { that subsequent bound-requested connections aren't accidentally re-using wrong connections. */ char *localdev; - unsigned short localportrange; + int localportrange; + int cselect_bits; /* bitmask of socket events */ int waitfor; /* current READ/WRITE bits to wait for */ #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) int socks5_gssapi_enctype; #endif - /* The field below gets set in connect.c:connecthost() */ + /* The field below gets set in Curl_connecthost */ + int num_addr; /* number of addresses to try to connect to */ int port; /* which port to use locally - to connect to */ int remote_port; /* the remote port, not the proxy port! */ int conn_to_port; /* the remote port to connect to. valid only if @@ -1064,17 +1145,12 @@ struct connectdata { unsigned short localport; unsigned short secondary_port; /* secondary socket remote port to connect to (ftp) */ - unsigned char cselect_bits; /* bitmask of socket events */ unsigned char alpn; /* APLN TLS negotiated protocol, a CURL_HTTP_VERSION* value */ -#ifndef CURL_DISABLE_PROXY - unsigned char proxy_alpn; /* APLN of proxy tunnel, CURL_HTTP_VERSION* */ -#endif unsigned char transport; /* one of the TRNSPRT_* defines */ unsigned char ip_version; /* copied from the Curl_easy at creation time */ unsigned char httpversion; /* the HTTP version*10 reported by the server */ unsigned char connect_only; - unsigned char gssapi_delegation; /* inherited from set.gssapi_delegation */ }; /* The end of connectdata. */ @@ -1203,29 +1279,10 @@ struct auth { should be RFC compliant */ }; -#ifdef USE_NGHTTP2 -struct Curl_data_prio_node { - struct Curl_data_prio_node *next; +struct Curl_http2_dep { + struct Curl_http2_dep *next; struct Curl_easy *data; }; -#endif - -/** - * Priority information for an easy handle in relation to others - * on the same connection. - * TODO: we need to adapt it to the new priority scheme as defined in RFC 9218 - */ -struct Curl_data_priority { -#ifdef USE_NGHTTP2 - /* tree like dependencies only implemented in nghttp2 */ - struct Curl_easy *parent; - struct Curl_data_prio_node *children; -#endif - int weight; -#ifdef USE_NGHTTP2 - BIT(exclusive); -#endif -}; /* * This struct is for holding data that was attempted to get sent to the user's @@ -1254,7 +1311,6 @@ typedef enum { EXPIRE_TOOFAST, EXPIRE_QUIC, EXPIRE_FTP_ACCEPT, - EXPIRE_ALPN_EYEBALLS, EXPIRE_LAST /* not an actual timer, used as a marker only */ } expire_id; @@ -1294,9 +1350,7 @@ struct UrlState { /* buffers to store authentication data in, as parsed from input options */ struct curltime keeps_speed; /* for the progress meter really */ - curl_off_t lastconnect_id; /* The last connection, -1 if undefined */ - curl_off_t recent_conn_id; /* The most recent connection used, might no - * longer exist */ + long lastconnect_id; /* The last connection, -1 if undefined */ struct dynbuf headerb; /* buffer to store headers in */ char *buffer; /* download buffer */ @@ -1346,6 +1400,11 @@ struct UrlState { /* a place to store the most recently set (S)FTP entrypath */ char *most_recent_ftp_entrypath; + unsigned char httpwant; /* when non-zero, a specific HTTP version requested + to be used in the library's request(s) */ + unsigned char httpversion; /* the lowest HTTP version*10 reported by any + server involved in this request */ + #if !defined(WIN32) && !defined(MSDOS) && !defined(__EMX__) /* do FTP line-end conversions on most platforms */ #define CURL_DO_LINEEND_CONV @@ -1363,23 +1422,32 @@ struct UrlState { long rtsp_next_client_CSeq; /* the session's next client CSeq */ long rtsp_next_server_CSeq; /* the session's next server CSeq */ long rtsp_CSeq_recv; /* most recent CSeq received */ - - unsigned char rtp_channel_mask[32]; /* for the correctness checking of the - interleaved data */ #endif curl_off_t infilesize; /* size of file to upload, -1 means unknown. Copied from set.filesize at start of operation */ #if defined(USE_HTTP2) || defined(USE_HTTP3) - struct Curl_data_priority priority; /* shallow copy of data->set */ + size_t drain; /* Increased when this stream has data to read, even if its + socket is not necessarily is readable. Decreased when + checked. */ #endif curl_read_callback fread_func; /* read callback/function */ void *in; /* CURLOPT_READDATA */ +#ifdef USE_HTTP2 + struct Curl_easy *stream_depends_on; + int stream_weight; +#endif CURLU *uh; /* URL handle for the current parsed URL */ struct urlpieces up; + unsigned char httpreq; /* Curl_HttpReq; what kind of HTTP request (if any) + is this */ char *url; /* work URL, copied from UserDefined */ char *referer; /* referer string */ +#ifndef CURL_DISABLE_COOKIES + struct curl_slist *cookielist; /* list of cookie files set by + curl_easy_setopt(COOKIEFILE) calls */ +#endif struct curl_slist *resolve; /* set to point to the set.resolve list when this should be dealt with in pretransfer */ #ifndef CURL_DISABLE_HTTP @@ -1387,7 +1455,7 @@ struct UrlState { struct dynbuf trailers_buf; /* a buffer containing the compiled trailing headers */ struct Curl_llist httphdrs; /* received headers */ - struct curl_header headerout[2]; /* for external purposes */ + struct curl_header headerout; /* for external purposes */ struct Curl_header_store *prevhead; /* the latest added header */ trailers_state trailers_state; /* whether we are sending trailers and what stage are we at */ @@ -1418,15 +1486,6 @@ struct UrlState { char *proxypasswd; } aptr; - unsigned char httpwant; /* when non-zero, a specific HTTP version requested - to be used in the library's request(s) */ - unsigned char httpversion; /* the lowest HTTP version*10 reported by any - server involved in this request */ - unsigned char httpreq; /* Curl_HttpReq; what kind of HTTP request (if any) - is this */ - unsigned char dselect_bits; /* != 0 -> bitmask of socket events for this - transfer overriding anything the socket may - report */ #ifdef CURLDEBUG BIT(conncache_lock); #endif @@ -1453,6 +1512,7 @@ struct UrlState { BIT(done); /* set to FALSE when Curl_init_do() is called and set to TRUE when multi_done() is called, to prevent multi_done() to get invoked twice when the multi interface is used. */ + BIT(stream_depends_e); /* set or don't set the Exclusive bit */ BIT(previouslypending); /* this transfer WAS in the multi->pending queue */ BIT(cookie_engine); BIT(prefer_ascii); /* ASCII rather than binary */ @@ -1460,10 +1520,6 @@ struct UrlState { BIT(url_alloc); /* URL string is malloc()'ed */ BIT(referer_alloc); /* referer string is malloc()ed */ BIT(wildcard_resolve); /* Set to true if any resolve change is a wildcard */ - BIT(rewindbeforesend);/* TRUE when the sending couldn't be stopped even - though it will be discarded. We must call the data - rewind callback before trying to send again. */ - BIT(upload); /* upload request */ }; /* @@ -1475,7 +1531,7 @@ struct UrlState { * Character pointer fields point to dynamic storage, unless otherwise stated. */ -struct Curl_multi; /* declared in multihandle.c */ +struct Curl_multi; /* declared and used only in multi.c */ /* * This enumeration MUST not use conditional directives (#ifdefs), new @@ -1564,8 +1620,6 @@ enum dupstring { STRING_DNS_LOCAL_IP4, STRING_DNS_LOCAL_IP6, STRING_SSL_EC_CURVES, - STRING_AWS_SIGV4, /* Parameters for V4 signature */ - STRING_HAPROXY_CLIENT_IP, /* CURLOPT_HAPROXY_CLIENT_IP */ /* -- end of null-terminated strings -- */ @@ -1575,6 +1629,8 @@ enum dupstring { STRING_COPYPOSTFIELDS, /* if POST, set the fields' values here */ + STRING_AWS_SIGV4, /* Parameters for V4 signature */ + STRING_LAST /* not used, just an end-of-list marker */ }; @@ -1599,12 +1655,18 @@ struct UserDefined { FILE *err; /* the stderr user data goes here */ void *debugdata; /* the data that will be passed to fdebug */ char *errorbuffer; /* (Static) store failure messages in here */ + long proxyport; /* If non-zero, use this port number by default. If the + proxy string features a ":[port]" that one will override + this. */ void *out; /* CURLOPT_WRITEDATA */ void *in_set; /* CURLOPT_READDATA */ void *writeheader; /* write the header to this if non-NULL */ unsigned short use_port; /* which port to use (when not using default) */ unsigned long httpauth; /* kind of HTTP authentication to use (bitmask) */ unsigned long proxyauth; /* kind of proxy authentication to use (bitmask) */ +#ifndef CURL_DISABLE_PROXY + unsigned long socks5auth;/* kind of SOCKS5 authentication to use (bitmask) */ +#endif long maxredirs; /* maximum no. of http(s) redirects to follow, set to -1 for infinity */ @@ -1614,9 +1676,8 @@ struct UserDefined { of strlen(), and then the data *may* be binary (contain zero bytes) */ unsigned short localport; /* local port number to bind to */ - unsigned short localportrange; /* number of additional port numbers to test - in case the 'localport' one can't be - bind()ed */ + int localportrange; /* number of additional port numbers to test in case the + 'localport' one can't be bind()ed */ curl_write_callback fwrite_func; /* function that stores the output */ curl_write_callback fwrite_header; /* function that stores headers */ curl_write_callback fwrite_rtp; /* function that stores interleaved RTP */ @@ -1638,13 +1699,7 @@ struct UserDefined { void *prereq_userp; /* pre-initial request user data */ void *seek_client; /* pointer to pass to the seek callback */ -#ifndef CURL_DISABLE_COOKIES - struct curl_slist *cookielist; /* list of cookie files set by - curl_easy_setopt(COOKIEFILE) calls */ -#endif #ifndef CURL_DISABLE_HSTS - struct curl_slist *hstslist; /* list of HSTS files set by - curl_easy_setopt(HSTS) calls */ curl_hstsread_callback hsts_read; void *hsts_read_userp; curl_hstswrite_callback hsts_write; @@ -1671,8 +1726,17 @@ struct UserDefined { download */ curl_off_t set_resume_from; /* continue [ftp] transfer from here */ struct curl_slist *headers; /* linked list of extra headers */ + struct curl_slist *proxyheaders; /* linked list of extra CONNECT headers */ struct curl_httppost *httppost; /* linked list of old POST data */ curl_mimepart mimepost; /* MIME/POST data. */ + struct curl_slist *quote; /* after connection is established */ + struct curl_slist *postquote; /* after the transfer */ + struct curl_slist *prequote; /* before the transfer, after type */ + struct curl_slist *source_quote; /* 3rd party quote */ + struct curl_slist *source_prequote; /* in 3rd party transfer mode - before + the transfer on source host */ + struct curl_slist *source_postquote; /* in 3rd party transfer mode - after + the transfer on source host */ #ifndef CURL_DISABLE_TELNET struct curl_slist *telnet_options; /* linked list of telnet options */ #endif @@ -1682,18 +1746,13 @@ struct UserDefined { the hostname and port to connect to */ time_t timevalue; /* what time to compare with */ unsigned char timecondition; /* kind of time comparison: curl_TimeCond */ + unsigned char proxytype; /* what kind of proxy: curl_proxytype */ unsigned char method; /* what kind of HTTP request: Curl_HttpReq */ unsigned char httpwant; /* when non-zero, a specific HTTP version requested to be used in the library's request(s) */ struct ssl_config_data ssl; /* user defined SSL stuff */ #ifndef CURL_DISABLE_PROXY struct ssl_config_data proxy_ssl; /* user defined SSL stuff for proxy */ - struct curl_slist *proxyheaders; /* linked list of extra CONNECT headers */ - unsigned short proxyport; /* If non-zero, use this port number by - default. If the proxy string features a - ":[port]" that one will override this. */ - unsigned char proxytype; /* what kind of proxy: curl_proxytype */ - unsigned char socks5auth;/* kind of SOCKS5 authentication to use (bitmask) */ #endif struct ssl_general_config general_ssl; /* general user defined SSL stuff */ int dns_cache_timeout; /* DNS cache timeout (seconds) */ @@ -1701,9 +1760,7 @@ struct UserDefined { unsigned int upload_buffer_size; /* size of upload buffer to use, keep it >= CURL_MAX_WRITE_SIZE */ void *private_data; /* application-private data */ -#ifndef CURL_DISABLE_HTTP struct curl_slist *http200aliases; /* linked list of aliases for http200 */ -#endif unsigned char ipver; /* the CURL_IPRESOLVE_* defines in the public header file 0 - whatever, 1 - v2, 2 - v6 */ curl_off_t max_filesize; /* Maximum file size to download */ @@ -1713,30 +1770,26 @@ struct UserDefined { unsigned char ftp_ccc; /* FTP CCC options: curl_ftpccc */ unsigned int accepttimeout; /* in milliseconds, 0 means no timeout */ #endif -#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) - struct curl_slist *quote; /* after connection is established */ - struct curl_slist *postquote; /* after the transfer */ - struct curl_slist *prequote; /* before the transfer, after type */ - /* Despite the name, ftp_create_missing_dirs is for FTP(S) and SFTP + /* Desppie the name ftp_create_missing_dirs is for FTP(S) and SFTP 1 - create directories that don't exist 2 - the same but also allow MKD to fail once */ unsigned char ftp_create_missing_dirs; -#endif #ifdef USE_LIBSSH2 curl_sshhostkeycallback ssh_hostkeyfunc; /* hostkey check callback */ void *ssh_hostkeyfunc_userp; /* custom pointer to callback */ #endif -#ifdef USE_SSH + curl_sshkeycallback ssh_keyfunc; /* key matching callback */ void *ssh_keyfunc_userp; /* custom pointer to callback */ - int ssh_auth_types; /* allowed SSH auth types */ - unsigned int new_directory_perms; /* when creating remote dirs */ -#endif #ifndef CURL_DISABLE_NETRC unsigned char use_netrc; /* enum CURL_NETRC_OPTION values */ #endif + curl_usessl use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or + IMAP or POP3 or others! */ unsigned int new_file_perms; /* when creating remote files */ + unsigned int new_directory_perms; /* when creating remote dirs */ + int ssh_auth_types; /* allowed SSH auth types */ char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */ struct curl_blob *blobs[BLOB_LAST]; #ifdef ENABLE_IPV6 @@ -1744,9 +1797,8 @@ struct UserDefined { #endif curl_prot_t allowed_protocols; curl_prot_t redir_protocols; -#ifndef CURL_DISABLE_MIME unsigned int mime_options; /* Mime option flags. */ -#endif + #ifndef CURL_DISABLE_RTSP void *rtp_out; /* write RTP to this if non-NULL */ /* Common RTSP header options */ @@ -1760,11 +1812,9 @@ struct UserDefined { curl_fnmatch_callback fnmatch; /* callback to decide which file corresponds to pattern (e.g. if WILDCARDMATCH is on) */ void *fnmatch_data; - void *wildcardptr; #endif - /* GSS-API credential delegation, see the documentation of - CURLOPT_GSSAPI_DELEGATION */ - unsigned char gssapi_delegation; + long gssapi_delegation; /* GSS-API credential delegation, see the + documentation of CURLOPT_GSSAPI_DELEGATION */ int tcp_keepidle; /* seconds in idle before sending keepalive probe */ int tcp_keepintvl; /* seconds between TCP keepalive probes */ @@ -1772,8 +1822,10 @@ struct UserDefined { size_t maxconnects; /* Max idle connections in the connection cache */ long expect_100_timeout; /* in milliseconds */ -#if defined(USE_HTTP2) || defined(USE_HTTP3) - struct Curl_data_priority priority; +#ifdef USE_HTTP2 + struct Curl_easy *stream_depends_on; + int stream_weight; + struct Curl_http2_dep *stream_dependents; #endif curl_resolver_start_callback resolver_start; /* optional callback called before resolver start */ @@ -1784,10 +1836,8 @@ struct UserDefined { struct Curl_easy *dohfor; /* this is a DoH request for that transfer */ #endif CURLU *uh; /* URL handle for the current parsed URL */ -#ifndef CURL_DISABLE_HTTP void *trailer_data; /* pointer to pass to trailer data callback */ curl_trailer_callback trailer_callback; /* trailing data callback */ -#endif char keep_post; /* keep POSTs as POSTs after a 30x request; each bit represents a request, from 301 to 303 */ #ifndef CURL_DISABLE_SMTP @@ -1795,8 +1845,6 @@ struct UserDefined { BIT(mail_rcpt_allowfails); /* allow RCPT TO command to fail for some recipients */ #endif - unsigned char use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or - IMAP or POP3 or others! (type: curl_usessl)*/ unsigned char connect_only; /* make connection/request, then let application use the socket */ BIT(is_fread_set); /* has read callback been set to non-NULL? */ @@ -1811,12 +1859,8 @@ struct UserDefined { /* Here follows boolean settings that define how to behave during this session. They are STATIC, set by libcurl users or at least initially and they don't change during operations. */ - BIT(quick_exit); /* set 1L when it is okay to leak things (like - threads), as we're about to exit() anyway and - don't want lengthy cleanups to delay termination, - e.g. after a DNS timeout */ BIT(get_filetime); /* get the time and get of the remote file */ - BIT(tunnel_thru_httpproxy); /* use CONNECT through an HTTP proxy */ + BIT(tunnel_thru_httpproxy); /* use CONNECT through a HTTP proxy */ BIT(prefer_ascii); /* ASCII rather than binary */ BIT(remote_append); /* append, not overwrite, on upload */ BIT(list_only); /* list directory */ @@ -1840,6 +1884,7 @@ struct UserDefined { BIT(http_auto_referer); /* set "correct" referer when following location: */ BIT(opt_no_body); /* as set with CURLOPT_NOBODY */ + BIT(upload); /* upload request */ BIT(verbose); /* output verbosity */ BIT(krb); /* Kerberos connection requested */ BIT(reuse_forbid); /* forbidden to be reused, close after use */ @@ -1866,6 +1911,7 @@ struct UserDefined { BIT(suppress_connect_headers); /* suppress proxy CONNECT response headers from user callbacks */ BIT(dns_shuffle_addresses); /* whether to shuffle addresses before use */ + BIT(stream_depends_e); /* set or don't set the Exclusive bit */ BIT(haproxyprotocol); /* whether to send HAProxy PROXY protocol v1 header */ BIT(abstract_unix_socket); @@ -1905,21 +1951,13 @@ struct Curl_easy { /* First a simple identifier to easier detect if a user mix up this easy handle with a multi handle. Set this to CURLEASY_MAGIC_NUMBER */ unsigned int magic; - /* once an easy handle is tied to a connection cache - a non-negative number to distinguish this transfer from - other using the same cache. For easier tracking - in log output. - This may wrap around after LONG_MAX to 0 again, so it - has no uniqueness guarantuee for very large processings. */ - curl_off_t id; /* first, two fields for the linked list of these */ struct Curl_easy *next; struct Curl_easy *prev; struct connectdata *conn; - struct Curl_llist_element connect_queue; /* for the pending and msgsent - lists */ + struct Curl_llist_element connect_queue; struct Curl_llist_element conn_queue; /* list per connectdata */ CURLMstate mstate; /* the handle's state */ @@ -1965,7 +2003,7 @@ struct Curl_easy { struct UrlState state; /* struct for fields used for state info and other dynamic purposes */ #ifndef CURL_DISABLE_FTP - struct WildcardData *wildcard; /* wildcard download state info */ + struct WildcardData wildcard; /* wildcard download state info */ #endif struct PureInfo info; /* stats, reports and info data */ struct curl_tlssessioninfo tsi; /* Information about the TLS session, only diff --git a/contrib/libs/curl/lib/vauth/cleartext.c b/contrib/libs/curl/lib/vauth/cleartext.c index c651fc5145..b82b171467 100644 --- a/contrib/libs/curl/lib/vauth/cleartext.c +++ b/contrib/libs/curl/lib/vauth/cleartext.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -28,8 +28,7 @@ #include "curl_setup.h" #if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \ - !defined(CURL_DISABLE_POP3) || \ - (!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)) + !defined(CURL_DISABLE_POP3) #include <curl/curl.h> #include "urldata.h" diff --git a/contrib/libs/curl/lib/vauth/cram.c b/contrib/libs/curl/lib/vauth/cram.c index 5894ed4bcf..475d31b8d7 100644 --- a/contrib/libs/curl/lib/vauth/cram.c +++ b/contrib/libs/curl/lib/vauth/cram.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/vauth/digest.c b/contrib/libs/curl/lib/vauth/digest.c index fda2d911f7..f945e8b6c9 100644 --- a/contrib/libs/curl/lib/vauth/digest.c +++ b/contrib/libs/curl/lib/vauth/digest.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -142,7 +142,7 @@ bool Curl_auth_digest_get_pair(const char *str, char *value, char *content, } #if !defined(USE_WINDOWS_SSPI) -/* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string */ +/* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/ static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */ unsigned char *dest) /* 33 bytes */ { @@ -151,7 +151,7 @@ static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */ msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]); } -/* Convert sha256 chunk to RFC7616 -suitable ascii string */ +/* Convert sha256 chunk to RFC7616 -suitable ascii string*/ static void auth_digest_sha256_to_ascii(unsigned char *source, /* 32 bytes */ unsigned char *dest) /* 65 bytes */ { @@ -186,7 +186,7 @@ static char *auth_digest_string_quoted(const char *source) } *d++ = *s++; } - *d = '\0'; + *d = 0; } return dest; @@ -490,7 +490,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, /* * Curl_auth_decode_digest_http_message() * - * This is used to decode an HTTP DIGEST challenge message into the separate + * This is used to decode a HTTP DIGEST challenge message into the separate * attributes. * * Parameters: @@ -650,7 +650,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, /* * auth_create_digest_http_message() * - * This is used to generate an HTTP DIGEST response message ready for sending + * This is used to generate a HTTP DIGEST response message ready for sending * to the recipient. * * Parameters: @@ -694,7 +694,6 @@ static CURLcode auth_create_digest_http_message( char *hashthis = NULL; char *tmp = NULL; - memset(hashbuf, 0, sizeof(hashbuf)); if(!digest->nc) digest->nc = 1; @@ -927,7 +926,7 @@ static CURLcode auth_create_digest_http_message( /* * Curl_auth_create_digest_http_message() * - * This is used to generate an HTTP DIGEST response message ready for sending + * This is used to generate a HTTP DIGEST response message ready for sending * to the recipient. * * Parameters: diff --git a/contrib/libs/curl/lib/vauth/digest.h b/contrib/libs/curl/lib/vauth/digest.h index 68fdb28c47..d785bdd91b 100644 --- a/contrib/libs/curl/lib/vauth/digest.h +++ b/contrib/libs/curl/lib/vauth/digest.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/vauth/digest_sspi.c b/contrib/libs/curl/lib/vauth/digest_sspi.c index 8fb8669393..89a9db52c7 100644 --- a/contrib/libs/curl/lib/vauth/digest_sspi.c +++ b/contrib/libs/curl/lib/vauth/digest_sspi.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Steve Holme, <steve_holme@hotmail.com>. - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2014 - 2016, Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) 2015 - 2022, 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 @@ -307,7 +307,7 @@ CURLcode Curl_override_sspi_http_realm(const char *chlg, /* * Curl_auth_decode_digest_http_message() * - * This is used to decode an HTTP DIGEST challenge message into the separate + * This is used to decode a HTTP DIGEST challenge message into the separate * attributes. * * Parameters: @@ -371,7 +371,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, /* * Curl_auth_create_digest_http_message() * - * This is used to generate an HTTP DIGEST response message ready for sending + * This is used to generate a HTTP DIGEST response message ready for sending * to the recipient. * * Parameters: diff --git a/contrib/libs/curl/lib/vauth/gsasl.c b/contrib/libs/curl/lib/vauth/gsasl.c index df18bd2b7d..f96a6dca29 100644 --- a/contrib/libs/curl/lib/vauth/gsasl.c +++ b/contrib/libs/curl/lib/vauth/gsasl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Simon Josefsson, <simon@josefsson.org>, et al. + * Copyright (C) 2020 - 2022, Simon Josefsson, <simon@josefsson.org>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/contrib/libs/curl/lib/vauth/krb5_gssapi.c b/contrib/libs/curl/lib/vauth/krb5_gssapi.c index b9c3289b82..73b9863b33 100644 --- a/contrib/libs/curl/lib/vauth/krb5_gssapi.c +++ b/contrib/libs/curl/lib/vauth/krb5_gssapi.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Steve Holme, <steve_holme@hotmail.com>. - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2014 - 2019, Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) 2015 - 2022, 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 diff --git a/contrib/libs/curl/lib/vauth/krb5_sspi.c b/contrib/libs/curl/lib/vauth/krb5_sspi.c index c487149b9d..895b4a1937 100644 --- a/contrib/libs/curl/lib/vauth/krb5_sspi.c +++ b/contrib/libs/curl/lib/vauth/krb5_sspi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) 2014 - 2022, Steve Holme, <steve_holme@hotmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -471,4 +471,4 @@ void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5) krb5->token_max = 0; } -#endif /* USE_WINDOWS_SSPI && USE_KERBEROS5 */ +#endif /* USE_WINDOWS_SSPI && USE_KERBEROS5*/ diff --git a/contrib/libs/curl/lib/vauth/ntlm.c b/contrib/libs/curl/lib/vauth/ntlm.c index 93096ba5ec..c10fa6caaf 100644 --- a/contrib/libs/curl/lib/vauth/ntlm.c +++ b/contrib/libs/curl/lib/vauth/ntlm.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -88,6 +88,8 @@ static void ntlm_print_flags(FILE *handle, unsigned long flags) fprintf(handle, "NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE "); if(flags & NTLMFLAG_NEGOTIATE_LM_KEY) fprintf(handle, "NTLMFLAG_NEGOTIATE_LM_KEY "); + if(flags & NTLMFLAG_NEGOTIATE_NETWARE) + fprintf(handle, "NTLMFLAG_NEGOTIATE_NETWARE "); if(flags & NTLMFLAG_NEGOTIATE_NTLM_KEY) fprintf(handle, "NTLMFLAG_NEGOTIATE_NTLM_KEY "); if(flags & (1<<10)) @@ -380,8 +382,8 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, (void)data; (void)userp; (void)passwdp; - (void)service; - (void)hostname; + (void)service, + (void)hostname, /* Clean up any former leftovers and initialise to defaults */ Curl_auth_cleanup_ntlm(ntlm); @@ -511,8 +513,6 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, size_t userlen = 0; size_t domlen = 0; - memset(lmresp, 0, sizeof(lmresp)); - memset(ntresp, 0, sizeof(ntresp)); user = strchr(userp, '\\'); if(!user) user = strchr(userp, '/'); diff --git a/contrib/libs/curl/lib/vauth/ntlm.h b/contrib/libs/curl/lib/vauth/ntlm.h index 31ce921cd1..4dfda55453 100644 --- a/contrib/libs/curl/lib/vauth/ntlm.h +++ b/contrib/libs/curl/lib/vauth/ntlm.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -64,6 +64,9 @@ /* Indicates that the LAN Manager session key should be used for signing and sealing authenticated communications. */ +#define NTLMFLAG_NEGOTIATE_NETWARE (1<<8) +/* unknown purpose */ + #define NTLMFLAG_NEGOTIATE_NTLM_KEY (1<<9) /* Indicates that NTLM authentication is being used. */ diff --git a/contrib/libs/curl/lib/vauth/ntlm_sspi.c b/contrib/libs/curl/lib/vauth/ntlm_sspi.c index 5118963f4d..193576acaa 100644 --- a/contrib/libs/curl/lib/vauth/ntlm_sspi.c +++ b/contrib/libs/curl/lib/vauth/ntlm_sspi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/vauth/oauth2.c b/contrib/libs/curl/lib/vauth/oauth2.c index a4adbdcf15..1604b303e0 100644 --- a/contrib/libs/curl/lib/vauth/oauth2.c +++ b/contrib/libs/curl/lib/vauth/oauth2.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -27,8 +27,7 @@ #include "curl_setup.h" #if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \ - !defined(CURL_DISABLE_POP3) || \ - (!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)) + !defined(CURL_DISABLE_POP3) #include <curl/curl.h> #include "urldata.h" diff --git a/contrib/libs/curl/lib/vauth/spnego_gssapi.c b/contrib/libs/curl/lib/vauth/spnego_gssapi.c index 59cc2ae422..298c2bb9b0 100644 --- a/contrib/libs/curl/lib/vauth/spnego_gssapi.c +++ b/contrib/libs/curl/lib/vauth/spnego_gssapi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/vauth/spnego_sspi.c b/contrib/libs/curl/lib/vauth/spnego_sspi.c index d3245d0b18..d845caca6c 100644 --- a/contrib/libs/curl/lib/vauth/spnego_sspi.c +++ b/contrib/libs/curl/lib/vauth/spnego_sspi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/vauth/vauth.c b/contrib/libs/curl/lib/vauth/vauth.c index 62fc7c40fe..58fe05139d 100644 --- a/contrib/libs/curl/lib/vauth/vauth.c +++ b/contrib/libs/curl/lib/vauth/vauth.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) 2014 - 2022, Steve Holme, <steve_holme@hotmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/contrib/libs/curl/lib/vauth/vauth.h b/contrib/libs/curl/lib/vauth/vauth.h index d8cff24381..af27f01dfb 100644 --- a/contrib/libs/curl/lib/vauth/vauth.h +++ b/contrib/libs/curl/lib/vauth/vauth.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) 2014 - 2022, Steve Holme, <steve_holme@hotmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -104,11 +104,11 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, const char *service, struct bufref *out); -/* This is used to decode an HTTP DIGEST challenge message */ +/* This is used to decode a HTTP DIGEST challenge message */ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, struct digestdata *digest); -/* This is used to generate an HTTP DIGEST response message */ +/* This is used to generate a HTTP DIGEST response message */ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, const char *userp, const char *passwdp, @@ -219,7 +219,7 @@ bool Curl_auth_is_spnego_supported(void); message */ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, const char *user, - const char *password, + const char *passwood, const char *service, const char *host, const char *chlg64, diff --git a/contrib/libs/curl/lib/version.c b/contrib/libs/curl/lib/version.c index 10e639bad1..8d7be114f4 100644 --- a/contrib/libs/curl/lib/version.c +++ b/contrib/libs/curl/lib/version.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,16 +24,12 @@ #include "curl_setup.h" -#ifdef USE_NGHTTP2 -#include <nghttp2/nghttp2.h> -#endif - #include <curl/curl.h> #include "urldata.h" #include "vtls/vtls.h" #include "http2.h" #include "vssh/ssh.h" -#include "vquic/vquic.h" +#include "quic.h" #include "curl_printf.h" #include "easy_lock.h" @@ -62,15 +58,7 @@ #endif #ifdef HAVE_BROTLI -#if defined(__GNUC__) -/* Ignore -Wvla warnings in brotli headers */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wvla" -#endif #error #include <brotli/decode.h> -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif #endif #ifdef HAVE_ZSTD @@ -300,7 +288,7 @@ char *curl_version(void) protocol line has its own #if line to make things easier on the eye. */ -static const char * const supported_protocols[] = { +static const char * const protocols[] = { #ifndef CURL_DISABLE_DICT "dict", #endif @@ -365,7 +353,8 @@ static const char * const supported_protocols[] = { #ifdef USE_SSH "sftp", #endif -#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) +#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \ + (SIZEOF_CURL_OFF_T > 4) "smb", # ifdef USE_SSL "smbs", @@ -393,149 +382,97 @@ static const char * const supported_protocols[] = { NULL }; -/* - * Feature presence run-time check functions. - * - * Warning: the value returned by these should not change between - * curl_global_init() and curl_global_cleanup() calls. - */ - -#if defined(USE_LIBIDN2) -static int idn_present(curl_version_info_data *info) -{ - return info->libidn != NULL; -} -#else -#define idn_present NULL -#endif - -#if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY) -static int https_proxy_present(curl_version_info_data *info) -{ - (void) info; - return Curl_ssl_supports(NULL, SSLSUPP_HTTPS_PROXY); -} +static curl_version_info_data version_info = { + CURLVERSION_NOW, + LIBCURL_VERSION, + LIBCURL_VERSION_NUM, + OS, /* as found by configure or set by hand at build-time */ + 0 /* features is 0 by default */ +#ifdef ENABLE_IPV6 + | CURL_VERSION_IPV6 #endif - -/* - * Features table. - * - * Keep the features alphabetically sorted. - * Use FEATURE() macro to define an entry: this allows documentation check. - */ - -#define FEATURE(name, present, bitmask) {(name), (present), (bitmask)} - -struct feat { - const char *name; - int (*present)(curl_version_info_data *info); - int bitmask; -}; - -static const struct feat features_table[] = { -#ifndef CURL_DISABLE_ALTSVC - FEATURE("alt-svc", NULL, CURL_VERSION_ALTSVC), +#ifdef USE_SSL + | CURL_VERSION_SSL #endif -#ifdef CURLRES_ASYNCH - FEATURE("AsynchDNS", NULL, CURL_VERSION_ASYNCHDNS), +#ifdef USE_NTLM + | CURL_VERSION_NTLM #endif -#ifdef HAVE_BROTLI - FEATURE("brotli", NULL, CURL_VERSION_BROTLI), +#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \ + defined(NTLM_WB_ENABLED) + | CURL_VERSION_NTLM_WB #endif -#ifdef DEBUGBUILD - FEATURE("Debug", NULL, CURL_VERSION_DEBUG), +#ifdef USE_SPNEGO + | CURL_VERSION_SPNEGO #endif -#ifdef USE_GSASL - FEATURE("gsasl", NULL, CURL_VERSION_GSASL), +#ifdef USE_KERBEROS5 + | CURL_VERSION_KERBEROS5 #endif #ifdef HAVE_GSSAPI - FEATURE("GSS-API", NULL, CURL_VERSION_GSSAPI), -#endif -#ifndef CURL_DISABLE_HSTS - FEATURE("HSTS", NULL, CURL_VERSION_HSTS), -#endif -#if defined(USE_NGHTTP2) || defined(USE_HYPER) - FEATURE("HTTP2", NULL, CURL_VERSION_HTTP2), + | CURL_VERSION_GSSAPI #endif -#if defined(ENABLE_QUIC) - FEATURE("HTTP3", NULL, CURL_VERSION_HTTP3), +#ifdef USE_WINDOWS_SSPI + | CURL_VERSION_SSPI #endif -#if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY) - FEATURE("HTTPS-proxy", https_proxy_present, CURL_VERSION_HTTPS_PROXY), +#ifdef HAVE_LIBZ + | CURL_VERSION_LIBZ #endif -#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) - FEATURE("IDN", idn_present, CURL_VERSION_IDN), +#ifdef DEBUGBUILD + | CURL_VERSION_DEBUG #endif -#ifdef ENABLE_IPV6 - FEATURE("IPv6", NULL, CURL_VERSION_IPV6), +#ifdef CURLDEBUG + | CURL_VERSION_CURLDEBUG #endif -#ifdef USE_KERBEROS5 - FEATURE("Kerberos", NULL, CURL_VERSION_KERBEROS5), +#ifdef CURLRES_ASYNCH + | CURL_VERSION_ASYNCHDNS #endif #if (SIZEOF_CURL_OFF_T > 4) && \ ( (SIZEOF_OFF_T > 4) || defined(USE_WIN32_LARGE_FILES) ) - FEATURE("Largefile", NULL, CURL_VERSION_LARGEFILE), + | CURL_VERSION_LARGEFILE #endif -#ifdef HAVE_LIBZ - FEATURE("libz", NULL, CURL_VERSION_LIBZ), -#endif -#ifdef CURL_WITH_MULTI_SSL - FEATURE("MultiSSL", NULL, CURL_VERSION_MULTI_SSL), +#if defined(WIN32) && defined(UNICODE) && defined(_UNICODE) + | CURL_VERSION_UNICODE #endif -#ifdef USE_NTLM - FEATURE("NTLM", NULL, CURL_VERSION_NTLM), +#if defined(USE_TLS_SRP) + | CURL_VERSION_TLSAUTH_SRP #endif -#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \ - defined(NTLM_WB_ENABLED) - FEATURE("NTLM_WB", NULL, CURL_VERSION_NTLM_WB), +#if defined(USE_NGHTTP2) || defined(USE_HYPER) + | CURL_VERSION_HTTP2 #endif -#if defined(USE_LIBPSL) - FEATURE("PSL", NULL, CURL_VERSION_PSL), +#if defined(ENABLE_QUIC) + | CURL_VERSION_HTTP3 #endif -#ifdef USE_SPNEGO - FEATURE("SPNEGO", NULL, CURL_VERSION_SPNEGO), +#if defined(USE_UNIX_SOCKETS) + | CURL_VERSION_UNIX_SOCKETS #endif -#ifdef USE_SSL - FEATURE("SSL", NULL, CURL_VERSION_SSL), +#if defined(USE_LIBPSL) + | CURL_VERSION_PSL #endif -#ifdef USE_WINDOWS_SSPI - FEATURE("SSPI", NULL, CURL_VERSION_SSPI), +#if defined(CURL_WITH_MULTI_SSL) + | CURL_VERSION_MULTI_SSL #endif -#ifdef GLOBAL_INIT_IS_THREADSAFE - FEATURE("threadsafe", NULL, CURL_VERSION_THREADSAFE), +#if defined(HAVE_BROTLI) + | CURL_VERSION_BROTLI #endif -#ifdef USE_TLS_SRP - FEATURE("TLS-SRP", NULL, CURL_VERSION_TLSAUTH_SRP), +#if defined(HAVE_ZSTD) + | CURL_VERSION_ZSTD #endif -#ifdef CURLDEBUG - FEATURE("TrackMemory", NULL, CURL_VERSION_CURLDEBUG), +#ifndef CURL_DISABLE_ALTSVC + | CURL_VERSION_ALTSVC #endif -#if defined(WIN32) && defined(UNICODE) && defined(_UNICODE) - FEATURE("Unicode", NULL, CURL_VERSION_UNICODE), +#ifndef CURL_DISABLE_HSTS + | CURL_VERSION_HSTS #endif -#ifdef USE_UNIX_SOCKETS - FEATURE("UnixSockets", NULL, CURL_VERSION_UNIX_SOCKETS), +#if defined(USE_GSASL) + | CURL_VERSION_GSASL #endif -#ifdef HAVE_ZSTD - FEATURE("zstd", NULL, CURL_VERSION_ZSTD), +#if defined(GLOBAL_INIT_IS_THREADSAFE) + | CURL_VERSION_THREADSAFE #endif - {NULL, NULL, 0} -}; - -static const char *feature_names[sizeof(features_table) / - sizeof(features_table[0])] = {NULL}; - - -static curl_version_info_data version_info = { - CURLVERSION_NOW, - LIBCURL_VERSION, - LIBCURL_VERSION_NUM, - OS, /* as found by configure or set by hand at build-time */ - 0, /* features bitmask is built at run-time */ + , NULL, /* ssl_version */ 0, /* ssl_version_num, this is kept at zero */ NULL, /* zlib_version */ - supported_protocols, + protocols, NULL, /* c-ares version */ 0, /* c-ares version numerical */ NULL, /* libidn version */ @@ -559,16 +496,11 @@ static curl_version_info_data version_info = { 0, /* zstd_ver_num */ NULL, /* zstd version */ NULL, /* Hyper version */ - NULL, /* gsasl version */ - feature_names + NULL /* gsasl version */ }; curl_version_info_data *curl_version_info(CURLversion stamp) { - size_t n; - const struct feat *p; - int features = 0; - #if defined(USE_SSH) static char ssh_buffer[80]; #endif @@ -586,11 +518,15 @@ curl_version_info_data *curl_version_info(CURLversion stamp) static char zstd_buffer[80]; #endif - (void)stamp; /* avoid compiler warnings, we don't use this */ - #ifdef USE_SSL Curl_ssl_version(ssl_buffer, sizeof(ssl_buffer)); version_info.ssl_version = ssl_buffer; +#ifndef CURL_DISABLE_PROXY + if(Curl_ssl->supports & SSLSUPP_HTTPS_PROXY) + version_info.features |= CURL_VERSION_HTTPS_PROXY; + else + version_info.features &= ~CURL_VERSION_HTTPS_PROXY; +#endif #endif #ifdef HAVE_LIBZ @@ -608,6 +544,10 @@ curl_version_info_data *curl_version_info(CURLversion stamp) /* This returns a version string if we use the given version or later, otherwise it returns NULL */ version_info.libidn = idn2_check_version(IDN2_VERSION); + if(version_info.libidn) + version_info.features |= CURL_VERSION_IDN; +#elif defined(USE_WIN32_IDN) + version_info.features |= CURL_VERSION_IDN; #endif #if defined(USE_SSH) @@ -657,16 +597,6 @@ curl_version_info_data *curl_version_info(CURLversion stamp) } #endif - /* Get available features, build bitmask and names array. */ - n = 0; - for(p = features_table; p->name; p++) - if(!p->present || p->present(&version_info)) { - features |= p->bitmask; - feature_names[n++] = p->name; - } - - feature_names[n] = NULL; /* Terminate array. */ - version_info.features = features; - + (void)stamp; /* avoid compiler warnings, we don't use this */ return &version_info; } diff --git a/contrib/libs/curl/lib/version_win32.c b/contrib/libs/curl/lib/version_win32.c index 872d5b4f3c..e8f14f9dff 100644 --- a/contrib/libs/curl/lib/version_win32.c +++ b/contrib/libs/curl/lib/version_win32.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/contrib/libs/curl/lib/version_win32.h b/contrib/libs/curl/lib/version_win32.h index 3899174a31..7a9a6a14f1 100644 --- a/contrib/libs/curl/lib/version_win32.h +++ b/contrib/libs/curl/lib/version_win32.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/contrib/libs/curl/lib/vquic/curl_msh3.c b/contrib/libs/curl/lib/vquic/curl_msh3.c deleted file mode 100644 index 02b5334f28..0000000000 --- a/contrib/libs/curl/lib/vquic/curl_msh3.c +++ /dev/null @@ -1,1087 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifdef USE_MSH3 - -#include "urldata.h" -#include "timeval.h" -#include "multiif.h" -#include "sendf.h" -#include "curl_log.h" -#include "cfilters.h" -#include "cf-socket.h" -#include "connect.h" -#include "progress.h" -#include "http1.h" -#include "curl_msh3.h" -#include "socketpair.h" -#include "vquic/vquic.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#define H3_STREAM_WINDOW_SIZE (128 * 1024) -#define H3_STREAM_CHUNK_SIZE (16 * 1024) -#define H3_STREAM_RECV_CHUNKS \ - (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) - -#ifdef _WIN32 -#define msh3_lock CRITICAL_SECTION -#define msh3_lock_initialize(lock) InitializeCriticalSection(lock) -#define msh3_lock_uninitialize(lock) DeleteCriticalSection(lock) -#define msh3_lock_acquire(lock) EnterCriticalSection(lock) -#define msh3_lock_release(lock) LeaveCriticalSection(lock) -#else /* !_WIN32 */ -#include <pthread.h> -#define msh3_lock pthread_mutex_t -#define msh3_lock_initialize(lock) do { \ - pthread_mutexattr_t attr; \ - pthread_mutexattr_init(&attr); \ - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \ - pthread_mutex_init(lock, &attr); \ - pthread_mutexattr_destroy(&attr); \ -}while(0) -#define msh3_lock_uninitialize(lock) pthread_mutex_destroy(lock) -#define msh3_lock_acquire(lock) pthread_mutex_lock(lock) -#define msh3_lock_release(lock) pthread_mutex_unlock(lock) -#endif /* _WIN32 */ - - -static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection, - void *IfContext); -static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection, - void *IfContext); -static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection, - void *IfContext, - MSH3_REQUEST *Request); -static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request, - void *IfContext, - const MSH3_HEADER *Header); -static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request, - void *IfContext, uint32_t *Length, - const uint8_t *Data); -static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext, - bool Aborted, uint64_t AbortError); -static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request, - void *IfContext); -static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request, - void *IfContext, void *SendContext); - - -void Curl_msh3_ver(char *p, size_t len) -{ - uint32_t v[4]; - MsH3Version(v); - (void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]); -} - -#define SP_LOCAL 0 -#define SP_REMOTE 1 - -struct cf_msh3_ctx { - MSH3_API *api; - MSH3_CONNECTION *qconn; - struct Curl_sockaddr_ex addr; - curl_socket_t sock[2]; /* fake socket pair until we get support in msh3 */ - char l_ip[MAX_IPADR_LEN]; /* local IP as string */ - int l_port; /* local port number */ - struct cf_call_data call_data; - struct curltime connect_started; /* time the current attempt started */ - struct curltime handshake_at; /* time connect handshake finished */ - /* Flags written by msh3/msquic thread */ - bool handshake_complete; - bool handshake_succeeded; - bool connected; - /* Flags written by curl thread */ - BIT(verbose); - BIT(active); -}; - -/* How to access `call_data` from a cf_msh3 filter */ -#undef CF_CTX_CALL_DATA -#define CF_CTX_CALL_DATA(cf) \ - ((struct cf_msh3_ctx *)(cf)->ctx)->call_data - -/** - * All about the H3 internals of a stream - */ -struct stream_ctx { - struct MSH3_REQUEST *req; - struct bufq recvbuf; /* h3 response */ -#ifdef _WIN32 - CRITICAL_SECTION recv_lock; -#else /* !_WIN32 */ - pthread_mutex_t recv_lock; -#endif /* _WIN32 */ - uint64_t error3; /* HTTP/3 stream error code */ - int status_code; /* HTTP status code */ - CURLcode recv_error; - bool closed; - bool reset; - bool upload_done; - bool firstheader; /* FALSE until headers arrive */ - bool recv_header_complete; -}; - -#define H3_STREAM_CTX(d) ((struct stream_ctx *)(((d) && (d)->req.p.http)? \ - ((struct HTTP *)(d)->req.p.http)->h3_ctx \ - : NULL)) -#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx -#define H3_STREAM_ID(d) (H3_STREAM_CTX(d)? \ - H3_STREAM_CTX(d)->id : -2) - - -static CURLcode h3_data_setup(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct stream_ctx *stream = H3_STREAM_CTX(data); - - if(stream) - return CURLE_OK; - - stream = calloc(1, sizeof(*stream)); - if(!stream) - return CURLE_OUT_OF_MEMORY; - - H3_STREAM_LCTX(data) = stream; - stream->req = ZERO_NULL; - msh3_lock_initialize(&stream->recv_lock); - Curl_bufq_init2(&stream->recvbuf, H3_STREAM_CHUNK_SIZE, - H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); - DEBUGF(LOG_CF(data, cf, "data setup")); - return CURLE_OK; -} - -static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct stream_ctx *stream = H3_STREAM_CTX(data); - - (void)cf; - if(stream) { - DEBUGF(LOG_CF(data, cf, "easy handle is done")); - Curl_bufq_free(&stream->recvbuf); - free(stream); - H3_STREAM_LCTX(data) = NULL; - } -} - -static void drain_stream_from_other_thread(struct Curl_easy *data, - struct stream_ctx *stream) -{ - unsigned char bits; - - /* risky */ - bits = CURL_CSELECT_IN; - if(stream && !stream->upload_done) - bits |= CURL_CSELECT_OUT; - if(data->state.dselect_bits != bits) { - data->state.dselect_bits = bits; - /* cannot expire from other thread */ - } -} - -static void drain_stream(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct stream_ctx *stream = H3_STREAM_CTX(data); - unsigned char bits; - - (void)cf; - bits = CURL_CSELECT_IN; - if(stream && !stream->upload_done) - bits |= CURL_CSELECT_OUT; - if(data->state.dselect_bits != bits) { - data->state.dselect_bits = bits; - Curl_expire(data, 0, EXPIRE_RUN_NOW); - } -} - -static const MSH3_CONNECTION_IF msh3_conn_if = { - msh3_conn_connected, - msh3_conn_shutdown_complete, - msh3_conn_new_request -}; - -static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection, - void *IfContext) -{ - struct Curl_cfilter *cf = IfContext; - struct cf_msh3_ctx *ctx = cf->ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - (void)Connection; - - DEBUGF(LOG_CF(data, cf, "[MSH3] connected")); - ctx->handshake_succeeded = true; - ctx->connected = true; - ctx->handshake_complete = true; -} - -static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection, - void *IfContext) -{ - struct Curl_cfilter *cf = IfContext; - struct cf_msh3_ctx *ctx = cf->ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - - (void)Connection; - DEBUGF(LOG_CF(data, cf, "[MSH3] shutdown complete")); - ctx->connected = false; - ctx->handshake_complete = true; -} - -static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection, - void *IfContext, - MSH3_REQUEST *Request) -{ - (void)Connection; - (void)IfContext; - (void)Request; -} - -static const MSH3_REQUEST_IF msh3_request_if = { - msh3_header_received, - msh3_data_received, - msh3_complete, - msh3_shutdown_complete, - msh3_data_sent -}; - -/* Decode HTTP status code. Returns -1 if no valid status code was - decoded. (duplicate from http2.c) */ -static int decode_status_code(const char *value, size_t len) -{ - int i; - int res; - - if(len != 3) { - return -1; - } - - res = 0; - - for(i = 0; i < 3; ++i) { - char c = value[i]; - - if(c < '0' || c > '9') { - return -1; - } - - res *= 10; - res += c - '0'; - } - - return res; -} - -/* - * write_resp_raw() copies response data in raw format to the `data`'s - * receive buffer. If not enough space is available, it appends to the - * `data`'s overflow buffer. - */ -static CURLcode write_resp_raw(struct Curl_easy *data, - const void *mem, size_t memlen) -{ - struct stream_ctx *stream = H3_STREAM_CTX(data); - CURLcode result = CURLE_OK; - ssize_t nwritten; - - if(!stream) - return CURLE_RECV_ERROR; - - nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result); - if(nwritten < 0) { - return result; - } - - if((size_t)nwritten < memlen) { - /* This MUST not happen. Our recbuf is dimensioned to hold the - * full max_stream_window and then some for this very reason. */ - DEBUGASSERT(0); - return CURLE_RECV_ERROR; - } - return result; -} - -static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request, - void *userp, - const MSH3_HEADER *hd) -{ - struct Curl_easy *data = userp; - struct stream_ctx *stream = H3_STREAM_CTX(data); - CURLcode result; - (void)Request; - - if(!stream || stream->recv_header_complete) { - return; - } - - msh3_lock_acquire(&stream->recv_lock); - - if((hd->NameLength == 7) && - !strncmp(HTTP_PSEUDO_STATUS, (char *)hd->Name, 7)) { - char line[14]; /* status line is always 13 characters long */ - size_t ncopy; - - DEBUGASSERT(!stream->firstheader); - stream->status_code = decode_status_code(hd->Value, hd->ValueLength); - DEBUGASSERT(stream->status_code != -1); - ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n", - stream->status_code); - result = write_resp_raw(data, line, ncopy); - if(result) - stream->recv_error = result; - stream->firstheader = TRUE; - } - else { - /* store as an HTTP1-style header */ - DEBUGASSERT(stream->firstheader); - result = write_resp_raw(data, hd->Name, hd->NameLength); - if(!result) - result = write_resp_raw(data, ": ", 2); - if(!result) - result = write_resp_raw(data, hd->Value, hd->ValueLength); - if(!result) - result = write_resp_raw(data, "\r\n", 2); - if(result) { - stream->recv_error = result; - } - } - - drain_stream_from_other_thread(data, stream); - msh3_lock_release(&stream->recv_lock); -} - -static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request, - void *IfContext, uint32_t *buflen, - const uint8_t *buf) -{ - struct Curl_easy *data = IfContext; - struct stream_ctx *stream = H3_STREAM_CTX(data); - CURLcode result; - bool rv = FALSE; - - /* TODO: we would like to limit the amount of data we are buffer here. - * There seems to be no mechanism in msh3 to adjust flow control and - * it is undocumented what happens if we return FALSE here or less - * length (buflen is an inout parameter). - */ - (void)Request; - if(!stream) - return FALSE; - - msh3_lock_acquire(&stream->recv_lock); - - if(!stream->recv_header_complete) { - result = write_resp_raw(data, "\r\n", 2); - if(result) { - stream->recv_error = result; - goto out; - } - stream->recv_header_complete = true; - } - - result = write_resp_raw(data, buf, *buflen); - if(result) { - stream->recv_error = result; - } - rv = TRUE; - -out: - msh3_lock_release(&stream->recv_lock); - return rv; -} - -static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext, - bool aborted, uint64_t error) -{ - struct Curl_easy *data = IfContext; - struct stream_ctx *stream = H3_STREAM_CTX(data); - - (void)Request; - if(!stream) - return; - msh3_lock_acquire(&stream->recv_lock); - stream->closed = TRUE; - stream->recv_header_complete = true; - if(error) - stream->error3 = error; - if(aborted) - stream->reset = TRUE; - msh3_lock_release(&stream->recv_lock); -} - -static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request, - void *IfContext) -{ - struct Curl_easy *data = IfContext; - struct stream_ctx *stream = H3_STREAM_CTX(data); - - if(!stream) - return; - (void)Request; - (void)stream; -} - -static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request, - void *IfContext, void *SendContext) -{ - struct Curl_easy *data = IfContext; - struct stream_ctx *stream = H3_STREAM_CTX(data); - if(!stream) - return; - (void)Request; - (void)stream; - (void)SendContext; -} - -static ssize_t recv_closed_stream(struct Curl_cfilter *cf, - struct Curl_easy *data, - CURLcode *err) -{ - struct stream_ctx *stream = H3_STREAM_CTX(data); - ssize_t nread = -1; - - if(!stream) { - *err = CURLE_RECV_ERROR; - return -1; - } - (void)cf; - if(stream->reset) { - failf(data, "HTTP/3 stream reset by server"); - *err = CURLE_PARTIAL_FILE; - DEBUGF(LOG_CF(data, cf, "cf_recv, was reset -> %d", *err)); - goto out; - } - else if(stream->error3) { - failf(data, "HTTP/3 stream was not closed cleanly: (error %zd)", - (ssize_t)stream->error3); - *err = CURLE_HTTP3; - DEBUGF(LOG_CF(data, cf, "cf_recv, closed uncleanly -> %d", *err)); - goto out; - } - else { - DEBUGF(LOG_CF(data, cf, "cf_recv, closed ok -> %d", *err)); - } - *err = CURLE_OK; - nread = 0; - -out: - return nread; -} - -static void set_quic_expire(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct stream_ctx *stream = H3_STREAM_CTX(data); - - /* we have no indication from msh3 when it would be a good time - * to juggle the connection again. So, we compromise by calling - * us again every some milliseconds. */ - (void)cf; - if(stream && stream->req && !stream->closed) { - Curl_expire(data, 10, EXPIRE_QUIC); - } - else { - Curl_expire(data, 50, EXPIRE_QUIC); - } -} - -static ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) -{ - struct stream_ctx *stream = H3_STREAM_CTX(data); - ssize_t nread = -1; - struct cf_call_data save; - - (void)cf; - if(!stream) { - *err = CURLE_RECV_ERROR; - return -1; - } - CF_DATA_SAVE(save, cf, data); - DEBUGF(LOG_CF(data, cf, "req: recv with %zu byte buffer", len)); - - msh3_lock_acquire(&stream->recv_lock); - - if(stream->recv_error) { - failf(data, "request aborted"); - *err = stream->recv_error; - goto out; - } - - *err = CURLE_OK; - - if(!Curl_bufq_is_empty(&stream->recvbuf)) { - nread = Curl_bufq_read(&stream->recvbuf, - (unsigned char *)buf, len, err); - DEBUGF(LOG_CF(data, cf, "read recvbuf(len=%zu) -> %zd, %d", - len, nread, *err)); - if(nread < 0) - goto out; - if(stream->closed) - drain_stream(cf, data); - } - else if(stream->closed) { - nread = recv_closed_stream(cf, data, err); - goto out; - } - else { - DEBUGF(LOG_CF(data, cf, "req: nothing here, call again")); - *err = CURLE_AGAIN; - } - -out: - msh3_lock_release(&stream->recv_lock); - set_quic_expire(cf, data); - CF_DATA_RESTORE(cf, save); - return nread; -} - -static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) -{ - struct cf_msh3_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H3_STREAM_CTX(data); - struct h1_req_parser h1; - struct dynhds h2_headers; - MSH3_HEADER *nva = NULL; - size_t nheader, i; - ssize_t nwritten = -1; - struct cf_call_data save; - bool eos; - - CF_DATA_SAVE(save, cf, data); - - Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); - Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); - - /* Sizes must match for cast below to work" */ - DEBUGASSERT(stream); - DEBUGF(LOG_CF(data, cf, "req: send %zu bytes", len)); - - if(!stream->req) { - /* The first send on the request contains the headers and possibly some - data. Parse out the headers and create the request, then if there is - any data left over go ahead and send it too. */ - nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err); - if(nwritten < 0) - goto out; - DEBUGASSERT(h1.done); - DEBUGASSERT(h1.req); - - *err = Curl_http_req_to_h2(&h2_headers, h1.req, data); - if(*err) { - nwritten = -1; - goto out; - } - - nheader = Curl_dynhds_count(&h2_headers); - nva = malloc(sizeof(MSH3_HEADER) * nheader); - if(!nva) { - *err = CURLE_OUT_OF_MEMORY; - nwritten = -1; - goto out; - } - - for(i = 0; i < nheader; ++i) { - struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); - nva[i].Name = e->name; - nva[i].NameLength = e->namelen; - nva[i].Value = e->value; - nva[i].ValueLength = e->valuelen; - } - - switch(data->state.httpreq) { - case HTTPREQ_POST: - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - case HTTPREQ_PUT: - /* known request body size or -1 */ - eos = FALSE; - break; - default: - /* there is not request body */ - eos = TRUE; - stream->upload_done = TRUE; - break; - } - - DEBUGF(LOG_CF(data, cf, "req: send %zu headers", nheader)); - stream->req = MsH3RequestOpen(ctx->qconn, &msh3_request_if, data, - nva, nheader, - eos ? MSH3_REQUEST_FLAG_FIN : - MSH3_REQUEST_FLAG_NONE); - if(!stream->req) { - failf(data, "request open failed"); - *err = CURLE_SEND_ERROR; - goto out; - } - *err = CURLE_OK; - nwritten = len; - goto out; - } - else { - /* request is open */ - DEBUGF(LOG_CF(data, cf, "req: send %zu body bytes", len)); - if(len > 0xFFFFFFFF) { - len = 0xFFFFFFFF; - } - - if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_NONE, buf, - (uint32_t)len, stream)) { - *err = CURLE_SEND_ERROR; - goto out; - } - - /* TODO - msh3/msquic will hold onto this memory until the send complete - event. How do we make sure curl doesn't free it until then? */ - *err = CURLE_OK; - nwritten = len; - } - -out: - set_quic_expire(cf, data); - free(nva); - Curl_h1_req_parse_free(&h1); - Curl_dynhds_free(&h2_headers); - CF_DATA_RESTORE(cf, save); - return nwritten; -} - -static int cf_msh3_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) -{ - struct cf_msh3_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H3_STREAM_CTX(data); - int bitmap = GETSOCK_BLANK; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - if(stream && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) { - socks[0] = ctx->sock[SP_LOCAL]; - - if(stream->recv_error) { - bitmap |= GETSOCK_READSOCK(0); - drain_stream(cf, data); - } - else if(stream->req) { - bitmap |= GETSOCK_READSOCK(0); - drain_stream(cf, data); - } - } - DEBUGF(LOG_CF(data, cf, "select_sock -> %d", bitmap)); - CF_DATA_RESTORE(cf, save); - return bitmap; -} - -static bool cf_msh3_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data) -{ - struct stream_ctx *stream = H3_STREAM_CTX(data); - struct cf_call_data save; - bool pending = FALSE; - - CF_DATA_SAVE(save, cf, data); - - (void)cf; - if(stream && stream->req) { - msh3_lock_acquire(&stream->recv_lock); - DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data pending = %zu", - Curl_bufq_len(&stream->recvbuf))); - pending = !Curl_bufq_is_empty(&stream->recvbuf); - msh3_lock_release(&stream->recv_lock); - if(pending) - drain_stream(cf, (struct Curl_easy *)data); - } - - CF_DATA_RESTORE(cf, save); - return pending; -} - -static void cf_msh3_active(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_msh3_ctx *ctx = cf->ctx; - - /* use this socket from now on */ - cf->conn->sock[cf->sockindex] = ctx->sock[SP_LOCAL]; - /* the first socket info gets set at conn and data */ - if(cf->sockindex == FIRSTSOCKET) { - cf->conn->remote_addr = &ctx->addr; - #ifdef ENABLE_IPV6 - cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE; - #endif - Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port); - } - ctx->active = TRUE; -} - -static CURLcode h3_data_pause(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool pause) -{ - if(!pause) { - drain_stream(cf, data); - Curl_expire(data, 0, EXPIRE_RUN_NOW); - } - return CURLE_OK; -} - -static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf, - struct Curl_easy *data, - int event, int arg1, void *arg2) -{ - struct stream_ctx *stream = H3_STREAM_CTX(data); - struct cf_call_data save; - CURLcode result = CURLE_OK; - - CF_DATA_SAVE(save, cf, data); - - (void)arg1; - (void)arg2; - switch(event) { - case CF_CTRL_DATA_SETUP: - result = h3_data_setup(cf, data); - break; - case CF_CTRL_DATA_PAUSE: - result = h3_data_pause(cf, data, (arg1 != 0)); - break; - case CF_CTRL_DATA_DONE: - h3_data_done(cf, data); - break; - case CF_CTRL_DATA_DONE_SEND: - DEBUGF(LOG_CF(data, cf, "req: send done")); - if(stream) { - stream->upload_done = TRUE; - if(stream->req) { - char buf[1]; - if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_FIN, - buf, 0, data)) { - result = CURLE_SEND_ERROR; - } - } - } - break; - case CF_CTRL_CONN_INFO_UPDATE: - DEBUGF(LOG_CF(data, cf, "req: update info")); - cf_msh3_active(cf, data); - break; - default: - break; - } - - CF_DATA_RESTORE(cf, save); - return result; -} - -static CURLcode cf_connect_start(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_msh3_ctx *ctx = cf->ctx; - bool verify = !!cf->conn->ssl_config.verifypeer; - MSH3_ADDR addr = {0}; - CURLcode result; - - memcpy(&addr, &ctx->addr.sa_addr, ctx->addr.addrlen); - MSH3_SET_PORT(&addr, (uint16_t)cf->conn->remote_port); - - if(verify && (cf->conn->ssl_config.CAfile || cf->conn->ssl_config.CApath)) { - /* TODO: need a way to provide trust anchors to MSH3 */ -#ifdef DEBUGBUILD - /* we need this for our test cases to run */ - DEBUGF(LOG_CF(data, cf, "non-standard CA not supported, " - "switching off verifypeer in DEBUG mode")); - verify = 0; -#else - DEBUGF(LOG_CF(data, cf, "non-standard CA not supported, " - "attempting with built-in verification")); -#endif - } - - DEBUGF(LOG_CF(data, cf, "connecting to %s:%d (verify=%d)", - cf->conn->host.name, (int)cf->conn->remote_port, verify)); - - ctx->api = MsH3ApiOpen(); - if(!ctx->api) { - failf(data, "can't create msh3 api"); - return CURLE_FAILED_INIT; - } - - ctx->qconn = MsH3ConnectionOpen(ctx->api, - &msh3_conn_if, - cf, - cf->conn->host.name, - &addr, - !verify); - if(!ctx->qconn) { - failf(data, "can't create msh3 connection"); - if(ctx->api) { - MsH3ApiClose(ctx->api); - ctx->api = NULL; - } - return CURLE_FAILED_INIT; - } - - result = h3_data_setup(cf, data); - if(result) - return result; - - return CURLE_OK; -} - -static CURLcode cf_msh3_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking, bool *done) -{ - struct cf_msh3_ctx *ctx = cf->ctx; - struct cf_call_data save; - CURLcode result = CURLE_OK; - - (void)blocking; - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } - - CF_DATA_SAVE(save, cf, data); - - if(ctx->sock[SP_LOCAL] == CURL_SOCKET_BAD) { - if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &ctx->sock[0]) < 0) { - ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; - ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD; - return CURLE_COULDNT_CONNECT; - } - } - - *done = FALSE; - if(!ctx->qconn) { - ctx->connect_started = Curl_now(); - result = cf_connect_start(cf, data); - if(result) - goto out; - } - - if(ctx->handshake_complete) { - ctx->handshake_at = Curl_now(); - if(ctx->handshake_succeeded) { - DEBUGF(LOG_CF(data, cf, "handshake succeeded")); - cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - cf->conn->httpversion = 30; - cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; - cf->connected = TRUE; - cf->conn->alpn = CURL_HTTP_VERSION_3; - *done = TRUE; - connkeep(cf->conn, "HTTP/3 default"); - Curl_pgrsTime(data, TIMER_APPCONNECT); - } - else { - failf(data, "failed to connect, handshake failed"); - result = CURLE_COULDNT_CONNECT; - } - } - -out: - CF_DATA_RESTORE(cf, save); - return result; -} - -static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_msh3_ctx *ctx = cf->ctx; - struct cf_call_data save; - - (void)data; - CF_DATA_SAVE(save, cf, data); - - if(ctx) { - DEBUGF(LOG_CF(data, cf, "destroying")); - if(ctx->qconn) { - MsH3ConnectionClose(ctx->qconn); - ctx->qconn = NULL; - } - if(ctx->api) { - MsH3ApiClose(ctx->api); - ctx->api = NULL; - } - - if(ctx->active) { - /* We share our socket at cf->conn->sock[cf->sockindex] when active. - * If it is no longer there, someone has stolen (and hopefully - * closed it) and we just forget about it. - */ - ctx->active = FALSE; - if(ctx->sock[SP_LOCAL] == cf->conn->sock[cf->sockindex]) { - DEBUGF(LOG_CF(data, cf, "cf_msh3_close(%d) active", - (int)ctx->sock[SP_LOCAL])); - cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; - } - else { - DEBUGF(LOG_CF(data, cf, "cf_socket_close(%d) no longer at " - "conn->sock[], discarding", (int)ctx->sock[SP_LOCAL])); - ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; - } - if(cf->sockindex == FIRSTSOCKET) - cf->conn->remote_addr = NULL; - } - if(ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) { - sclose(ctx->sock[SP_LOCAL]); - } - if(ctx->sock[SP_REMOTE] != CURL_SOCKET_BAD) { - sclose(ctx->sock[SP_REMOTE]); - } - ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; - ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD; - } - CF_DATA_RESTORE(cf, save); -} - -static void cf_msh3_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - cf_msh3_close(cf, data); - free(cf->ctx); - cf->ctx = NULL; - /* no CF_DATA_RESTORE(cf, save); its gone */ - -} - -static CURLcode cf_msh3_query(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query, int *pres1, void *pres2) -{ - struct cf_msh3_ctx *ctx = cf->ctx; - - switch(query) { - case CF_QUERY_MAX_CONCURRENT: { - /* TODO: we do not have access to this so far, fake it */ - (void)ctx; - *pres1 = 100; - return CURLE_OK; - } - case CF_QUERY_TIMER_CONNECT: { - struct curltime *when = pres2; - /* we do not know when the first byte arrived */ - if(cf->connected) - *when = ctx->handshake_at; - return CURLE_OK; - } - case CF_QUERY_TIMER_APPCONNECT: { - struct curltime *when = pres2; - if(cf->connected) - *when = ctx->handshake_at; - return CURLE_OK; - } - default: - break; - } - return cf->next? - cf->next->cft->query(cf->next, data, query, pres1, pres2) : - CURLE_UNKNOWN_OPTION; -} - -static bool cf_msh3_conn_is_alive(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *input_pending) -{ - struct cf_msh3_ctx *ctx = cf->ctx; - - (void)data; - *input_pending = FALSE; - return ctx && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD && ctx->qconn && - ctx->connected; -} - -struct Curl_cftype Curl_cft_http3 = { - "HTTP/3", - CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX, - 0, - cf_msh3_destroy, - cf_msh3_connect, - cf_msh3_close, - Curl_cf_def_get_host, - cf_msh3_get_select_socks, - cf_msh3_data_pending, - cf_msh3_send, - cf_msh3_recv, - cf_msh3_data_event, - cf_msh3_conn_is_alive, - Curl_cf_def_conn_keep_alive, - cf_msh3_query, -}; - -CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai) -{ - struct cf_msh3_ctx *ctx = NULL; - struct Curl_cfilter *cf = NULL; - CURLcode result; - - (void)data; - (void)conn; - (void)ai; /* TODO: msh3 resolves itself? */ - ctx = calloc(sizeof(*ctx), 1); - if(!ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC); - ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; - ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD; - - result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); - -out: - *pcf = (!result)? cf : NULL; - if(result) { - Curl_safefree(cf); - Curl_safefree(ctx); - } - - return result; -} - -bool Curl_conn_is_msh3(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex) -{ - struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL; - - (void)data; - for(; cf; cf = cf->next) { - if(cf->cft == &Curl_cft_http3) - return TRUE; - if(cf->cft->flags & CF_TYPE_IP_CONNECT) - return FALSE; - } - return FALSE; -} - -#endif /* USE_MSH3 */ diff --git a/contrib/libs/curl/lib/vquic/curl_msh3.h b/contrib/libs/curl/lib/vquic/curl_msh3.h deleted file mode 100644 index 89c2a59f18..0000000000 --- a/contrib/libs/curl/lib/vquic/curl_msh3.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef HEADER_CURL_VQUIC_CURL_MSH3_H -#define HEADER_CURL_VQUIC_CURL_MSH3_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifdef USE_MSH3 - -#error #include <msh3.h> - -void Curl_msh3_ver(char *p, size_t len); - -CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai); - -bool Curl_conn_is_msh3(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex); - -#endif /* USE_MSQUIC */ - -#endif /* HEADER_CURL_VQUIC_CURL_MSH3_H */ diff --git a/contrib/libs/curl/lib/vquic/curl_ngtcp2.c b/contrib/libs/curl/lib/vquic/curl_ngtcp2.c deleted file mode 100644 index 89a4d3b880..0000000000 --- a/contrib/libs/curl/lib/vquic/curl_ngtcp2.c +++ /dev/null @@ -1,2698 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) -#error #include <ngtcp2/ngtcp2.h> -#error #include <nghttp3/nghttp3.h> - -#ifdef USE_OPENSSL -#include <openssl/err.h> -#ifdef OPENSSL_IS_BORINGSSL -#error #include <ngtcp2/ngtcp2_crypto_boringssl.h> -#else -#error #include <ngtcp2/ngtcp2_crypto_quictls.h> -#endif -#include "vtls/openssl.h" -#elif defined(USE_GNUTLS) -#error #include <ngtcp2/ngtcp2_crypto_gnutls.h> -#include "vtls/gtls.h" -#elif defined(USE_WOLFSSL) -#error #include <ngtcp2/ngtcp2_crypto_wolfssl.h> -#include "vtls/wolfssl.h" -#endif - -#include "urldata.h" -#include "sendf.h" -#include "strdup.h" -#include "rand.h" -#include "multiif.h" -#include "strcase.h" -#include "cfilters.h" -#include "cf-socket.h" -#include "connect.h" -#include "progress.h" -#include "strerror.h" -#include "dynbuf.h" -#include "http1.h" -#include "select.h" -#include "vquic.h" -#include "vquic_int.h" -#include "vtls/keylog.h" -#include "vtls/vtls.h" -#include "curl_ngtcp2.h" - -#include "warnless.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - - -#define H3_ALPN_H3_29 "\x5h3-29" -#define H3_ALPN_H3 "\x2h3" - -#define QUIC_MAX_STREAMS (256*1024) -#define QUIC_MAX_DATA (1*1024*1024) -#define QUIC_IDLE_TIMEOUT (60*NGTCP2_SECONDS) -#define QUIC_HANDSHAKE_TIMEOUT (10*NGTCP2_SECONDS) - -/* A stream window is the maximum amount we need to buffer for - * each active transfer. We use HTTP/3 flow control and only ACK - * when we take things out of the buffer. - * Chunk size is large enough to take a full DATA frame */ -#define H3_STREAM_WINDOW_SIZE (128 * 1024) -#define H3_STREAM_CHUNK_SIZE (16 * 1024) -/* The pool keeps spares around and half of a full stream windows - * seems good. More does not seem to improve performance. - * The benefit of the pool is that stream buffer to not keep - * spares. So memory consumption goes down when streams run empty, - * have a large upload done, etc. */ -#define H3_STREAM_POOL_SPARES \ - (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE ) / 2 -/* Receive and Send max number of chunks just follows from the - * chunk size and window size */ -#define H3_STREAM_RECV_CHUNKS \ - (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) -#define H3_STREAM_SEND_CHUNKS \ - (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) - - -#ifdef USE_OPENSSL -#define QUIC_CIPHERS \ - "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \ - "POLY1305_SHA256:TLS_AES_128_CCM_SHA256" -#define QUIC_GROUPS "P-256:X25519:P-384:P-521" -#elif defined(USE_GNUTLS) -#define QUIC_PRIORITY \ - "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \ - "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \ - "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \ - "%DISABLE_TLS13_COMPAT_MODE" -#elif defined(USE_WOLFSSL) -#define QUIC_CIPHERS \ - "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \ - "POLY1305_SHA256:TLS_AES_128_CCM_SHA256" -#define QUIC_GROUPS "P-256:P-384:P-521" -#endif - - -/* - * Store ngtcp2 version info in this buffer. - */ -void Curl_ngtcp2_ver(char *p, size_t len) -{ - const ngtcp2_info *ng2 = ngtcp2_version(0); - const nghttp3_info *ht3 = nghttp3_version(0); - (void)msnprintf(p, len, "ngtcp2/%s nghttp3/%s", - ng2->version_str, ht3->version_str); -} - -struct cf_ngtcp2_ctx { - struct cf_quic_ctx q; - ngtcp2_path connected_path; - ngtcp2_conn *qconn; - ngtcp2_cid dcid; - ngtcp2_cid scid; - uint32_t version; - ngtcp2_settings settings; - ngtcp2_transport_params transport_params; - ngtcp2_ccerr last_error; - ngtcp2_crypto_conn_ref conn_ref; -#ifdef USE_OPENSSL - SSL_CTX *sslctx; - SSL *ssl; -#elif defined(USE_GNUTLS) - struct gtls_instance *gtls; -#elif defined(USE_WOLFSSL) - WOLFSSL_CTX *sslctx; - WOLFSSL *ssl; -#endif - struct cf_call_data call_data; - nghttp3_conn *h3conn; - nghttp3_settings h3settings; - struct curltime started_at; /* time the current attempt started */ - struct curltime handshake_at; /* time connect handshake finished */ - struct curltime first_byte_at; /* when first byte was recvd */ - struct curltime reconnect_at; /* time the next attempt should start */ - struct bufc_pool stream_bufcp; /* chunk pool for streams */ - size_t max_stream_window; /* max flow window for one stream */ - int qlogfd; - BIT(got_first_byte); /* if first byte was received */ -}; - -/* How to access `call_data` from a cf_ngtcp2 filter */ -#undef CF_CTX_CALL_DATA -#define CF_CTX_CALL_DATA(cf) \ - ((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data - -/** - * All about the H3 internals of a stream - */ -struct h3_stream_ctx { - int64_t id; /* HTTP/3 protocol identifier */ - struct bufq sendbuf; /* h3 request body */ - struct bufq recvbuf; /* h3 response body */ - size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */ - size_t upload_blocked_len; /* the amount written last and EGAINed */ - size_t recv_buf_nonflow; /* buffered bytes, not counting for flow control */ - uint64_t error3; /* HTTP/3 stream error code */ - curl_off_t upload_left; /* number of request bytes left to upload */ - int status_code; /* HTTP status code */ - bool resp_hds_complete; /* we have a complete, final response */ - bool closed; /* TRUE on stream close */ - bool reset; /* TRUE on stream reset */ - bool send_closed; /* stream is local closed */ -}; - -#define H3_STREAM_CTX(d) ((struct h3_stream_ctx *)(((d) && (d)->req.p.http)? \ - ((struct HTTP *)(d)->req.p.http)->h3_ctx \ - : NULL)) -#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx -#define H3_STREAM_ID(d) (H3_STREAM_CTX(d)? \ - H3_STREAM_CTX(d)->id : -2) - -static CURLcode h3_data_setup(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - - if(!data || !data->req.p.http) { - failf(data, "initialization failure, transfer not http initialized"); - return CURLE_FAILED_INIT; - } - - if(stream) - return CURLE_OK; - - stream = calloc(1, sizeof(*stream)); - if(!stream) - return CURLE_OUT_OF_MEMORY; - - stream->id = -1; - /* on send, we control how much we put into the buffer */ - Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp, - H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); - stream->sendbuf_len_in_flight = 0; - /* on recv, we need a flexible buffer limit since we also write - * headers to it that are not counted against the nghttp3 flow limits. */ - Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp, - H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); - stream->recv_buf_nonflow = 0; - - H3_STREAM_LCTX(data) = stream; - DEBUGF(LOG_CF(data, cf, "data setup")); - return CURLE_OK; -} - -static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - - (void)cf; - if(stream) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] easy handle is done", - stream->id)); - Curl_bufq_free(&stream->sendbuf); - Curl_bufq_free(&stream->recvbuf); - free(stream); - H3_STREAM_LCTX(data) = NULL; - } -} - -/* ngtcp2 default congestion controller does not perform pacing. Limit - the maximum packet burst to MAX_PKT_BURST packets. */ -#define MAX_PKT_BURST 10 - -struct pkt_io_ctx { - struct Curl_cfilter *cf; - struct Curl_easy *data; - ngtcp2_tstamp ts; - size_t pkt_count; - ngtcp2_path_storage ps; -}; - -static ngtcp2_tstamp timestamp(void) -{ - struct curltime ct = Curl_now(); - return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS; -} - -static void pktx_init(struct pkt_io_ctx *pktx, - struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - pktx->cf = cf; - pktx->data = data; - pktx->ts = timestamp(); - pktx->pkt_count = 0; - ngtcp2_path_storage_zero(&pktx->ps); -} - -static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx); -static CURLcode cf_progress_egress(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx); -static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, - uint64_t datalen, void *user_data, - void *stream_user_data); - -static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref) -{ - struct Curl_cfilter *cf = conn_ref->user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - return ctx->qconn; -} - -#ifdef DEBUG_NGTCP2 -static void quic_printf(void *user_data, const char *fmt, ...) -{ - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - - (void)ctx; /* TODO: need an easy handle to infof() message */ - va_list ap; - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - fprintf(stderr, "\n"); -} -#endif - -static void qlog_callback(void *user_data, uint32_t flags, - const void *data, size_t datalen) -{ - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - (void)flags; - if(ctx->qlogfd != -1) { - ssize_t rc = write(ctx->qlogfd, data, datalen); - if(rc == -1) { - /* on write error, stop further write attempts */ - close(ctx->qlogfd); - ctx->qlogfd = -1; - } - } - -} - -static void quic_settings(struct cf_ngtcp2_ctx *ctx, - struct Curl_easy *data, - struct pkt_io_ctx *pktx) -{ - ngtcp2_settings *s = &ctx->settings; - ngtcp2_transport_params *t = &ctx->transport_params; - - ngtcp2_settings_default(s); - ngtcp2_transport_params_default(t); -#ifdef DEBUG_NGTCP2 - s->log_printf = quic_printf; -#else - s->log_printf = NULL; -#endif - - (void)data; - s->initial_ts = pktx->ts; - s->handshake_timeout = QUIC_HANDSHAKE_TIMEOUT; - s->max_window = 100 * ctx->max_stream_window; - s->max_stream_window = ctx->max_stream_window; - - t->initial_max_data = 10 * ctx->max_stream_window; - t->initial_max_stream_data_bidi_local = ctx->max_stream_window; - t->initial_max_stream_data_bidi_remote = ctx->max_stream_window; - t->initial_max_stream_data_uni = ctx->max_stream_window; - t->initial_max_streams_bidi = QUIC_MAX_STREAMS; - t->initial_max_streams_uni = QUIC_MAX_STREAMS; - t->max_idle_timeout = QUIC_IDLE_TIMEOUT; - if(ctx->qlogfd != -1) { - s->qlog_write = qlog_callback; - } -} - -#ifdef USE_OPENSSL -static void keylog_callback(const SSL *ssl, const char *line) -{ - (void)ssl; - Curl_tls_keylog_write_line(line); -} -#elif defined(USE_GNUTLS) -static int keylog_callback(gnutls_session_t session, const char *label, - const gnutls_datum_t *secret) -{ - gnutls_datum_t crandom; - gnutls_datum_t srandom; - - gnutls_session_get_random(session, &crandom, &srandom); - if(crandom.size != 32) { - return -1; - } - - Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size); - return 0; -} -#elif defined(USE_WOLFSSL) -#if defined(HAVE_SECRET_CALLBACK) -static void keylog_callback(const WOLFSSL *ssl, const char *line) -{ - (void)ssl; - Curl_tls_keylog_write_line(line); -} -#endif -#endif - -static int init_ngh3_conn(struct Curl_cfilter *cf); - -#ifdef USE_OPENSSL -static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx, - struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct connectdata *conn = cf->conn; - CURLcode result = CURLE_FAILED_INIT; - SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); - - if(!ssl_ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - -#ifdef OPENSSL_IS_BORINGSSL - if(ngtcp2_crypto_boringssl_configure_client_context(ssl_ctx) != 0) { - failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed"); - goto out; - } -#else - if(ngtcp2_crypto_quictls_configure_client_context(ssl_ctx) != 0) { - failf(data, "ngtcp2_crypto_quictls_configure_client_context failed"); - goto out; - } -#endif - - SSL_CTX_set_default_verify_paths(ssl_ctx); - -#ifdef OPENSSL_IS_BORINGSSL - if(SSL_CTX_set1_curves_list(ssl_ctx, QUIC_GROUPS) != 1) { - failf(data, "SSL_CTX_set1_curves_list failed"); - goto out; - } -#else - if(SSL_CTX_set_ciphersuites(ssl_ctx, QUIC_CIPHERS) != 1) { - char error_buffer[256]; - ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer)); - failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer); - goto out; - } - - if(SSL_CTX_set1_groups_list(ssl_ctx, QUIC_GROUPS) != 1) { - failf(data, "SSL_CTX_set1_groups_list failed"); - goto out; - } -#endif - - /* Open the file if a TLS or QUIC backend has not done this before. */ - Curl_tls_keylog_open(); - if(Curl_tls_keylog_enabled()) { - SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); - } - - result = Curl_ssl_setup_x509_store(cf, data, ssl_ctx); - if(result) - goto out; - - /* OpenSSL always tries to verify the peer, this only says whether it should - * fail to connect if the verification fails, or if it should continue - * anyway. In the latter case the result of the verification is checked with - * SSL_get_verify_result() below. */ - SSL_CTX_set_verify(ssl_ctx, conn->ssl_config.verifypeer ? - SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL); - - /* give application a chance to interfere with SSL set up. */ - if(data->set.ssl.fsslctx) { - Curl_set_in_callback(data, true); - result = (*data->set.ssl.fsslctx)(data, ssl_ctx, - data->set.ssl.fsslctxp); - Curl_set_in_callback(data, false); - if(result) { - failf(data, "error signaled by ssl ctx callback"); - goto out; - } - } - result = CURLE_OK; - -out: - *pssl_ctx = result? NULL : ssl_ctx; - if(result && ssl_ctx) - SSL_CTX_free(ssl_ctx); - return result; -} - -static CURLcode quic_set_client_cert(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - SSL_CTX *ssl_ctx = ctx->sslctx; - const struct ssl_config_data *ssl_config; - - ssl_config = Curl_ssl_get_config(data, FIRSTSOCKET); - DEBUGASSERT(ssl_config); - - if(ssl_config->primary.clientcert || ssl_config->primary.cert_blob - || ssl_config->cert_type) { - return Curl_ossl_set_client_cert( - data, ssl_ctx, ssl_config->primary.clientcert, - ssl_config->primary.cert_blob, ssl_config->cert_type, - ssl_config->key, ssl_config->key_blob, - ssl_config->key_type, ssl_config->key_passwd); - } - - return CURLE_OK; -} - -/** SSL callbacks ***/ - -static CURLcode quic_init_ssl(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - const uint8_t *alpn = NULL; - size_t alpnlen = 0; - - (void)data; - DEBUGASSERT(!ctx->ssl); - ctx->ssl = SSL_new(ctx->sslctx); - - SSL_set_app_data(ctx->ssl, &ctx->conn_ref); - SSL_set_connect_state(ctx->ssl); - SSL_set_quic_use_legacy_codepoint(ctx->ssl, 0); - - alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3; - alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1; - if(alpn) - SSL_set_alpn_protos(ctx->ssl, alpn, (int)alpnlen); - - /* set SNI */ - SSL_set_tlsext_host_name(ctx->ssl, cf->conn->host.name); - return CURLE_OK; -} -#elif defined(USE_GNUTLS) -static CURLcode quic_init_ssl(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - CURLcode result; - gnutls_datum_t alpn[2]; - /* this will need some attention when HTTPS proxy over QUIC get fixed */ - const char * const hostname = cf->conn->host.name; - long * const pverifyresult = &data->set.ssl.certverifyresult; - int rc; - - DEBUGASSERT(ctx->gtls == NULL); - ctx->gtls = calloc(1, sizeof(*(ctx->gtls))); - if(!ctx->gtls) - return CURLE_OUT_OF_MEMORY; - - result = gtls_client_init(data, &cf->conn->ssl_config, &data->set.ssl, - hostname, ctx->gtls, pverifyresult); - if(result) - return result; - - gnutls_session_set_ptr(ctx->gtls->session, &ctx->conn_ref); - - if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls->session) != 0) { - DEBUGF(LOG_CF(data, cf, - "ngtcp2_crypto_gnutls_configure_client_session failed\n")); - return CURLE_QUIC_CONNECT_ERROR; - } - - rc = gnutls_priority_set_direct(ctx->gtls->session, QUIC_PRIORITY, NULL); - if(rc < 0) { - DEBUGF(LOG_CF(data, cf, "gnutls_priority_set_direct failed: %s\n", - gnutls_strerror(rc))); - return CURLE_QUIC_CONNECT_ERROR; - } - - /* Open the file if a TLS or QUIC backend has not done this before. */ - Curl_tls_keylog_open(); - if(Curl_tls_keylog_enabled()) { - gnutls_session_set_keylog_function(ctx->gtls->session, keylog_callback); - } - - /* strip the first byte (the length) from NGHTTP3_ALPN_H3 */ - alpn[0].data = (unsigned char *)H3_ALPN_H3_29 + 1; - alpn[0].size = sizeof(H3_ALPN_H3_29) - 2; - alpn[1].data = (unsigned char *)H3_ALPN_H3 + 1; - alpn[1].size = sizeof(H3_ALPN_H3) - 2; - - gnutls_alpn_set_protocols(ctx->gtls->session, - alpn, 2, GNUTLS_ALPN_MANDATORY); - return CURLE_OK; -} -#elif defined(USE_WOLFSSL) - -static CURLcode quic_ssl_ctx(WOLFSSL_CTX **pssl_ctx, - struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct connectdata *conn = cf->conn; - CURLcode result = CURLE_FAILED_INIT; - WOLFSSL_CTX *ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); - - if(!ssl_ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - if(ngtcp2_crypto_wolfssl_configure_client_context(ssl_ctx) != 0) { - failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed"); - goto out; - } - - wolfSSL_CTX_set_default_verify_paths(ssl_ctx); - - if(wolfSSL_CTX_set_cipher_list(ssl_ctx, QUIC_CIPHERS) != 1) { - char error_buffer[256]; - ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer)); - failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer); - goto out; - } - - if(wolfSSL_CTX_set1_groups_list(ssl_ctx, (char *)QUIC_GROUPS) != 1) { - failf(data, "SSL_CTX_set1_groups_list failed"); - goto out; - } - - /* Open the file if a TLS or QUIC backend has not done this before. */ - Curl_tls_keylog_open(); - if(Curl_tls_keylog_enabled()) { -#if defined(HAVE_SECRET_CALLBACK) - wolfSSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); -#else - failf(data, "wolfSSL was built without keylog callback"); - goto out; -#endif - } - - if(conn->ssl_config.verifypeer) { - const char * const ssl_cafile = conn->ssl_config.CAfile; - const char * const ssl_capath = conn->ssl_config.CApath; - - wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); - if(conn->ssl_config.CAfile || conn->ssl_config.CApath) { - /* tell wolfSSL where to find CA certificates that are used to verify - the server's certificate. */ - if(!wolfSSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate verify locations:" - " CAfile: %s CApath: %s", - ssl_cafile ? ssl_cafile : "none", - ssl_capath ? ssl_capath : "none"); - goto out; - } - infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); - infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); - } -#ifdef CURL_CA_FALLBACK - else { - /* verifying the peer without any CA certificates won't work so - use wolfssl's built-in default as fallback */ - wolfSSL_CTX_set_default_verify_paths(ssl_ctx); - } -#endif - } - else { - wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL); - } - - /* give application a chance to interfere with SSL set up. */ - if(data->set.ssl.fsslctx) { - Curl_set_in_callback(data, true); - result = (*data->set.ssl.fsslctx)(data, ssl_ctx, - data->set.ssl.fsslctxp); - Curl_set_in_callback(data, false); - if(result) { - failf(data, "error signaled by ssl ctx callback"); - goto out; - } - } - result = CURLE_OK; - -out: - *pssl_ctx = result? NULL : ssl_ctx; - if(result && ssl_ctx) - SSL_CTX_free(ssl_ctx); - return result; -} - -/** SSL callbacks ***/ - -static CURLcode quic_init_ssl(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - const uint8_t *alpn = NULL; - size_t alpnlen = 0; - /* this will need some attention when HTTPS proxy over QUIC get fixed */ - const char * const hostname = cf->conn->host.name; - - (void)data; - DEBUGASSERT(!ctx->ssl); - ctx->ssl = wolfSSL_new(ctx->sslctx); - - wolfSSL_set_app_data(ctx->ssl, &ctx->conn_ref); - wolfSSL_set_connect_state(ctx->ssl); - wolfSSL_set_quic_use_legacy_codepoint(ctx->ssl, 0); - - alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3; - alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1; - if(alpn) - wolfSSL_set_alpn_protos(ctx->ssl, alpn, (int)alpnlen); - - /* set SNI */ - wolfSSL_UseSNI(ctx->ssl, WOLFSSL_SNI_HOST_NAME, - hostname, (unsigned short)strlen(hostname)); - - return CURLE_OK; -} -#endif /* defined(USE_WOLFSSL) */ - -static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data) -{ - (void)user_data; - (void)tconn; - return 0; -} - -static void report_consumed_data(struct Curl_cfilter *cf, - struct Curl_easy *data, - size_t consumed) -{ - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - struct cf_ngtcp2_ctx *ctx = cf->ctx; - - if(!stream) - return; - /* the HTTP/1.1 response headers are written to the buffer, but - * consuming those does not count against flow control. */ - if(stream->recv_buf_nonflow) { - if(consumed >= stream->recv_buf_nonflow) { - consumed -= stream->recv_buf_nonflow; - stream->recv_buf_nonflow = 0; - } - else { - stream->recv_buf_nonflow -= consumed; - consumed = 0; - } - } - if(consumed > 0) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] consumed %zu DATA bytes", - stream->id, consumed)); - ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, - consumed); - ngtcp2_conn_extend_max_offset(ctx->qconn, consumed); - } -} - -static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, - int64_t stream_id, uint64_t offset, - const uint8_t *buf, size_t buflen, - void *user_data, void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - nghttp3_ssize nconsumed; - int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0; - struct Curl_easy *data = stream_user_data; - (void)offset; - (void)data; - - nconsumed = - nghttp3_conn_read_stream(ctx->h3conn, stream_id, buf, buflen, fin); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read_stream(len=%zu) -> %zd", - stream_id, buflen, nconsumed)); - if(nconsumed < 0) { - ngtcp2_ccerr_set_application_error( - &ctx->last_error, - nghttp3_err_infer_quic_app_error_code((int)nconsumed), NULL, 0); - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - /* number of bytes inside buflen which consists of framing overhead - * including QPACK HEADERS. In other words, it does not consume payload of - * DATA frame. */ - ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed); - ngtcp2_conn_extend_max_offset(tconn, nconsumed); - - return 0; -} - -static int -cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id, - uint64_t offset, uint64_t datalen, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - int rv; - (void)stream_id; - (void)tconn; - (void)offset; - (void)datalen; - (void)stream_user_data; - - rv = nghttp3_conn_add_ack_offset(ctx->h3conn, stream_id, datalen); - if(rv) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, - int64_t stream3_id, uint64_t app_error_code, - void *user_data, void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct Curl_easy *data = stream_user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - int rv; - - (void)tconn; - (void)data; - /* stream is closed... */ - - if(!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) { - app_error_code = NGHTTP3_H3_NO_ERROR; - } - - rv = nghttp3_conn_close_stream(ctx->h3conn, stream3_id, - app_error_code); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] quic close(err=%" - PRIu64 ") -> %d", stream3_id, app_error_code, rv)); - if(rv) { - ngtcp2_ccerr_set_application_error( - &ctx->last_error, nghttp3_err_infer_quic_app_error_code(rv), NULL, 0); - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id, - uint64_t final_size, uint64_t app_error_code, - void *user_data, void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - int rv; - (void)tconn; - (void)final_size; - (void)app_error_code; - (void)data; - - rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] reset -> %d", stream_id, rv)); - if(rv) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int cb_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - int rv; - (void)tconn; - (void)app_error_code; - (void)stream_user_data; - - rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); - if(rv) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn, - uint64_t max_streams, - void *user_data) -{ - (void)tconn; - (void)max_streams; - (void)user_data; - - return 0; -} - -static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id, - uint64_t max_data, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - int rv; - (void)tconn; - (void)max_data; - (void)stream_user_data; - - rv = nghttp3_conn_unblock_stream(ctx->h3conn, stream_id); - if(rv) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static void cb_rand(uint8_t *dest, size_t destlen, - const ngtcp2_rand_ctx *rand_ctx) -{ - CURLcode result; - (void)rand_ctx; - - result = Curl_rand(NULL, dest, destlen); - if(result) { - /* cb_rand is only used for non-cryptographic context. If Curl_rand - failed, just fill 0 and call it *random*. */ - memset(dest, 0, destlen); - } -} - -static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid, - uint8_t *token, size_t cidlen, - void *user_data) -{ - CURLcode result; - (void)tconn; - (void)user_data; - - result = Curl_rand(NULL, cid->data, cidlen); - if(result) - return NGTCP2_ERR_CALLBACK_FAILURE; - cid->datalen = cidlen; - - result = Curl_rand(NULL, token, NGTCP2_STATELESS_RESET_TOKENLEN); - if(result) - return NGTCP2_ERR_CALLBACK_FAILURE; - - return 0; -} - -static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_encryption_level level, - void *user_data) -{ - struct Curl_cfilter *cf = user_data; - (void)tconn; - - if(level != NGTCP2_ENCRYPTION_LEVEL_1RTT) { - return 0; - } - - if(init_ngh3_conn(cf) != CURLE_OK) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static ngtcp2_callbacks ng_callbacks = { - ngtcp2_crypto_client_initial_cb, - NULL, /* recv_client_initial */ - ngtcp2_crypto_recv_crypto_data_cb, - cb_handshake_completed, - NULL, /* recv_version_negotiation */ - ngtcp2_crypto_encrypt_cb, - ngtcp2_crypto_decrypt_cb, - ngtcp2_crypto_hp_mask_cb, - cb_recv_stream_data, - cb_acked_stream_data_offset, - NULL, /* stream_open */ - cb_stream_close, - NULL, /* recv_stateless_reset */ - ngtcp2_crypto_recv_retry_cb, - cb_extend_max_local_streams_bidi, - NULL, /* extend_max_local_streams_uni */ - cb_rand, - cb_get_new_connection_id, - NULL, /* remove_connection_id */ - ngtcp2_crypto_update_key_cb, /* update_key */ - NULL, /* path_validation */ - NULL, /* select_preferred_addr */ - cb_stream_reset, - NULL, /* extend_max_remote_streams_bidi */ - NULL, /* extend_max_remote_streams_uni */ - cb_extend_max_stream_data, - NULL, /* dcid_status */ - NULL, /* handshake_confirmed */ - NULL, /* recv_new_token */ - ngtcp2_crypto_delete_crypto_aead_ctx_cb, - ngtcp2_crypto_delete_crypto_cipher_ctx_cb, - NULL, /* recv_datagram */ - NULL, /* ack_datagram */ - NULL, /* lost_datagram */ - ngtcp2_crypto_get_path_challenge_data_cb, - cb_stream_stop_sending, - NULL, /* version_negotiation */ - cb_recv_rx_key, - NULL, /* recv_tx_key */ - NULL, /* early_data_rejected */ -}; - -/** - * Connection maintenance like timeouts on packet ACKs etc. are done by us, not - * the OS like for TCP. POLL events on the socket therefore are not - * sufficient. - * ngtcp2 tells us when it wants to be invoked again. We handle that via - * the `Curl_expire()` mechanisms. - */ -static CURLcode check_and_set_expiry(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct pkt_io_ctx local_pktx; - ngtcp2_tstamp expiry; - - if(!pktx) { - pktx_init(&local_pktx, cf, data); - pktx = &local_pktx; - } - else { - pktx->ts = timestamp(); - } - - expiry = ngtcp2_conn_get_expiry(ctx->qconn); - if(expiry != UINT64_MAX) { - if(expiry <= pktx->ts) { - CURLcode result; - int rv = ngtcp2_conn_handle_expiry(ctx->qconn, pktx->ts); - if(rv) { - failf(data, "ngtcp2_conn_handle_expiry returned error: %s", - ngtcp2_strerror(rv)); - ngtcp2_ccerr_set_liberr(&ctx->last_error, rv, NULL, 0); - return CURLE_SEND_ERROR; - } - result = cf_progress_ingress(cf, data, pktx); - if(result) - return result; - result = cf_progress_egress(cf, data, pktx); - if(result) - return result; - /* ask again, things might have changed */ - expiry = ngtcp2_conn_get_expiry(ctx->qconn); - } - - if(expiry > pktx->ts) { - ngtcp2_duration timeout = expiry - pktx->ts; - if(timeout % NGTCP2_MILLISECONDS) { - timeout += NGTCP2_MILLISECONDS; - } - Curl_expire(data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC); - } - } - return CURLE_OK; -} - -static int cf_ngtcp2_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct SingleRequest *k = &data->req; - int rv = GETSOCK_BLANK; - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - socks[0] = ctx->q.sockfd; - - /* in HTTP/3 we can always get a frame, so check read */ - rv |= GETSOCK_READSOCK(0); - - /* we're still uploading or the HTTP/2 layer wants to send data */ - if((k->keepon & KEEP_SENDBITS) == KEEP_SEND && - ngtcp2_conn_get_cwnd_left(ctx->qconn) && - ngtcp2_conn_get_max_data_left(ctx->qconn) && - stream && nghttp3_conn_is_stream_writable(ctx->h3conn, stream->id)) - rv |= GETSOCK_WRITESOCK(0); - - /* DEBUGF(LOG_CF(data, cf, "get_select_socks -> %x (sock=%d)", - rv, (int)socks[0])); */ - CF_DATA_RESTORE(cf, save); - return rv; -} - -static void h3_drain_stream(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - unsigned char bits; - - (void)cf; - bits = CURL_CSELECT_IN; - if(stream && stream->upload_left && !stream->send_closed) - bits |= CURL_CSELECT_OUT; - if(data->state.dselect_bits != bits) { - data->state.dselect_bits = bits; - Curl_expire(data, 0, EXPIRE_RUN_NOW); - } -} - -static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - (void)conn; - (void)stream_id; - (void)app_error_code; - (void)cf; - - /* we might be called by nghttp3 after we already cleaned up */ - if(!stream) - return 0; - - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] h3 close(err=%" PRId64 ")", - stream_id, app_error_code)); - stream->closed = TRUE; - stream->error3 = app_error_code; - if(app_error_code == NGHTTP3_H3_INTERNAL_ERROR) { - stream->reset = TRUE; - stream->send_closed = TRUE; - } - h3_drain_stream(cf, data); - return 0; -} - -/* - * write_resp_raw() copies response data in raw format to the `data`'s - * receive buffer. If not enough space is available, it appends to the - * `data`'s overflow buffer. - */ -static CURLcode write_resp_raw(struct Curl_cfilter *cf, - struct Curl_easy *data, - const void *mem, size_t memlen, - bool flow) -{ - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - CURLcode result = CURLE_OK; - ssize_t nwritten; - - (void)cf; - if(!stream) { - return CURLE_RECV_ERROR; - } - nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result); - /* DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] add recvbuf(len=%zu) " - "-> %zd, %d", stream->id, memlen, nwritten, result)); - */ - if(nwritten < 0) { - return result; - } - - if(!flow) - stream->recv_buf_nonflow += (size_t)nwritten; - - if((size_t)nwritten < memlen) { - /* This MUST not happen. Our recbuf is dimensioned to hold the - * full max_stream_window and then some for this very reason. */ - DEBUGASSERT(0); - return CURLE_RECV_ERROR; - } - return result; -} - -static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, - const uint8_t *buf, size_t buflen, - void *user_data, void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct Curl_easy *data = stream_user_data; - CURLcode result; - - (void)conn; - (void)stream3_id; - - result = write_resp_raw(cf, data, buf, buflen, TRUE); - h3_drain_stream(cf, data); - return result? -1 : 0; -} - -static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream3_id, - size_t consumed, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - (void)conn; - (void)stream_user_data; - - /* nghttp3 has consumed bytes on the QUIC stream and we need to - * tell the QUIC connection to increase its flow control */ - ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream3_id, consumed); - ngtcp2_conn_extend_max_offset(ctx->qconn, consumed); - return 0; -} - -static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, - int fin, void *user_data, void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - CURLcode result = CURLE_OK; - (void)conn; - (void)stream_id; - (void)fin; - (void)cf; - - if(!stream) - return 0; - /* add a CRLF only if we've received some headers */ - result = write_resp_raw(cf, data, "\r\n", 2, FALSE); - if(result) { - return -1; - } - - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] end_headers(status_code=%d", - stream_id, stream->status_code)); - if(stream->status_code / 100 != 1) { - stream->resp_hds_complete = TRUE; - } - h3_drain_stream(cf, data); - return 0; -} - -static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, - int32_t token, nghttp3_rcbuf *name, - nghttp3_rcbuf *value, uint8_t flags, - void *user_data, void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); - nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - CURLcode result = CURLE_OK; - (void)conn; - (void)stream_id; - (void)token; - (void)flags; - (void)cf; - - /* we might have cleaned up this transfer already */ - if(!stream) - return 0; - - if(token == NGHTTP3_QPACK_TOKEN__STATUS) { - char line[14]; /* status line is always 13 characters long */ - size_t ncopy; - - result = Curl_http_decode_status(&stream->status_code, - (const char *)h3val.base, h3val.len); - if(result) - return -1; - ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n", - stream->status_code); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] status: %s", - stream_id, line)); - result = write_resp_raw(cf, data, line, ncopy, FALSE); - if(result) { - return -1; - } - } - else { - /* store as an HTTP1-style header */ - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] header: %.*s: %.*s", - stream_id, (int)h3name.len, h3name.base, - (int)h3val.len, h3val.base)); - result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE); - if(result) { - return -1; - } - result = write_resp_raw(cf, data, ": ", 2, FALSE); - if(result) { - return -1; - } - result = write_resp_raw(cf, data, h3val.base, h3val.len, FALSE); - if(result) { - return -1; - } - result = write_resp_raw(cf, data, "\r\n", 2, FALSE); - if(result) { - return -1; - } - } - return 0; -} - -static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - int rv; - (void)conn; - (void)stream_user_data; - - rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, 0, stream_id, - app_error_code); - if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data) { - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - int rv; - (void)conn; - (void)data; - - rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id, - app_error_code); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] reset -> %d", stream_id, rv)); - if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static nghttp3_callbacks ngh3_callbacks = { - cb_h3_acked_req_body, /* acked_stream_data */ - cb_h3_stream_close, - cb_h3_recv_data, - cb_h3_deferred_consume, - NULL, /* begin_headers */ - cb_h3_recv_header, - cb_h3_end_headers, - NULL, /* begin_trailers */ - cb_h3_recv_header, - NULL, /* end_trailers */ - cb_h3_stop_sending, - NULL, /* end_stream */ - cb_h3_reset_stream, - NULL, /* shutdown */ - NULL /* recv_settings */ -}; - -static int init_ngh3_conn(struct Curl_cfilter *cf) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - CURLcode result; - int rc; - int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id; - - if(ngtcp2_conn_get_streams_uni_left(ctx->qconn) < 3) { - return CURLE_QUIC_CONNECT_ERROR; - } - - nghttp3_settings_default(&ctx->h3settings); - - rc = nghttp3_conn_client_new(&ctx->h3conn, - &ngh3_callbacks, - &ctx->h3settings, - nghttp3_mem_default(), - cf); - if(rc) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } - - rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &ctrl_stream_id, NULL); - if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto fail; - } - - rc = nghttp3_conn_bind_control_stream(ctx->h3conn, ctrl_stream_id); - if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto fail; - } - - rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_enc_stream_id, NULL); - if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto fail; - } - - rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_dec_stream_id, NULL); - if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto fail; - } - - rc = nghttp3_conn_bind_qpack_streams(ctx->h3conn, qpack_enc_stream_id, - qpack_dec_stream_id); - if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto fail; - } - - return CURLE_OK; -fail: - - return result; -} - -static ssize_t recv_closed_stream(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_stream_ctx *stream, - CURLcode *err) -{ - ssize_t nread = -1; - - (void)cf; - if(stream->reset) { - failf(data, - "HTTP/3 stream %" PRId64 " reset by server", stream->id); - *err = CURLE_PARTIAL_FILE; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, was reset -> %d", - stream->id, *err)); - goto out; - } - else if(stream->error3 != NGHTTP3_H3_NO_ERROR) { - failf(data, - "HTTP/3 stream %" PRId64 " was not closed cleanly: " - "(err %"PRId64")", stream->id, stream->error3); - *err = CURLE_HTTP3; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed uncleanly" - " -> %d", stream->id, *err)); - goto out; - } - - if(!stream->resp_hds_complete) { - failf(data, - "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting" - " all response header fields, treated as error", - stream->id); - *err = CURLE_HTTP3; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed incomplete" - " -> %d", stream->id, *err)); - goto out; - } - else { - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed ok" - " -> %d", stream->id, *err)); - } - *err = CURLE_OK; - nread = 0; - -out: - return nread; -} - -/* incoming data frames on the h3 stream */ -static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - ssize_t nread = -1; - struct cf_call_data save; - struct pkt_io_ctx pktx; - - (void)ctx; - - CF_DATA_SAVE(save, cf, data); - DEBUGASSERT(cf->connected); - DEBUGASSERT(ctx); - DEBUGASSERT(ctx->qconn); - DEBUGASSERT(ctx->h3conn); - *err = CURLE_OK; - - pktx_init(&pktx, cf, data); - - if(!stream) { - *err = CURLE_RECV_ERROR; - goto out; - } - - if(!Curl_bufq_is_empty(&stream->recvbuf)) { - nread = Curl_bufq_read(&stream->recvbuf, - (unsigned char *)buf, len, err); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read recvbuf(len=%zu) " - "-> %zd, %d", stream->id, len, nread, *err)); - if(nread < 0) - goto out; - report_consumed_data(cf, data, nread); - } - - if(cf_progress_ingress(cf, data, &pktx)) { - *err = CURLE_RECV_ERROR; - nread = -1; - goto out; - } - - /* recvbuf had nothing before, maybe after progressing ingress? */ - if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) { - nread = Curl_bufq_read(&stream->recvbuf, - (unsigned char *)buf, len, err); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read recvbuf(len=%zu) " - "-> %zd, %d", stream->id, len, nread, *err)); - if(nread < 0) - goto out; - report_consumed_data(cf, data, nread); - } - - if(nread > 0) { - h3_drain_stream(cf, data); - } - else { - if(stream->closed) { - nread = recv_closed_stream(cf, data, stream, err); - goto out; - } - *err = CURLE_AGAIN; - nread = -1; - } - -out: - if(cf_progress_egress(cf, data, &pktx)) { - *err = CURLE_SEND_ERROR; - nread = -1; - } - else { - CURLcode result2 = check_and_set_expiry(cf, data, &pktx); - if(result2) { - *err = result2; - nread = -1; - } - } - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv(len=%zu) -> %zd, %d", - stream? stream->id : -1, len, nread, *err)); - CF_DATA_RESTORE(cf, save); - return nread; -} - -static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, - uint64_t datalen, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - size_t skiplen; - - (void)cf; - if(!stream) - return 0; - /* The server acknowledged `datalen` of bytes from our request body. - * This is a delta. We have kept this data in `sendbuf` for - * re-transmissions and can free it now. */ - if(datalen >= (uint64_t)stream->sendbuf_len_in_flight) - skiplen = stream->sendbuf_len_in_flight; - else - skiplen = (size_t)datalen; - Curl_bufq_skip(&stream->sendbuf, skiplen); - stream->sendbuf_len_in_flight -= skiplen; - - /* Everything ACKed, we resume upload processing */ - if(!stream->sendbuf_len_in_flight) { - int rv = nghttp3_conn_resume_stream(conn, stream_id); - if(rv) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - if((data->req.keepon & KEEP_SEND_HOLD) && - (data->req.keepon & KEEP_SEND)) { - data->req.keepon &= ~KEEP_SEND_HOLD; - h3_drain_stream(cf, data); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] unpausing acks", - stream_id)); - } - } - return 0; -} - -static nghttp3_ssize -cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, - nghttp3_vec *vec, size_t veccnt, - uint32_t *pflags, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - ssize_t nwritten = 0; - size_t nvecs = 0; - (void)cf; - (void)conn; - (void)stream_id; - (void)user_data; - (void)veccnt; - - if(!stream) - return NGHTTP3_ERR_CALLBACK_FAILURE; - /* nghttp3 keeps references to the sendbuf data until it is ACKed - * by the server (see `cb_h3_acked_req_body()` for updates). - * `sendbuf_len_in_flight` is the amount of bytes in `sendbuf` - * that we have already passed to nghttp3, but which have not been - * ACKed yet. - * Any amount beyond `sendbuf_len_in_flight` we need still to pass - * to nghttp3. Do that now, if we can. */ - if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { - nvecs = 0; - while(nvecs < veccnt && - Curl_bufq_peek_at(&stream->sendbuf, - stream->sendbuf_len_in_flight, - (const unsigned char **)&vec[nvecs].base, - &vec[nvecs].len)) { - stream->sendbuf_len_in_flight += vec[nvecs].len; - nwritten += vec[nvecs].len; - ++nvecs; - } - DEBUGASSERT(nvecs > 0); /* we SHOULD have been be able to peek */ - } - - if(nwritten > 0 && stream->upload_left != -1) - stream->upload_left -= nwritten; - - /* When we stopped sending and everything in `sendbuf` is "in flight", - * we are at the end of the request body. */ - if(stream->upload_left == 0) { - *pflags = NGHTTP3_DATA_FLAG_EOF; - stream->send_closed = TRUE; - } - else if(!nwritten) { - /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read req body -> AGAIN", - stream->id)); - return NGHTTP3_ERR_WOULDBLOCK; - } - - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read req body -> " - "%d vecs%s with %zu (buffered=%zu, left=%" - CURL_FORMAT_CURL_OFF_T ")", - stream->id, (int)nvecs, - *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"", - nwritten, Curl_bufq_len(&stream->sendbuf), - stream->upload_left)); - return (nghttp3_ssize)nvecs; -} - -/* Index where :authority header field will appear in request header - field list. */ -#define AUTHORITY_DST_IDX 3 - -static ssize_t h3_stream_open(struct Curl_cfilter *cf, - struct Curl_easy *data, - const void *buf, size_t len, - CURLcode *err) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = NULL; - struct h1_req_parser h1; - struct dynhds h2_headers; - size_t nheader; - nghttp3_nv *nva = NULL; - int rc = 0; - unsigned int i; - ssize_t nwritten = -1; - nghttp3_data_reader reader; - nghttp3_data_reader *preader = NULL; - - Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); - Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); - - *err = h3_data_setup(cf, data); - if(*err) - goto out; - stream = H3_STREAM_CTX(data); - DEBUGASSERT(stream); - - rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &stream->id, NULL); - if(rc) { - failf(data, "can get bidi streams"); - *err = CURLE_SEND_ERROR; - goto out; - } - - nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err); - if(nwritten < 0) - goto out; - DEBUGASSERT(h1.done); - DEBUGASSERT(h1.req); - - *err = Curl_http_req_to_h2(&h2_headers, h1.req, data); - if(*err) { - nwritten = -1; - goto out; - } - - nheader = Curl_dynhds_count(&h2_headers); - nva = malloc(sizeof(nghttp3_nv) * nheader); - if(!nva) { - *err = CURLE_OUT_OF_MEMORY; - nwritten = -1; - goto out; - } - - for(i = 0; i < nheader; ++i) { - struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); - nva[i].name = (unsigned char *)e->name; - nva[i].namelen = e->namelen; - nva[i].value = (unsigned char *)e->value; - nva[i].valuelen = e->valuelen; - nva[i].flags = NGHTTP3_NV_FLAG_NONE; - } - - switch(data->state.httpreq) { - case HTTPREQ_POST: - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - case HTTPREQ_PUT: - /* known request body size or -1 */ - if(data->state.infilesize != -1) - stream->upload_left = data->state.infilesize; - else - /* data sending without specifying the data amount up front */ - stream->upload_left = -1; /* unknown */ - break; - default: - /* there is not request body */ - stream->upload_left = 0; /* no request body */ - break; - } - - stream->send_closed = (stream->upload_left == 0); - if(!stream->send_closed) { - reader.read_data = cb_h3_read_req_body; - preader = &reader; - } - - rc = nghttp3_conn_submit_request(ctx->h3conn, stream->id, - nva, nheader, preader, data); - if(rc) { - switch(rc) { - case NGHTTP3_ERR_CONN_CLOSING: - DEBUGF(LOG_CF(data, cf, "h3sid[%"PRId64"] failed to send, " - "connection is closing", stream->id)); - break; - default: - DEBUGF(LOG_CF(data, cf, "h3sid[%"PRId64"] failed to send -> %d (%s)", - stream->id, rc, ngtcp2_strerror(rc))); - break; - } - *err = CURLE_SEND_ERROR; - nwritten = -1; - goto out; - } - - infof(data, "Using HTTP/3 Stream ID: %" PRId64, stream->id); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] opened for %s", - stream->id, data->state.url)); - -out: - free(nva); - Curl_h1_req_parse_free(&h1); - Curl_dynhds_free(&h2_headers); - return nwritten; -} - -static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - ssize_t sent = 0; - struct cf_call_data save; - struct pkt_io_ctx pktx; - CURLcode result; - - CF_DATA_SAVE(save, cf, data); - DEBUGASSERT(cf->connected); - DEBUGASSERT(ctx->qconn); - DEBUGASSERT(ctx->h3conn); - pktx_init(&pktx, cf, data); - *err = CURLE_OK; - - result = cf_progress_ingress(cf, data, &pktx); - if(result) { - *err = result; - sent = -1; - } - - if(!stream || stream->id < 0) { - sent = h3_stream_open(cf, data, buf, len, err); - if(sent < 0) { - DEBUGF(LOG_CF(data, cf, "failed to open stream -> %d", *err)); - goto out; - } - } - else if(stream->upload_blocked_len) { - /* the data in `buf` has alread been submitted or added to the - * buffers, but have been EAGAINed on the last invocation. */ - DEBUGASSERT(len >= stream->upload_blocked_len); - if(len < stream->upload_blocked_len) { - /* Did we get called again with a smaller `len`? This should not - * happen. We are not prepared to handle that. */ - failf(data, "HTTP/3 send again with decreased length"); - *err = CURLE_HTTP3; - sent = -1; - goto out; - } - sent = (ssize_t)stream->upload_blocked_len; - stream->upload_blocked_len = 0; - } - else if(stream->closed) { - *err = CURLE_HTTP3; - sent = -1; - goto out; - } - else { - sent = Curl_bufq_write(&stream->sendbuf, buf, len, err); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send, add to " - "sendbuf(len=%zu) -> %zd, %d", - stream->id, len, sent, *err)); - if(sent < 0) { - goto out; - } - - (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); - } - - result = cf_progress_egress(cf, data, &pktx); - if(result) { - *err = result; - sent = -1; - } - - if(stream && sent > 0 && stream->sendbuf_len_in_flight) { - /* We have unacknowledged DATA and cannot report success to our - * caller. Instead we EAGAIN and remember how much we have already - * "written" into our various internal connection buffers. - * We put the stream upload on HOLD, until this gets ACKed. */ - stream->upload_blocked_len = sent; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send(len=%zu), " - "%zu bytes in flight -> EGAIN", stream->id, len, - stream->sendbuf_len_in_flight)); - *err = CURLE_AGAIN; - sent = -1; - data->req.keepon |= KEEP_SEND_HOLD; - } - -out: - result = check_and_set_expiry(cf, data, &pktx); - if(result) { - *err = result; - sent = -1; - } - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send(len=%zu) -> %zd, %d", - stream? stream->id : -1, len, sent, *err)); - CF_DATA_RESTORE(cf, save); - return sent; -} - -static CURLcode qng_verify_peer(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - const char *hostname, *disp_hostname; - int port; - char *snihost; - - Curl_conn_get_host(data, cf->sockindex, &hostname, &disp_hostname, &port); - snihost = Curl_ssl_snihost(data, hostname, NULL); - if(!snihost) - return CURLE_PEER_FAILED_VERIFICATION; - - cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - cf->conn->httpversion = 30; - cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; - - if(cf->conn->ssl_config.verifyhost) { -#ifdef USE_OPENSSL - X509 *server_cert; - server_cert = SSL_get_peer_certificate(ctx->ssl); - if(!server_cert) { - return CURLE_PEER_FAILED_VERIFICATION; - } - result = Curl_ossl_verifyhost(data, cf->conn, server_cert); - X509_free(server_cert); - if(result) - return result; -#elif defined(USE_GNUTLS) - result = Curl_gtls_verifyserver(data, ctx->gtls->session, - &cf->conn->ssl_config, &data->set.ssl, - hostname, disp_hostname, - data->set.str[STRING_SSL_PINNEDPUBLICKEY]); - if(result) - return result; -#elif defined(USE_WOLFSSL) - if(wolfSSL_check_domain_name(ctx->ssl, snihost) == SSL_FAILURE) - return CURLE_PEER_FAILED_VERIFICATION; -#endif - infof(data, "Verified certificate just fine"); - } - else - infof(data, "Skipped certificate verification"); -#ifdef USE_OPENSSL - if(data->set.ssl.certinfo) - /* asked to gather certificate info */ - (void)Curl_ossl_certchain(data, ctx->ssl); -#endif - return result; -} - -static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, - struct sockaddr_storage *remote_addr, - socklen_t remote_addrlen, int ecn, - void *userp) -{ - struct pkt_io_ctx *pktx = userp; - struct cf_ngtcp2_ctx *ctx = pktx->cf->ctx; - ngtcp2_pkt_info pi; - ngtcp2_path path; - int rv; - - ++pktx->pkt_count; - ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr, - ctx->q.local_addrlen); - ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr, - remote_addrlen); - pi.ecn = (uint32_t)ecn; - - rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, pkt, pktlen, pktx->ts); - if(rv) { - DEBUGF(LOG_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s", - ngtcp2_strerror(rv))); - if(!ctx->last_error.error_code) { - if(rv == NGTCP2_ERR_CRYPTO) { - ngtcp2_ccerr_set_tls_alert(&ctx->last_error, - ngtcp2_conn_get_tls_alert(ctx->qconn), - NULL, 0); - } - else { - ngtcp2_ccerr_set_liberr(&ctx->last_error, rv, NULL, 0); - } - } - - if(rv == NGTCP2_ERR_CRYPTO) - /* this is a "TLS problem", but a failed certificate verification - is a common reason for this */ - return CURLE_PEER_FAILED_VERIFICATION; - return CURLE_RECV_ERROR; - } - - return CURLE_OK; -} - -static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct pkt_io_ctx local_pktx; - size_t pkts_chunk = 128, i; - size_t pkts_max = 10 * pkts_chunk; - CURLcode result = CURLE_OK; - - if(!pktx) { - pktx_init(&local_pktx, cf, data); - pktx = &local_pktx; - } - else { - pktx->ts = timestamp(); - } - - for(i = 0; i < pkts_max; i += pkts_chunk) { - pktx->pkt_count = 0; - result = vquic_recv_packets(cf, data, &ctx->q, pkts_chunk, - recv_pkt, pktx); - if(result) /* error */ - break; - if(pktx->pkt_count < pkts_chunk) /* got less than we could */ - break; - /* give egress a chance before we receive more */ - result = cf_progress_egress(cf, data, pktx); - if(result) /* error */ - break; - } - return result; -} - -/** - * Read a network packet to send from ngtcp2 into `buf`. - * Return number of bytes written or -1 with *err set. - */ -static ssize_t read_pkt_to_send(void *userp, - unsigned char *buf, size_t buflen, - CURLcode *err) -{ - struct pkt_io_ctx *x = userp; - struct cf_ngtcp2_ctx *ctx = x->cf->ctx; - nghttp3_vec vec[16]; - nghttp3_ssize veccnt; - ngtcp2_ssize ndatalen; - uint32_t flags; - int64_t stream_id; - int fin; - ssize_t nwritten, n; - veccnt = 0; - stream_id = -1; - fin = 0; - - /* ngtcp2 may want to put several frames from different streams into - * this packet. `NGTCP2_WRITE_STREAM_FLAG_MORE` tells it to do so. - * When `NGTCP2_ERR_WRITE_MORE` is returned, we *need* to make - * another iteration. - * When ngtcp2 is happy (because it has no other frame that would fit - * or it has nothing more to send), it returns the total length - * of the assembled packet. This may be 0 if there was nothing to send. */ - nwritten = 0; - *err = CURLE_OK; - for(;;) { - - if(ctx->h3conn && ngtcp2_conn_get_max_data_left(ctx->qconn)) { - veccnt = nghttp3_conn_writev_stream(ctx->h3conn, &stream_id, &fin, vec, - sizeof(vec) / sizeof(vec[0])); - if(veccnt < 0) { - failf(x->data, "nghttp3_conn_writev_stream returned error: %s", - nghttp3_strerror((int)veccnt)); - ngtcp2_ccerr_set_application_error( - &ctx->last_error, - nghttp3_err_infer_quic_app_error_code((int)veccnt), NULL, 0); - *err = CURLE_SEND_ERROR; - return -1; - } - } - - flags = NGTCP2_WRITE_STREAM_FLAG_MORE | - (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0); - n = ngtcp2_conn_writev_stream(ctx->qconn, &x->ps.path, - NULL, buf, buflen, - &ndatalen, flags, stream_id, - (const ngtcp2_vec *)vec, veccnt, x->ts); - if(n == 0) { - /* nothing to send */ - *err = CURLE_AGAIN; - nwritten = -1; - goto out; - } - else if(n < 0) { - switch(n) { - case NGTCP2_ERR_STREAM_DATA_BLOCKED: - DEBUGASSERT(ndatalen == -1); - nghttp3_conn_block_stream(ctx->h3conn, stream_id); - n = 0; - break; - case NGTCP2_ERR_STREAM_SHUT_WR: - DEBUGASSERT(ndatalen == -1); - nghttp3_conn_shutdown_stream_write(ctx->h3conn, stream_id); - n = 0; - break; - case NGTCP2_ERR_WRITE_MORE: - /* ngtcp2 wants to send more. update the flow of the stream whose data - * is in the buffer and continue */ - DEBUGASSERT(ndatalen >= 0); - n = 0; - break; - default: - DEBUGASSERT(ndatalen == -1); - failf(x->data, "ngtcp2_conn_writev_stream returned error: %s", - ngtcp2_strerror((int)n)); - ngtcp2_ccerr_set_liberr(&ctx->last_error, (int)n, NULL, 0); - *err = CURLE_SEND_ERROR; - nwritten = -1; - goto out; - } - } - - if(ndatalen >= 0) { - /* we add the amount of data bytes to the flow windows */ - int rv = nghttp3_conn_add_write_offset(ctx->h3conn, stream_id, ndatalen); - if(rv) { - failf(x->data, "nghttp3_conn_add_write_offset returned error: %s\n", - nghttp3_strerror(rv)); - return CURLE_SEND_ERROR; - } - } - - if(n > 0) { - /* packet assembled, leave */ - nwritten = n; - goto out; - } - } -out: - return nwritten; -} - -static CURLcode cf_progress_egress(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - ssize_t nread; - size_t max_payload_size, path_max_payload_size, max_pktcnt; - size_t pktcnt = 0; - size_t gsolen = 0; /* this disables gso until we have a clue */ - CURLcode curlcode; - struct pkt_io_ctx local_pktx; - - if(!pktx) { - pktx_init(&local_pktx, cf, data); - pktx = &local_pktx; - } - else { - pktx->ts = timestamp(); - ngtcp2_path_storage_zero(&pktx->ps); - } - - curlcode = vquic_flush(cf, data, &ctx->q); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return curlcode; - } - - /* In UDP, there is a maximum theoretical packet paload length and - * a minimum payload length that is "guarantueed" to work. - * To detect if this minimum payload can be increased, ngtcp2 sends - * now and then a packet payload larger than the minimum. It that - * is ACKed by the peer, both parties know that it works and - * the subsequent packets can use a larger one. - * This is called PMTUD (Path Maximum Transmission Unit Discovery). - * Since a PMTUD might be rejected right on send, we do not want it - * be followed by other packets of lesser size. Because those would - * also fail then. So, if we detect a PMTUD while buffering, we flush. - */ - max_payload_size = ngtcp2_conn_get_max_tx_udp_payload_size(ctx->qconn); - path_max_payload_size = - ngtcp2_conn_get_path_max_tx_udp_payload_size(ctx->qconn); - /* maximum number of packets buffered before we flush to the socket */ - max_pktcnt = CURLMIN(MAX_PKT_BURST, - ctx->q.sendbuf.chunk_size / max_payload_size); - - for(;;) { - /* add the next packet to send, if any, to our buffer */ - nread = Curl_bufq_sipn(&ctx->q.sendbuf, max_payload_size, - read_pkt_to_send, pktx, &curlcode); - /* DEBUGF(LOG_CF(data, cf, "sip packet(maxlen=%zu) -> %zd, %d", - max_payload_size, nread, curlcode)); */ - if(nread < 0) { - if(curlcode != CURLE_AGAIN) - return curlcode; - /* Nothing more to add, flush and leave */ - curlcode = vquic_send(cf, data, &ctx->q, gsolen); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return curlcode; - } - goto out; - } - - DEBUGASSERT(nread > 0); - if(pktcnt == 0) { - /* first packet in buffer. This is either of a known, "good" - * payload size or it is a PMTUD. We'll see. */ - gsolen = (size_t)nread; - } - else if((size_t)nread > gsolen || - (gsolen > path_max_payload_size && (size_t)nread != gsolen)) { - /* The just added packet is a PMTUD *or* the one(s) before the - * just added were PMTUD and the last one is smaller. - * Flush the buffer before the last add. */ - curlcode = vquic_send_tail_split(cf, data, &ctx->q, - gsolen, nread, nread); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return curlcode; - } - pktcnt = 0; - continue; - } - - if(++pktcnt >= max_pktcnt || (size_t)nread < gsolen) { - /* Reached MAX_PKT_BURST *or* - * the capacity of our buffer *or* - * last add was shorter than the previous ones, flush */ - curlcode = vquic_send(cf, data, &ctx->q, gsolen); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return curlcode; - } - /* pktbuf has been completely sent */ - pktcnt = 0; - } - } - -out: - return CURLE_OK; -} - -/* - * Called from transfer.c:data_pending to know if we should keep looping - * to receive more data from the connection. - */ -static bool cf_ngtcp2_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data) -{ - const struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - (void)cf; - return stream && !Curl_bufq_is_empty(&stream->recvbuf); -} - -static CURLcode h3_data_pause(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool pause) -{ - /* TODO: there seems right now no API in ngtcp2 to shrink/enlarge - * the streams windows. As we do in HTTP/2. */ - if(!pause) { - h3_drain_stream(cf, data); - Curl_expire(data, 0, EXPIRE_RUN_NOW); - } - return CURLE_OK; -} - -static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf, - struct Curl_easy *data, - int event, int arg1, void *arg2) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - (void)arg1; - (void)arg2; - switch(event) { - case CF_CTRL_DATA_SETUP: - break; - case CF_CTRL_DATA_PAUSE: - result = h3_data_pause(cf, data, (arg1 != 0)); - break; - case CF_CTRL_DATA_DONE: { - h3_data_done(cf, data); - break; - } - case CF_CTRL_DATA_DONE_SEND: { - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - if(stream && !stream->send_closed) { - stream->send_closed = TRUE; - stream->upload_left = Curl_bufq_len(&stream->sendbuf); - (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); - } - break; - } - case CF_CTRL_DATA_IDLE: - result = check_and_set_expiry(cf, data, NULL); - break; - default: - break; - } - CF_DATA_RESTORE(cf, save); - return result; -} - -static void cf_ngtcp2_ctx_clear(struct cf_ngtcp2_ctx *ctx) -{ - struct cf_call_data save = ctx->call_data; - - if(ctx->qlogfd != -1) { - close(ctx->qlogfd); - } -#ifdef USE_OPENSSL - if(ctx->ssl) - SSL_free(ctx->ssl); - if(ctx->sslctx) - SSL_CTX_free(ctx->sslctx); -#elif defined(USE_GNUTLS) - if(ctx->gtls) { - if(ctx->gtls->cred) - gnutls_certificate_free_credentials(ctx->gtls->cred); - if(ctx->gtls->session) - gnutls_deinit(ctx->gtls->session); - free(ctx->gtls); - } -#elif defined(USE_WOLFSSL) - if(ctx->ssl) - wolfSSL_free(ctx->ssl); - if(ctx->sslctx) - wolfSSL_CTX_free(ctx->sslctx); -#endif - vquic_ctx_free(&ctx->q); - if(ctx->h3conn) - nghttp3_conn_del(ctx->h3conn); - if(ctx->qconn) - ngtcp2_conn_del(ctx->qconn); - Curl_bufcp_free(&ctx->stream_bufcp); - - memset(ctx, 0, sizeof(*ctx)); - ctx->qlogfd = -1; - ctx->call_data = save; -} - -static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - if(ctx && ctx->qconn) { - char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; - ngtcp2_tstamp ts; - ngtcp2_ssize rc; - - DEBUGF(LOG_CF(data, cf, "close")); - ts = timestamp(); - rc = ngtcp2_conn_write_connection_close(ctx->qconn, NULL, /* path */ - NULL, /* pkt_info */ - (uint8_t *)buffer, sizeof(buffer), - &ctx->last_error, ts); - if(rc > 0) { - while((send(ctx->q.sockfd, buffer, (SEND_TYPE_ARG3)rc, 0) == -1) && - SOCKERRNO == EINTR); - } - - cf_ngtcp2_ctx_clear(ctx); - } - - cf->connected = FALSE; - CF_DATA_RESTORE(cf, save); -} - -static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - DEBUGF(LOG_CF(data, cf, "destroy")); - if(ctx) { - cf_ngtcp2_ctx_clear(ctx); - free(ctx); - } - cf->ctx = NULL; - /* No CF_DATA_RESTORE(cf, save) possible */ - (void)save; -} - -/* - * Might be called twice for happy eyeballs. - */ -static CURLcode cf_connect_start(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - int rc; - int rv; - CURLcode result; - const struct Curl_sockaddr_ex *sockaddr; - int qfd; - - ctx->version = NGTCP2_PROTO_VER_MAX; - ctx->max_stream_window = H3_STREAM_WINDOW_SIZE; - Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, - H3_STREAM_POOL_SPARES); - -#ifdef USE_OPENSSL - result = quic_ssl_ctx(&ctx->sslctx, cf, data); - if(result) - return result; - - result = quic_set_client_cert(cf, data); - if(result) - return result; -#elif defined(USE_WOLFSSL) - result = quic_ssl_ctx(&ctx->sslctx, cf, data); - if(result) - return result; -#endif - - result = quic_init_ssl(cf, data); - if(result) - return result; - - ctx->dcid.datalen = NGTCP2_MAX_CIDLEN; - result = Curl_rand(data, ctx->dcid.data, NGTCP2_MAX_CIDLEN); - if(result) - return result; - - ctx->scid.datalen = NGTCP2_MAX_CIDLEN; - result = Curl_rand(data, ctx->scid.data, NGTCP2_MAX_CIDLEN); - if(result) - return result; - - (void)Curl_qlogdir(data, ctx->scid.data, NGTCP2_MAX_CIDLEN, &qfd); - ctx->qlogfd = qfd; /* -1 if failure above */ - quic_settings(ctx, data, pktx); - - result = vquic_ctx_init(&ctx->q); - if(result) - return result; - - Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, - &sockaddr, NULL, NULL, NULL, NULL); - ctx->q.local_addrlen = sizeof(ctx->q.local_addr); - rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, - &ctx->q.local_addrlen); - if(rv == -1) - return CURLE_QUIC_CONNECT_ERROR; - - ngtcp2_addr_init(&ctx->connected_path.local, - (struct sockaddr *)&ctx->q.local_addr, - ctx->q.local_addrlen); - ngtcp2_addr_init(&ctx->connected_path.remote, - &sockaddr->sa_addr, sockaddr->addrlen); - - rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, - &ctx->connected_path, - NGTCP2_PROTO_VER_V1, &ng_callbacks, - &ctx->settings, &ctx->transport_params, - NULL, cf); - if(rc) - return CURLE_QUIC_CONNECT_ERROR; - -#ifdef USE_GNUTLS - ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->gtls->session); -#else - ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->ssl); -#endif - - ngtcp2_ccerr_default(&ctx->last_error); - - ctx->conn_ref.get_conn = get_conn; - ctx->conn_ref.user_data = cf; - - return CURLE_OK; -} - -static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking, bool *done) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - struct cf_call_data save; - struct curltime now; - struct pkt_io_ctx pktx; - - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } - - /* Connect the UDP filter first */ - if(!cf->next->connected) { - result = Curl_conn_cf_connect(cf->next, data, blocking, done); - if(result || !*done) - return result; - } - - *done = FALSE; - now = Curl_now(); - pktx_init(&pktx, cf, data); - - CF_DATA_SAVE(save, cf, data); - - if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) { - /* Not time yet to attempt the next connect */ - DEBUGF(LOG_CF(data, cf, "waiting for reconnect time")); - goto out; - } - - if(!ctx->qconn) { - ctx->started_at = now; - result = cf_connect_start(cf, data, &pktx); - if(result) - goto out; - result = cf_progress_egress(cf, data, &pktx); - /* we do not expect to be able to recv anything yet */ - goto out; - } - - result = cf_progress_ingress(cf, data, &pktx); - if(result) - goto out; - - result = cf_progress_egress(cf, data, &pktx); - if(result) - goto out; - - if(ngtcp2_conn_get_handshake_completed(ctx->qconn)) { - ctx->handshake_at = now; - DEBUGF(LOG_CF(data, cf, "handshake complete after %dms", - (int)Curl_timediff(now, ctx->started_at))); - result = qng_verify_peer(cf, data); - if(!result) { - DEBUGF(LOG_CF(data, cf, "peer verified")); - cf->connected = TRUE; - cf->conn->alpn = CURL_HTTP_VERSION_3; - *done = TRUE; - connkeep(cf->conn, "HTTP/3 default"); - } - } - -out: - if(result == CURLE_RECV_ERROR && ctx->qconn && - ngtcp2_conn_in_draining_period(ctx->qconn)) { - /* When a QUIC server instance is shutting down, it may send us a - * CONNECTION_CLOSE right away. Our connection then enters the DRAINING - * state. - * This may be a stopping of the service or it may be that the server - * is reloading and a new instance will start serving soon. - * In any case, we tear down our socket and start over with a new one. - * We re-open the underlying UDP cf right now, but do not start - * connecting until called again. - */ - int reconn_delay_ms = 200; - - DEBUGF(LOG_CF(data, cf, "connect, remote closed, reconnect after %dms", - reconn_delay_ms)); - Curl_conn_cf_close(cf->next, data); - cf_ngtcp2_ctx_clear(ctx); - result = Curl_conn_cf_connect(cf->next, data, FALSE, done); - if(!result && *done) { - *done = FALSE; - ctx->reconnect_at = now; - ctx->reconnect_at.tv_usec += reconn_delay_ms * 1000; - Curl_expire(data, reconn_delay_ms, EXPIRE_QUIC); - result = CURLE_OK; - } - } - -#ifndef CURL_DISABLE_VERBOSE_STRINGS - if(result) { - const char *r_ip; - int r_port; - - Curl_cf_socket_peek(cf->next, data, NULL, NULL, - &r_ip, &r_port, NULL, NULL); - infof(data, "QUIC connect to %s port %u failed: %s", - r_ip, r_port, curl_easy_strerror(result)); - } -#endif - if(!result && ctx->qconn) { - result = check_and_set_expiry(cf, data, &pktx); - } - DEBUGF(LOG_CF(data, cf, "connect -> %d, done=%d", result, *done)); - CF_DATA_RESTORE(cf, save); - return result; -} - -static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query, int *pres1, void *pres2) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct cf_call_data save; - - switch(query) { - case CF_QUERY_MAX_CONCURRENT: { - const ngtcp2_transport_params *rp; - DEBUGASSERT(pres1); - - CF_DATA_SAVE(save, cf, data); - rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); - if(rp) - *pres1 = (rp->initial_max_streams_bidi > INT_MAX)? - INT_MAX : (int)rp->initial_max_streams_bidi; - else /* not arrived yet? */ - *pres1 = Curl_multi_max_concurrent_streams(data->multi); - DEBUGF(LOG_CF(data, cf, "query max_conncurrent -> %d", *pres1)); - CF_DATA_RESTORE(cf, save); - return CURLE_OK; - } - case CF_QUERY_CONNECT_REPLY_MS: - if(ctx->got_first_byte) { - timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at); - *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX; - } - else - *pres1 = -1; - return CURLE_OK; - case CF_QUERY_TIMER_CONNECT: { - struct curltime *when = pres2; - if(ctx->got_first_byte) - *when = ctx->first_byte_at; - return CURLE_OK; - } - case CF_QUERY_TIMER_APPCONNECT: { - struct curltime *when = pres2; - if(cf->connected) - *when = ctx->handshake_at; - return CURLE_OK; - } - default: - break; - } - return cf->next? - cf->next->cft->query(cf->next, data, query, pres1, pres2) : - CURLE_UNKNOWN_OPTION; -} - -static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *input_pending) -{ - bool alive = TRUE; - - *input_pending = FALSE; - if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) - return FALSE; - - if(*input_pending) { - /* This happens before we've sent off a request and the connection is - not in use by any other transfer, there shouldn't be any data here, - only "protocol frames" */ - *input_pending = FALSE; - if(cf_progress_ingress(cf, data, NULL)) - alive = FALSE; - else { - alive = TRUE; - } - } - - return alive; -} - -struct Curl_cftype Curl_cft_http3 = { - "HTTP/3", - CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX, - 0, - cf_ngtcp2_destroy, - cf_ngtcp2_connect, - cf_ngtcp2_close, - Curl_cf_def_get_host, - cf_ngtcp2_get_select_socks, - cf_ngtcp2_data_pending, - cf_ngtcp2_send, - cf_ngtcp2_recv, - cf_ngtcp2_data_event, - cf_ngtcp2_conn_is_alive, - Curl_cf_def_conn_keep_alive, - cf_ngtcp2_query, -}; - -CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai) -{ - struct cf_ngtcp2_ctx *ctx = NULL; - struct Curl_cfilter *cf = NULL, *udp_cf = NULL; - CURLcode result; - - (void)data; - ctx = calloc(sizeof(*ctx), 1); - if(!ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - ctx->qlogfd = -1; - cf_ngtcp2_ctx_clear(ctx); - - result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); - if(result) - goto out; - - result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC); - if(result) - goto out; - - cf->conn = conn; - udp_cf->conn = cf->conn; - udp_cf->sockindex = cf->sockindex; - cf->next = udp_cf; - -out: - *pcf = (!result)? cf : NULL; - if(result) { - if(udp_cf) - Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE); - Curl_safefree(cf); - Curl_safefree(ctx); - } - return result; -} - -bool Curl_conn_is_ngtcp2(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex) -{ - struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL; - - (void)data; - for(; cf; cf = cf->next) { - if(cf->cft == &Curl_cft_http3) - return TRUE; - if(cf->cft->flags & CF_TYPE_IP_CONNECT) - return FALSE; - } - return FALSE; -} - -#endif diff --git a/contrib/libs/curl/lib/vquic/curl_ngtcp2.h b/contrib/libs/curl/lib/vquic/curl_ngtcp2.h deleted file mode 100644 index baa4d41527..0000000000 --- a/contrib/libs/curl/lib/vquic/curl_ngtcp2.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef HEADER_CURL_VQUIC_CURL_NGTCP2_H -#define HEADER_CURL_VQUIC_CURL_NGTCP2_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) - -#ifdef HAVE_NETINET_UDP_H -#include <netinet/udp.h> -#endif - -#error #include <ngtcp2/ngtcp2_crypto.h> -#error #include <nghttp3/nghttp3.h> -#ifdef USE_OPENSSL -#include <openssl/ssl.h> -#elif defined(USE_WOLFSSL) -#error #include <wolfssl/options.h> -#error #include <wolfssl/ssl.h> -#error #include <wolfssl/quic.h> -#endif - -struct Curl_cfilter; - -#include "urldata.h" - -void Curl_ngtcp2_ver(char *p, size_t len); - -CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai); - -bool Curl_conn_is_ngtcp2(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex); -#endif - -#endif /* HEADER_CURL_VQUIC_CURL_NGTCP2_H */ diff --git a/contrib/libs/curl/lib/vquic/curl_quiche.c b/contrib/libs/curl/lib/vquic/curl_quiche.c deleted file mode 100644 index 2d33bc458a..0000000000 --- a/contrib/libs/curl/lib/vquic/curl_quiche.c +++ /dev/null @@ -1,1637 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifdef USE_QUICHE -#error #include <quiche.h> -#include <openssl/err.h> -#include <openssl/ssl.h> -#include "bufq.h" -#include "urldata.h" -#include "cfilters.h" -#include "cf-socket.h" -#include "sendf.h" -#include "strdup.h" -#include "rand.h" -#include "strcase.h" -#include "multiif.h" -#include "connect.h" -#include "progress.h" -#include "strerror.h" -#include "http1.h" -#include "vquic.h" -#include "vquic_int.h" -#include "curl_quiche.h" -#include "transfer.h" -#include "vtls/openssl.h" -#include "vtls/keylog.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* #define DEBUG_QUICHE */ - -#define QUIC_MAX_STREAMS (100) -#define QUIC_IDLE_TIMEOUT (5 * 1000) /* milliseconds */ - -#define H3_STREAM_WINDOW_SIZE (128 * 1024) -#define H3_STREAM_CHUNK_SIZE (16 * 1024) -/* The pool keeps spares around and half of a full stream windows - * seems good. More does not seem to improve performance. - * The benefit of the pool is that stream buffer to not keep - * spares. So memory consumption goes down when streams run empty, - * have a large upload done, etc. */ -#define H3_STREAM_POOL_SPARES \ - (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE ) / 2 -/* Receive and Send max number of chunks just follows from the - * chunk size and window size */ -#define H3_STREAM_RECV_CHUNKS \ - (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) -#define H3_STREAM_SEND_CHUNKS \ - (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) - -/* - * Store quiche version info in this buffer. - */ -void Curl_quiche_ver(char *p, size_t len) -{ - (void)msnprintf(p, len, "quiche/%s", quiche_version()); -} - -static void keylog_callback(const SSL *ssl, const char *line) -{ - (void)ssl; - Curl_tls_keylog_write_line(line); -} - -static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data) -{ - SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); - - SSL_CTX_set_alpn_protos(ssl_ctx, - (const uint8_t *)QUICHE_H3_APPLICATION_PROTOCOL, - sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1); - - SSL_CTX_set_default_verify_paths(ssl_ctx); - - /* Open the file if a TLS or QUIC backend has not done this before. */ - Curl_tls_keylog_open(); - if(Curl_tls_keylog_enabled()) { - SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); - } - - { - struct connectdata *conn = data->conn; - if(conn->ssl_config.verifypeer) { - const char * const ssl_cafile = conn->ssl_config.CAfile; - const char * const ssl_capath = conn->ssl_config.CApath; - if(ssl_cafile || ssl_capath) { - SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); - /* tell OpenSSL where to find CA certificates that are used to verify - the server's certificate. */ - if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate verify locations:" - " CAfile: %s CApath: %s", - ssl_cafile ? ssl_cafile : "none", - ssl_capath ? ssl_capath : "none"); - return NULL; - } - infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); - infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); - } -#ifdef CURL_CA_FALLBACK - else { - /* 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(ssl_ctx); - } -#endif - } - } - return ssl_ctx; -} - -struct cf_quiche_ctx { - struct cf_quic_ctx q; - quiche_conn *qconn; - quiche_config *cfg; - quiche_h3_conn *h3c; - quiche_h3_config *h3config; - uint8_t scid[QUICHE_MAX_CONN_ID_LEN]; - SSL_CTX *sslctx; - SSL *ssl; - struct curltime started_at; /* time the current attempt started */ - struct curltime handshake_at; /* time connect handshake finished */ - struct curltime first_byte_at; /* when first byte was recvd */ - struct curltime reconnect_at; /* time the next attempt should start */ - struct bufc_pool stream_bufcp; /* chunk pool for streams */ - curl_off_t data_recvd; - size_t sends_on_hold; /* # of streams with SEND_HOLD set */ - BIT(goaway); /* got GOAWAY from server */ - BIT(got_first_byte); /* if first byte was received */ -}; - -#ifdef DEBUG_QUICHE -static void quiche_debug_log(const char *line, void *argp) -{ - (void)argp; - fprintf(stderr, "%s\n", line); -} -#endif - -static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx) -{ - if(ctx) { - vquic_ctx_free(&ctx->q); - if(ctx->qconn) - quiche_conn_free(ctx->qconn); - if(ctx->h3config) - quiche_h3_config_free(ctx->h3config); - if(ctx->h3c) - quiche_h3_conn_free(ctx->h3c); - if(ctx->cfg) - quiche_config_free(ctx->cfg); - Curl_bufcp_free(&ctx->stream_bufcp); - memset(ctx, 0, sizeof(*ctx)); - } -} - -/** - * All about the H3 internals of a stream - */ -struct stream_ctx { - int64_t id; /* HTTP/3 protocol stream identifier */ - struct bufq recvbuf; /* h3 response */ - uint64_t error3; /* HTTP/3 stream error code */ - curl_off_t upload_left; /* number of request bytes left to upload */ - bool closed; /* TRUE on stream close */ - bool reset; /* TRUE on stream reset */ - bool send_closed; /* stream is locally closed */ - bool resp_hds_complete; /* complete, final response has been received */ - bool resp_got_header; /* TRUE when h3 stream has recvd some HEADER */ -}; - -#define H3_STREAM_CTX(d) ((struct stream_ctx *)(((d) && (d)->req.p.http)? \ - ((struct HTTP *)(d)->req.p.http)->h3_ctx \ - : NULL)) -#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx -#define H3_STREAM_ID(d) (H3_STREAM_CTX(d)? \ - H3_STREAM_CTX(d)->id : -2) - -static bool stream_send_is_suspended(struct Curl_easy *data) -{ - return (data->req.keepon & KEEP_SEND_HOLD); -} - -static void stream_send_suspend(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - - if((data->req.keepon & KEEP_SENDBITS) == KEEP_SEND) { - data->req.keepon |= KEEP_SEND_HOLD; - ++ctx->sends_on_hold; - if(H3_STREAM_ID(data) >= 0) - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] suspend sending", - H3_STREAM_ID(data))); - else - DEBUGF(LOG_CF(data, cf, "[%s] suspend sending", - data->state.url)); - } -} - -static void stream_send_resume(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - - if(stream_send_is_suspended(data)) { - data->req.keepon &= ~KEEP_SEND_HOLD; - --ctx->sends_on_hold; - if(H3_STREAM_ID(data) >= 0) - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] resume sending", - H3_STREAM_ID(data))); - else - DEBUGF(LOG_CF(data, cf, "[%s] resume sending", - data->state.url)); - Curl_expire(data, 0, EXPIRE_RUN_NOW); - } -} - -static void check_resumes(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - struct Curl_easy *sdata; - - if(ctx->sends_on_hold) { - DEBUGASSERT(data->multi); - for(sdata = data->multi->easyp; - sdata && ctx->sends_on_hold; sdata = sdata->next) { - if(stream_send_is_suspended(sdata)) { - stream_send_resume(cf, sdata); - } - } - } -} - -static CURLcode h3_data_setup(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H3_STREAM_CTX(data); - - if(stream) - return CURLE_OK; - - stream = calloc(1, sizeof(*stream)); - if(!stream) - return CURLE_OUT_OF_MEMORY; - - H3_STREAM_LCTX(data) = stream; - stream->id = -1; - Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp, - H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); - DEBUGF(LOG_CF(data, cf, "data setup")); - return CURLE_OK; -} - -static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H3_STREAM_CTX(data); - - (void)cf; - if(stream) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] easy handle is done", - stream->id)); - if(stream_send_is_suspended(data)) { - data->req.keepon &= ~KEEP_SEND_HOLD; - --ctx->sends_on_hold; - } - Curl_bufq_free(&stream->recvbuf); - free(stream); - H3_STREAM_LCTX(data) = NULL; - } -} - -static void drain_stream(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct stream_ctx *stream = H3_STREAM_CTX(data); - unsigned char bits; - - (void)cf; - bits = CURL_CSELECT_IN; - if(stream && !stream->send_closed && stream->upload_left) - bits |= CURL_CSELECT_OUT; - if(data->state.dselect_bits != bits) { - data->state.dselect_bits = bits; - Curl_expire(data, 0, EXPIRE_RUN_NOW); - } -} - -static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf, - struct Curl_easy *data, - int64_t stream3_id) -{ - struct Curl_easy *sdata; - - (void)cf; - if(H3_STREAM_ID(data) == stream3_id) { - return data; - } - else { - DEBUGASSERT(data->multi); - for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { - if((sdata->conn == data->conn) && H3_STREAM_ID(sdata) == stream3_id) { - return sdata; - } - } - } - return NULL; -} - -/* - * write_resp_raw() copies response data in raw format to the `data`'s - * receive buffer. If not enough space is available, it appends to the - * `data`'s overflow buffer. - */ -static CURLcode write_resp_raw(struct Curl_cfilter *cf, - struct Curl_easy *data, - const void *mem, size_t memlen) -{ - struct stream_ctx *stream = H3_STREAM_CTX(data); - CURLcode result = CURLE_OK; - ssize_t nwritten; - - (void)cf; - if(!stream) - return CURLE_RECV_ERROR; - nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result); - if(nwritten < 0) - return result; - - if((size_t)nwritten < memlen) { - /* This MUST not happen. Our recbuf is dimensioned to hold the - * full max_stream_window and then some for this very reason. */ - DEBUGASSERT(0); - return CURLE_RECV_ERROR; - } - return result; -} - -struct cb_ctx { - struct Curl_cfilter *cf; - struct Curl_easy *data; -}; - -static int cb_each_header(uint8_t *name, size_t name_len, - uint8_t *value, size_t value_len, - void *argp) -{ - struct cb_ctx *x = argp; - struct stream_ctx *stream = H3_STREAM_CTX(x->data); - CURLcode result; - - (void)stream; - if((name_len == 7) && !strncmp(HTTP_PSEUDO_STATUS, (char *)name, 7)) { - result = write_resp_raw(x->cf, x->data, "HTTP/3 ", sizeof("HTTP/3 ") - 1); - if(!result) - result = write_resp_raw(x->cf, x->data, value, value_len); - if(!result) - result = write_resp_raw(x->cf, x->data, " \r\n", 3); - } - else { - result = write_resp_raw(x->cf, x->data, name, name_len); - if(!result) - result = write_resp_raw(x->cf, x->data, ": ", 2); - if(!result) - result = write_resp_raw(x->cf, x->data, value, value_len); - if(!result) - result = write_resp_raw(x->cf, x->data, "\r\n", 2); - } - if(result) { - DEBUGF(LOG_CF(x->data, x->cf, - "[h3sid=%"PRId64"][HEADERS][%.*s: %.*s] error %d", - stream? stream->id : -1, (int)name_len, name, - (int)value_len, value, result)); - } - return result; -} - -static ssize_t stream_resp_read(void *reader_ctx, - unsigned char *buf, size_t len, - CURLcode *err) -{ - struct cb_ctx *x = reader_ctx; - struct cf_quiche_ctx *ctx = x->cf->ctx; - struct stream_ctx *stream = H3_STREAM_CTX(x->data); - ssize_t nread; - - if(!stream) { - *err = CURLE_RECV_ERROR; - return -1; - } - - nread = quiche_h3_recv_body(ctx->h3c, ctx->qconn, stream->id, - buf, len); - if(nread >= 0) { - *err = CURLE_OK; - return nread; - } - else { - *err = CURLE_AGAIN; - return -1; - } -} - -static CURLcode cf_recv_body(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct stream_ctx *stream = H3_STREAM_CTX(data); - ssize_t nwritten; - struct cb_ctx cb_ctx; - CURLcode result = CURLE_OK; - - if(!stream) - return CURLE_RECV_ERROR; - - if(!stream->resp_hds_complete) { - result = write_resp_raw(cf, data, "\r\n", 2); - if(result) - return result; - stream->resp_hds_complete = TRUE; - } - - cb_ctx.cf = cf; - cb_ctx.data = data; - nwritten = Curl_bufq_slurp(&stream->recvbuf, - stream_resp_read, &cb_ctx, &result); - - if(nwritten < 0 && result != CURLE_AGAIN) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] recv_body error %zd", - stream->id, nwritten)); - failf(data, "Error %d in HTTP/3 response body for stream[%"PRId64"]", - result, stream->id); - stream->closed = TRUE; - stream->reset = TRUE; - stream->send_closed = TRUE; - streamclose(cf->conn, "Reset of stream"); - return result; - } - return CURLE_OK; -} - -#ifdef DEBUGBUILD -static const char *cf_ev_name(quiche_h3_event *ev) -{ - switch(quiche_h3_event_type(ev)) { - case QUICHE_H3_EVENT_HEADERS: - return "HEADERS"; - case QUICHE_H3_EVENT_DATA: - return "DATA"; - case QUICHE_H3_EVENT_RESET: - return "RESET"; - case QUICHE_H3_EVENT_FINISHED: - return "FINISHED"; - case QUICHE_H3_EVENT_GOAWAY: - return "GOAWAY"; - default: - return "Unknown"; - } -} -#else -#define cf_ev_name(x) "" -#endif - -static CURLcode h3_process_event(struct Curl_cfilter *cf, - struct Curl_easy *data, - int64_t stream3_id, - quiche_h3_event *ev) -{ - struct stream_ctx *stream = H3_STREAM_CTX(data); - struct cb_ctx cb_ctx; - CURLcode result = CURLE_OK; - int rc; - - if(!stream) - return CURLE_OK; - DEBUGASSERT(stream3_id == stream->id); - switch(quiche_h3_event_type(ev)) { - case QUICHE_H3_EVENT_HEADERS: - stream->resp_got_header = TRUE; - cb_ctx.cf = cf; - cb_ctx.data = data; - rc = quiche_h3_event_for_each_header(ev, cb_each_header, &cb_ctx); - if(rc) { - failf(data, "Error %d in HTTP/3 response header for stream[%"PRId64"]", - rc, stream3_id); - return CURLE_RECV_ERROR; - } - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][HEADERS]", stream3_id)); - break; - - case QUICHE_H3_EVENT_DATA: - if(!stream->closed) { - result = cf_recv_body(cf, data); - } - break; - - case QUICHE_H3_EVENT_RESET: - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][RESET]", stream3_id)); - stream->closed = TRUE; - stream->reset = TRUE; - stream->send_closed = TRUE; - streamclose(cf->conn, "Reset of stream"); - break; - - case QUICHE_H3_EVENT_FINISHED: - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][FINISHED]", stream3_id)); - if(!stream->resp_hds_complete) { - result = write_resp_raw(cf, data, "\r\n", 2); - if(result) - return result; - stream->resp_hds_complete = TRUE; - } - stream->closed = TRUE; - streamclose(cf->conn, "End of stream"); - break; - - case QUICHE_H3_EVENT_GOAWAY: - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][GOAWAY]", stream3_id)); - break; - - default: - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] recv, unhandled event %d", - stream3_id, quiche_h3_event_type(ev))); - break; - } - return result; -} - -static CURLcode cf_poll_events(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H3_STREAM_CTX(data); - struct Curl_easy *sdata; - quiche_h3_event *ev; - CURLcode result; - - /* Take in the events and distribute them to the transfers. */ - while(ctx->h3c) { - int64_t stream3_id = quiche_h3_conn_poll(ctx->h3c, ctx->qconn, &ev); - if(stream3_id == QUICHE_H3_ERR_DONE) { - break; - } - else if(stream3_id < 0) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] error poll: %"PRId64, - stream? stream->id : -1, stream3_id)); - return CURLE_HTTP3; - } - - sdata = get_stream_easy(cf, data, stream3_id); - if(!sdata) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] discard event %s for " - "unknown [h3sid=%"PRId64"]", - stream? stream->id : -1, cf_ev_name(ev), - stream3_id)); - } - else { - result = h3_process_event(cf, sdata, stream3_id, ev); - drain_stream(cf, sdata); - if(result) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] error processing event %s " - "for [h3sid=%"PRId64"] -> %d", - stream? stream->id : -1, cf_ev_name(ev), - stream3_id, result)); - if(data == sdata) { - /* Only report this error to the caller if it is about the - * transfer we were called with. Otherwise we fail a transfer - * due to a problem in another one. */ - quiche_h3_event_free(ev); - return result; - } - } - quiche_h3_event_free(ev); - } - } - return CURLE_OK; -} - -struct recv_ctx { - struct Curl_cfilter *cf; - struct Curl_easy *data; - int pkts; -}; - -static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, - struct sockaddr_storage *remote_addr, - socklen_t remote_addrlen, int ecn, - void *userp) -{ - struct recv_ctx *r = userp; - struct cf_quiche_ctx *ctx = r->cf->ctx; - quiche_recv_info recv_info; - ssize_t nread; - - (void)ecn; - ++r->pkts; - - recv_info.to = (struct sockaddr *)&ctx->q.local_addr; - recv_info.to_len = ctx->q.local_addrlen; - recv_info.from = (struct sockaddr *)remote_addr; - recv_info.from_len = remote_addrlen; - - nread = quiche_conn_recv(ctx->qconn, (unsigned char *)pkt, pktlen, - &recv_info); - if(nread < 0) { - if(QUICHE_ERR_DONE == nread) { - DEBUGF(LOG_CF(r->data, r->cf, "ingress, quiche is DONE")); - return CURLE_OK; - } - else if(QUICHE_ERR_TLS_FAIL == nread) { - long verify_ok = SSL_get_verify_result(ctx->ssl); - if(verify_ok != X509_V_OK) { - failf(r->data, "SSL certificate problem: %s", - X509_verify_cert_error_string(verify_ok)); - return CURLE_PEER_FAILED_VERIFICATION; - } - } - else { - failf(r->data, "quiche_conn_recv() == %zd", nread); - return CURLE_RECV_ERROR; - } - } - else if((size_t)nread < pktlen) { - DEBUGF(LOG_CF(r->data, r->cf, "ingress, quiche only read %zd/%zu bytes", - nread, pktlen)); - } - - return CURLE_OK; -} - -static CURLcode cf_process_ingress(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - struct recv_ctx rctx; - CURLcode result; - - DEBUGASSERT(ctx->qconn); - rctx.cf = cf; - rctx.data = data; - rctx.pkts = 0; - - result = vquic_recv_packets(cf, data, &ctx->q, 1000, recv_pkt, &rctx); - if(result) - return result; - - if(rctx.pkts > 0) { - /* quiche digested ingress packets. It might have opened flow control - * windows again. */ - check_resumes(cf, data); - } - return cf_poll_events(cf, data); -} - -struct read_ctx { - struct Curl_cfilter *cf; - struct Curl_easy *data; - quiche_send_info send_info; -}; - -static ssize_t read_pkt_to_send(void *userp, - unsigned char *buf, size_t buflen, - CURLcode *err) -{ - struct read_ctx *x = userp; - struct cf_quiche_ctx *ctx = x->cf->ctx; - ssize_t nwritten; - - nwritten = quiche_conn_send(ctx->qconn, buf, buflen, &x->send_info); - if(nwritten == QUICHE_ERR_DONE) { - *err = CURLE_AGAIN; - return -1; - } - - if(nwritten < 0) { - failf(x->data, "quiche_conn_send returned %zd", nwritten); - *err = CURLE_SEND_ERROR; - return -1; - } - *err = CURLE_OK; - return nwritten; -} - -/* - * flush_egress drains the buffers and sends off data. - * Calls failf() on errors. - */ -static CURLcode cf_flush_egress(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - ssize_t nread; - CURLcode result; - int64_t timeout_ns; - struct read_ctx readx; - size_t pkt_count, gsolen; - - result = vquic_flush(cf, data, &ctx->q); - if(result) { - if(result == CURLE_AGAIN) { - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return result; - } - - readx.cf = cf; - readx.data = data; - memset(&readx.send_info, 0, sizeof(readx.send_info)); - pkt_count = 0; - gsolen = quiche_conn_max_send_udp_payload_size(ctx->qconn); - for(;;) { - /* add the next packet to send, if any, to our buffer */ - nread = Curl_bufq_sipn(&ctx->q.sendbuf, 0, - read_pkt_to_send, &readx, &result); - /* DEBUGF(LOG_CF(data, cf, "sip packet(maxlen=%zu) -> %zd, %d", - (size_t)0, nread, result)); */ - - if(nread < 0) { - if(result != CURLE_AGAIN) - return result; - /* Nothing more to add, flush and leave */ - result = vquic_send(cf, data, &ctx->q, gsolen); - if(result) { - if(result == CURLE_AGAIN) { - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return result; - } - goto out; - } - - ++pkt_count; - if((size_t)nread < gsolen || pkt_count >= MAX_PKT_BURST) { - result = vquic_send(cf, data, &ctx->q, gsolen); - if(result) { - if(result == CURLE_AGAIN) { - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - goto out; - } - pkt_count = 0; - } - } - -out: - timeout_ns = quiche_conn_timeout_as_nanos(ctx->qconn); - if(timeout_ns % 1000000) - timeout_ns += 1000000; - /* expire resolution is milliseconds */ - Curl_expire(data, (timeout_ns / 1000000), EXPIRE_QUIC); - return result; -} - -static ssize_t recv_closed_stream(struct Curl_cfilter *cf, - struct Curl_easy *data, - CURLcode *err) -{ - struct stream_ctx *stream = H3_STREAM_CTX(data); - ssize_t nread = -1; - - DEBUGASSERT(stream); - if(stream->reset) { - failf(data, - "HTTP/3 stream %" PRId64 " reset by server", stream->id); - *err = stream->resp_got_header? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, was reset -> %d", - stream->id, *err)); - } - else if(!stream->resp_got_header) { - failf(data, - "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting" - " all response header fields, treated as error", - stream->id); - /* *err = CURLE_PARTIAL_FILE; */ - *err = CURLE_RECV_ERROR; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed incomplete" - " -> %d", stream->id, *err)); - } - else { - *err = CURLE_OK; - nread = 0; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed ok" - " -> %d", stream->id, *err)); - } - return nread; -} - -static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H3_STREAM_CTX(data); - ssize_t nread = -1; - CURLcode result; - - if(!stream) { - *err = CURLE_RECV_ERROR; - return -1; - } - - if(!Curl_bufq_is_empty(&stream->recvbuf)) { - nread = Curl_bufq_read(&stream->recvbuf, - (unsigned char *)buf, len, err); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read recvbuf(len=%zu) " - "-> %zd, %d", stream->id, len, nread, *err)); - if(nread < 0) - goto out; - } - - if(cf_process_ingress(cf, data)) { - DEBUGF(LOG_CF(data, cf, "cf_recv, error on ingress")); - *err = CURLE_RECV_ERROR; - nread = -1; - goto out; - } - - /* recvbuf had nothing before, maybe after progressing ingress? */ - if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) { - nread = Curl_bufq_read(&stream->recvbuf, - (unsigned char *)buf, len, err); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read recvbuf(len=%zu) " - "-> %zd, %d", stream->id, len, nread, *err)); - if(nread < 0) - goto out; - } - - if(nread > 0) { - if(stream->closed) - drain_stream(cf, data); - } - else { - if(stream->closed) { - nread = recv_closed_stream(cf, data, err); - goto out; - } - else if(quiche_conn_is_draining(ctx->qconn)) { - failf(data, "QUIC connection is draining"); - *err = CURLE_HTTP3; - nread = -1; - goto out; - } - *err = CURLE_AGAIN; - nread = -1; - } - -out: - result = cf_flush_egress(cf, data); - if(result) { - DEBUGF(LOG_CF(data, cf, "cf_recv, flush egress failed")); - *err = result; - nread = -1; - } - if(nread > 0) - ctx->data_recvd += nread; - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] cf_recv(total=%" - CURL_FORMAT_CURL_OFF_T ") -> %zd, %d", - stream ? stream->id : (int64_t)0, - ctx->data_recvd, nread, *err)); - return nread; -} - -/* Index where :authority header field will appear in request header - field list. */ -#define AUTHORITY_DST_IDX 3 - -static ssize_t h3_open_stream(struct Curl_cfilter *cf, - struct Curl_easy *data, - const void *buf, size_t len, - CURLcode *err) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H3_STREAM_CTX(data); - size_t nheader, i; - int64_t stream3_id; - struct h1_req_parser h1; - struct dynhds h2_headers; - quiche_h3_header *nva = NULL; - ssize_t nwritten; - - if(!stream) { - *err = h3_data_setup(cf, data); - if(*err) { - return -1; - } - stream = H3_STREAM_CTX(data); - DEBUGASSERT(stream); - } - - Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); - Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); - - DEBUGASSERT(stream); - nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err); - if(nwritten < 0) - goto out; - DEBUGASSERT(h1.done); - DEBUGASSERT(h1.req); - - *err = Curl_http_req_to_h2(&h2_headers, h1.req, data); - if(*err) { - nwritten = -1; - goto out; - } - - nheader = Curl_dynhds_count(&h2_headers); - nva = malloc(sizeof(quiche_h3_header) * nheader); - if(!nva) { - *err = CURLE_OUT_OF_MEMORY; - nwritten = -1; - goto out; - } - - for(i = 0; i < nheader; ++i) { - struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); - nva[i].name = (unsigned char *)e->name; - nva[i].name_len = e->namelen; - nva[i].value = (unsigned char *)e->value; - nva[i].value_len = e->valuelen; - } - - switch(data->state.httpreq) { - case HTTPREQ_POST: - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - case HTTPREQ_PUT: - if(data->state.infilesize != -1) - stream->upload_left = data->state.infilesize; - else - /* data sending without specifying the data amount up front */ - stream->upload_left = -1; /* unknown */ - break; - default: - stream->upload_left = 0; /* no request body */ - break; - } - - if(stream->upload_left == 0) - stream->send_closed = TRUE; - - stream3_id = quiche_h3_send_request(ctx->h3c, ctx->qconn, nva, nheader, - stream->send_closed); - if(stream3_id < 0) { - if(QUICHE_H3_ERR_STREAM_BLOCKED == stream3_id) { - /* quiche seems to report this error if the connection window is - * exhausted. Which happens frequently and intermittent. */ - DEBUGF(LOG_CF(data, cf, "send_request(%s) rejected with BLOCKED", - data->state.url)); - stream_send_suspend(cf, data); - *err = CURLE_AGAIN; - nwritten = -1; - goto out; - } - else { - DEBUGF(LOG_CF(data, cf, "send_request(%s) -> %" PRId64, - data->state.url, stream3_id)); - } - *err = CURLE_SEND_ERROR; - nwritten = -1; - goto out; - } - - DEBUGASSERT(stream->id == -1); - *err = CURLE_OK; - stream->id = stream3_id; - stream->closed = FALSE; - stream->reset = FALSE; - - infof(data, "Using HTTP/3 Stream ID: %" PRId64, stream3_id); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] opened for %s", - stream3_id, data->state.url)); - -out: - free(nva); - Curl_h1_req_parse_free(&h1); - Curl_dynhds_free(&h2_headers); - return nwritten; -} - -static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H3_STREAM_CTX(data); - CURLcode result; - ssize_t nwritten; - - *err = cf_process_ingress(cf, data); - if(*err) { - nwritten = -1; - goto out; - } - - if(!stream || stream->id < 0) { - nwritten = h3_open_stream(cf, data, buf, len, err); - if(nwritten < 0) - goto out; - stream = H3_STREAM_CTX(data); - } - else { - bool eof = (stream->upload_left >= 0 && - (curl_off_t)len >= stream->upload_left); - nwritten = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->id, - (uint8_t *)buf, len, eof); - if(nwritten == QUICHE_H3_ERR_DONE || (nwritten == 0 && len > 0)) { - /* TODO: we seem to be blocked on flow control and should HOLD - * sending. But when do we open again? */ - if(!quiche_conn_stream_writable(ctx->qconn, stream->id, len)) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send_body(len=%zu) " - "-> window exhausted", stream->id, len)); - stream_send_suspend(cf, data); - } - *err = CURLE_AGAIN; - nwritten = -1; - goto out; - } - else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send_body(len=%zu) " - "-> exceeds size", stream->id, len)); - *err = CURLE_SEND_ERROR; - nwritten = -1; - goto out; - } - else if(nwritten < 0) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send_body(len=%zu) " - "-> quiche err %zd", stream->id, len, nwritten)); - *err = CURLE_SEND_ERROR; - nwritten = -1; - goto out; - } - else { - /* quiche accepted all or at least a part of the buf */ - if(stream->upload_left > 0) { - stream->upload_left = (nwritten < stream->upload_left)? - (stream->upload_left - nwritten) : 0; - } - if(stream->upload_left == 0) - stream->send_closed = TRUE; - - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send body(len=%zu, " - "left=%" CURL_FORMAT_CURL_OFF_T ") -> %zd", - stream->id, len, stream->upload_left, nwritten)); - *err = CURLE_OK; - } - } - -out: - result = cf_flush_egress(cf, data); - if(result) { - *err = result; - nwritten = -1; - } - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send(len=%zu) -> %zd, %d", - stream? stream->id : -1, len, nwritten, *err)); - return nwritten; -} - -static bool stream_is_writeable(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H3_STREAM_CTX(data); - - return stream && - quiche_conn_stream_writable(ctx->qconn, (uint64_t)stream->id, 1); -} - -static int cf_quiche_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - struct SingleRequest *k = &data->req; - int rv = GETSOCK_BLANK; - - socks[0] = ctx->q.sockfd; - - /* in an HTTP/3 connection we can basically always get a frame so we should - always be ready for one */ - rv |= GETSOCK_READSOCK(0); - - /* we're still uploading or the HTTP/3 layer wants to send data */ - if(((k->keepon & KEEP_SENDBITS) == KEEP_SEND) - && stream_is_writeable(cf, data)) - rv |= GETSOCK_WRITESOCK(0); - - return rv; -} - -/* - * Called from transfer.c:data_pending to know if we should keep looping - * to receive more data from the connection. - */ -static bool cf_quiche_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data) -{ - const struct stream_ctx *stream = H3_STREAM_CTX(data); - (void)cf; - return stream && !Curl_bufq_is_empty(&stream->recvbuf); -} - -static CURLcode h3_data_pause(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool pause) -{ - /* TODO: there seems right now no API in quiche to shrink/enlarge - * the streams windows. As we do in HTTP/2. */ - if(!pause) { - drain_stream(cf, data); - Curl_expire(data, 0, EXPIRE_RUN_NOW); - } - return CURLE_OK; -} - -static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf, - struct Curl_easy *data, - int event, int arg1, void *arg2) -{ - CURLcode result = CURLE_OK; - - (void)arg1; - (void)arg2; - switch(event) { - case CF_CTRL_DATA_SETUP: - break; - case CF_CTRL_DATA_PAUSE: - result = h3_data_pause(cf, data, (arg1 != 0)); - break; - case CF_CTRL_DATA_DONE: { - h3_data_done(cf, data); - break; - } - case CF_CTRL_DATA_DONE_SEND: { - struct stream_ctx *stream = H3_STREAM_CTX(data); - if(stream && !stream->send_closed) { - unsigned char body[1]; - ssize_t sent; - - stream->send_closed = TRUE; - stream->upload_left = 0; - body[0] = 'X'; - sent = cf_quiche_send(cf, data, body, 0, &result); - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] DONE_SEND -> %zd, %d", - stream->id, sent, result)); - } - break; - } - case CF_CTRL_DATA_IDLE: - result = cf_flush_egress(cf, data); - if(result) - DEBUGF(LOG_CF(data, cf, "data idle, flush egress -> %d", result)); - break; - default: - break; - } - return result; -} - -static CURLcode cf_verify_peer(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - - cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - cf->conn->httpversion = 30; - cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; - - if(cf->conn->ssl_config.verifyhost) { - X509 *server_cert; - server_cert = SSL_get_peer_certificate(ctx->ssl); - if(!server_cert) { - result = CURLE_PEER_FAILED_VERIFICATION; - goto out; - } - result = Curl_ossl_verifyhost(data, cf->conn, server_cert); - X509_free(server_cert); - if(result) - goto out; - } - else - DEBUGF(LOG_CF(data, cf, "Skipped certificate verification")); - - ctx->h3config = quiche_h3_config_new(); - if(!ctx->h3config) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - /* Create a new HTTP/3 connection on the QUIC connection. */ - ctx->h3c = quiche_h3_conn_new_with_transport(ctx->qconn, ctx->h3config); - if(!ctx->h3c) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - if(data->set.ssl.certinfo) - /* asked to gather certificate info */ - (void)Curl_ossl_certchain(data, ctx->ssl); - -out: - if(result) { - if(ctx->h3config) { - quiche_h3_config_free(ctx->h3config); - ctx->h3config = NULL; - } - if(ctx->h3c) { - quiche_h3_conn_free(ctx->h3c); - ctx->h3c = NULL; - } - } - return result; -} - -static CURLcode cf_connect_start(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - int rv; - CURLcode result; - const struct Curl_sockaddr_ex *sockaddr; - - DEBUGASSERT(ctx->q.sockfd != CURL_SOCKET_BAD); - -#ifdef DEBUG_QUICHE - /* initialize debug log callback only once */ - static int debug_log_init = 0; - if(!debug_log_init) { - quiche_enable_debug_logging(quiche_debug_log, NULL); - debug_log_init = 1; - } -#endif - Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, - H3_STREAM_POOL_SPARES); - ctx->data_recvd = 0; - - result = vquic_ctx_init(&ctx->q); - if(result) - return result; - - ctx->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION); - if(!ctx->cfg) { - failf(data, "can't create quiche config"); - return CURLE_FAILED_INIT; - } - quiche_config_enable_pacing(ctx->cfg, false); - quiche_config_set_max_idle_timeout(ctx->cfg, QUIC_IDLE_TIMEOUT); - quiche_config_set_initial_max_data(ctx->cfg, (1 * 1024 * 1024) - /* (QUIC_MAX_STREAMS/2) * H3_STREAM_WINDOW_SIZE */); - quiche_config_set_initial_max_streams_bidi(ctx->cfg, QUIC_MAX_STREAMS); - quiche_config_set_initial_max_streams_uni(ctx->cfg, QUIC_MAX_STREAMS); - quiche_config_set_initial_max_stream_data_bidi_local(ctx->cfg, - H3_STREAM_WINDOW_SIZE); - quiche_config_set_initial_max_stream_data_bidi_remote(ctx->cfg, - H3_STREAM_WINDOW_SIZE); - quiche_config_set_initial_max_stream_data_uni(ctx->cfg, - H3_STREAM_WINDOW_SIZE); - quiche_config_set_disable_active_migration(ctx->cfg, TRUE); - - quiche_config_set_max_connection_window(ctx->cfg, - 10 * QUIC_MAX_STREAMS * H3_STREAM_WINDOW_SIZE); - quiche_config_set_max_stream_window(ctx->cfg, 10 * H3_STREAM_WINDOW_SIZE); - quiche_config_set_application_protos(ctx->cfg, - (uint8_t *) - QUICHE_H3_APPLICATION_PROTOCOL, - sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - - 1); - - DEBUGASSERT(!ctx->ssl); - DEBUGASSERT(!ctx->sslctx); - ctx->sslctx = quic_ssl_ctx(data); - if(!ctx->sslctx) - return CURLE_QUIC_CONNECT_ERROR; - ctx->ssl = SSL_new(ctx->sslctx); - if(!ctx->ssl) - return CURLE_QUIC_CONNECT_ERROR; - - SSL_set_app_data(ctx->ssl, cf); - SSL_set_tlsext_host_name(ctx->ssl, cf->conn->host.name); - - result = Curl_rand(data, ctx->scid, sizeof(ctx->scid)); - if(result) - return result; - - Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, - &sockaddr, NULL, NULL, NULL, NULL); - ctx->q.local_addrlen = sizeof(ctx->q.local_addr); - rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, - &ctx->q.local_addrlen); - if(rv == -1) - return CURLE_QUIC_CONNECT_ERROR; - - ctx->qconn = quiche_conn_new_with_tls((const uint8_t *)ctx->scid, - sizeof(ctx->scid), NULL, 0, - (struct sockaddr *)&ctx->q.local_addr, - ctx->q.local_addrlen, - &sockaddr->sa_addr, sockaddr->addrlen, - ctx->cfg, ctx->ssl, false); - if(!ctx->qconn) { - failf(data, "can't create quiche connection"); - return CURLE_OUT_OF_MEMORY; - } - - /* Known to not work on Windows */ -#if !defined(WIN32) && defined(HAVE_QUICHE_CONN_SET_QLOG_FD) - { - int qfd; - (void)Curl_qlogdir(data, ctx->scid, sizeof(ctx->scid), &qfd); - if(qfd != -1) - quiche_conn_set_qlog_fd(ctx->qconn, qfd, - "qlog title", "curl qlog"); - } -#endif - - result = cf_flush_egress(cf, data); - if(result) - return result; - - { - unsigned char alpn_protocols[] = QUICHE_H3_APPLICATION_PROTOCOL; - unsigned alpn_len, offset = 0; - - /* Replace each ALPN length prefix by a comma. */ - while(offset < sizeof(alpn_protocols) - 1) { - alpn_len = alpn_protocols[offset]; - alpn_protocols[offset] = ','; - offset += 1 + alpn_len; - } - - DEBUGF(LOG_CF(data, cf, "Sent QUIC client Initial, ALPN: %s", - alpn_protocols + 1)); - } - - return CURLE_OK; -} - -static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking, bool *done) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - struct curltime now; - - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } - - /* Connect the UDP filter first */ - if(!cf->next->connected) { - result = Curl_conn_cf_connect(cf->next, data, blocking, done); - if(result || !*done) - return result; - } - - *done = FALSE; - now = Curl_now(); - - if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) { - /* Not time yet to attempt the next connect */ - DEBUGF(LOG_CF(data, cf, "waiting for reconnect time")); - goto out; - } - - if(!ctx->qconn) { - result = cf_connect_start(cf, data); - if(result) - goto out; - ctx->started_at = now; - result = cf_flush_egress(cf, data); - /* we do not expect to be able to recv anything yet */ - goto out; - } - - result = cf_process_ingress(cf, data); - if(result) - goto out; - - result = cf_flush_egress(cf, data); - if(result) - goto out; - - if(quiche_conn_is_established(ctx->qconn)) { - DEBUGF(LOG_CF(data, cf, "handshake complete after %dms", - (int)Curl_timediff(now, ctx->started_at))); - ctx->handshake_at = now; - result = cf_verify_peer(cf, data); - if(!result) { - DEBUGF(LOG_CF(data, cf, "peer verified")); - cf->connected = TRUE; - cf->conn->alpn = CURL_HTTP_VERSION_3; - *done = TRUE; - connkeep(cf->conn, "HTTP/3 default"); - } - } - else if(quiche_conn_is_draining(ctx->qconn)) { - /* When a QUIC server instance is shutting down, it may send us a - * CONNECTION_CLOSE right away. Our connection then enters the DRAINING - * state. - * This may be a stopping of the service or it may be that the server - * is reloading and a new instance will start serving soon. - * In any case, we tear down our socket and start over with a new one. - * We re-open the underlying UDP cf right now, but do not start - * connecting until called again. - */ - int reconn_delay_ms = 200; - - DEBUGF(LOG_CF(data, cf, "connect, remote closed, reconnect after %dms", - reconn_delay_ms)); - Curl_conn_cf_close(cf->next, data); - cf_quiche_ctx_clear(ctx); - result = Curl_conn_cf_connect(cf->next, data, FALSE, done); - if(!result && *done) { - *done = FALSE; - ctx->reconnect_at = Curl_now(); - ctx->reconnect_at.tv_usec += reconn_delay_ms * 1000; - Curl_expire(data, reconn_delay_ms, EXPIRE_QUIC); - result = CURLE_OK; - } - } - -out: -#ifndef CURL_DISABLE_VERBOSE_STRINGS - if(result && result != CURLE_AGAIN) { - const char *r_ip; - int r_port; - - Curl_cf_socket_peek(cf->next, data, NULL, NULL, - &r_ip, &r_port, NULL, NULL); - infof(data, "connect to %s port %u failed: %s", - r_ip, r_port, curl_easy_strerror(result)); - } -#endif - return result; -} - -static void cf_quiche_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - - if(ctx) { - if(ctx->qconn) { - (void)quiche_conn_close(ctx->qconn, TRUE, 0, NULL, 0); - /* flushing the egress is not a failsafe way to deliver all the - outstanding packets, but we also don't want to get stuck here... */ - (void)cf_flush_egress(cf, data); - } - cf_quiche_ctx_clear(ctx); - } -} - -static void cf_quiche_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - - (void)data; - cf_quiche_ctx_clear(ctx); - free(ctx); - cf->ctx = NULL; -} - -static CURLcode cf_quiche_query(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query, int *pres1, void *pres2) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - - switch(query) { - case CF_QUERY_MAX_CONCURRENT: { - uint64_t max_streams = CONN_INUSE(cf->conn); - if(!ctx->goaway) { - max_streams += quiche_conn_peer_streams_left_bidi(ctx->qconn); - } - *pres1 = (max_streams > INT_MAX)? INT_MAX : (int)max_streams; - DEBUGF(LOG_CF(data, cf, "query: MAX_CONCURRENT -> %d", *pres1)); - return CURLE_OK; - } - case CF_QUERY_CONNECT_REPLY_MS: - if(ctx->got_first_byte) { - timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at); - *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX; - } - else - *pres1 = -1; - return CURLE_OK; - case CF_QUERY_TIMER_CONNECT: { - struct curltime *when = pres2; - if(ctx->got_first_byte) - *when = ctx->first_byte_at; - return CURLE_OK; - } - case CF_QUERY_TIMER_APPCONNECT: { - struct curltime *when = pres2; - if(cf->connected) - *when = ctx->handshake_at; - return CURLE_OK; - } - default: - break; - } - return cf->next? - cf->next->cft->query(cf->next, data, query, pres1, pres2) : - CURLE_UNKNOWN_OPTION; -} - -static bool cf_quiche_conn_is_alive(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *input_pending) -{ - bool alive = TRUE; - - *input_pending = FALSE; - if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) - return FALSE; - - if(*input_pending) { - /* This happens before we've sent off a request and the connection is - not in use by any other transfer, there shouldn't be any data here, - only "protocol frames" */ - *input_pending = FALSE; - if(cf_process_ingress(cf, data)) - alive = FALSE; - else { - alive = TRUE; - } - } - - return alive; -} - -struct Curl_cftype Curl_cft_http3 = { - "HTTP/3", - CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX, - 0, - cf_quiche_destroy, - cf_quiche_connect, - cf_quiche_close, - Curl_cf_def_get_host, - cf_quiche_get_select_socks, - cf_quiche_data_pending, - cf_quiche_send, - cf_quiche_recv, - cf_quiche_data_event, - cf_quiche_conn_is_alive, - Curl_cf_def_conn_keep_alive, - cf_quiche_query, -}; - -CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai) -{ - struct cf_quiche_ctx *ctx = NULL; - struct Curl_cfilter *cf = NULL, *udp_cf = NULL; - CURLcode result; - - (void)data; - (void)conn; - ctx = calloc(sizeof(*ctx), 1); - if(!ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); - if(result) - goto out; - - result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC); - if(result) - goto out; - - udp_cf->conn = cf->conn; - udp_cf->sockindex = cf->sockindex; - cf->next = udp_cf; - -out: - *pcf = (!result)? cf : NULL; - if(result) { - if(udp_cf) - Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE); - Curl_safefree(cf); - Curl_safefree(ctx); - } - - return result; -} - -bool Curl_conn_is_quiche(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex) -{ - struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL; - - (void)data; - for(; cf; cf = cf->next) { - if(cf->cft == &Curl_cft_http3) - return TRUE; - if(cf->cft->flags & CF_TYPE_IP_CONNECT) - return FALSE; - } - return FALSE; -} - -#endif diff --git a/contrib/libs/curl/lib/vquic/curl_quiche.h b/contrib/libs/curl/lib/vquic/curl_quiche.h deleted file mode 100644 index df9467d226..0000000000 --- a/contrib/libs/curl/lib/vquic/curl_quiche.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef HEADER_CURL_VQUIC_CURL_QUICHE_H -#define HEADER_CURL_VQUIC_CURL_QUICHE_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifdef USE_QUICHE - -#error #include <quiche.h> -#include <openssl/ssl.h> - -struct Curl_cfilter; -struct Curl_easy; - -void Curl_quiche_ver(char *p, size_t len); - -CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai); - -bool Curl_conn_is_quiche(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex); - -#endif - -#endif /* HEADER_CURL_VQUIC_CURL_QUICHE_H */ diff --git a/contrib/libs/curl/lib/vquic/msh3.c b/contrib/libs/curl/lib/vquic/msh3.c new file mode 100644 index 0000000000..7a70ee57d4 --- /dev/null +++ b/contrib/libs/curl/lib/vquic/msh3.c @@ -0,0 +1,527 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_MSH3 + +#include "urldata.h" +#include "timeval.h" +#include "multiif.h" +#include "sendf.h" +#include "connect.h" +#include "h2h3.h" +#error #include "msh3.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* #define DEBUG_HTTP3 1 */ +#ifdef DEBUG_HTTP3 +#define H3BUGF(x) x +#else +#define H3BUGF(x) do { } while(0) +#endif + +#define MSH3_REQ_INIT_BUF_LEN 8192 + +static CURLcode msh3_do_it(struct Curl_easy *data, bool *done); +static int msh3_getsock(struct Curl_easy *data, + struct connectdata *conn, curl_socket_t *socks); +static CURLcode msh3_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead_connection); +static unsigned int msh3_conncheck(struct Curl_easy *data, + struct connectdata *conn, + unsigned int checks_to_perform); +static Curl_recv msh3_stream_recv; +static Curl_send msh3_stream_send; +static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request, + void *IfContext, + const MSH3_HEADER *Header); +static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request, + void *IfContext, uint32_t Length, + const uint8_t *Data); +static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext, + bool Aborted, uint64_t AbortError); +static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext); + +static const struct Curl_handler msh3_curl_handler_http3 = { + "HTTPS", /* scheme */ + ZERO_NULL, /* setup_connection */ + msh3_do_it, /* do_it */ + Curl_http_done, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + msh3_getsock, /* proto_getsock */ + msh3_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + msh3_getsock, /* perform_getsock */ + msh3_disconnect, /* disconnect */ + ZERO_NULL, /* readwrite */ + msh3_conncheck, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_HTTP, /* defport */ + CURLPROTO_HTTPS, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_SSL | PROTOPT_STREAM /* flags */ +}; + +static const MSH3_REQUEST_IF msh3_request_if = { + msh3_header_received, + msh3_data_received, + msh3_complete, + msh3_shutdown +}; + +void Curl_quic_ver(char *p, size_t len) +{ + uint32_t v[4]; + MsH3Version(v); + (void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]); +} + +CURLcode Curl_quic_connect(struct Curl_easy *data, + struct connectdata *conn, + curl_socket_t sockfd, + int sockindex, + const struct sockaddr *addr, + socklen_t addrlen) +{ + struct quicsocket *qs = &conn->hequic[sockindex]; + bool insecure = !conn->ssl_config.verifypeer; + memset(qs, 0, sizeof(*qs)); + + (void)sockfd; + (void)addr; /* TODO - Pass address along */ + (void)addrlen; + + H3BUGF(infof(data, "creating new api/connection")); + + qs->api = MsH3ApiOpen(); + if(!qs->api) { + failf(data, "can't create msh3 api"); + return CURLE_FAILED_INIT; + } + + qs->conn = MsH3ConnectionOpen(qs->api, + conn->host.name, + (uint16_t)conn->remote_port, + insecure); + if(!qs->conn) { + failf(data, "can't create msh3 connection"); + if(qs->api) { + MsH3ApiClose(qs->api); + } + return CURLE_FAILED_INIT; + } + + return CURLE_OK; +} + +CURLcode Curl_quic_is_connected(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + bool *connected) +{ + struct quicsocket *qs = &conn->hequic[sockindex]; + MSH3_CONNECTION_STATE state; + + state = MsH3ConnectionGetState(qs->conn, false); + if(state == MSH3_CONN_HANDSHAKE_FAILED || state == MSH3_CONN_DISCONNECTED) { + failf(data, "failed to connect, state=%u", (uint32_t)state); + return CURLE_COULDNT_CONNECT; + } + + if(state == MSH3_CONN_CONNECTED) { + H3BUGF(infof(data, "connection connected")); + *connected = true; + conn->quic = qs; + conn->recv[sockindex] = msh3_stream_recv; + conn->send[sockindex] = msh3_stream_send; + conn->handler = &msh3_curl_handler_http3; + conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ + conn->httpversion = 30; + conn->bundle->multiuse = BUNDLE_MULTIPLEX; + /* TODO - Clean up other happy-eyeballs connection(s)? */ + } + + return CURLE_OK; +} + +static int msh3_getsock(struct Curl_easy *data, + struct connectdata *conn, curl_socket_t *socks) +{ + struct HTTP *stream = data->req.p.http; + int bitmap = GETSOCK_BLANK; + + socks[0] = conn->sock[FIRSTSOCKET]; + + if(stream->recv_error) { + bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); + data->state.drain++; + } + else if(stream->recv_header_len || stream->recv_data_len) { + bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); + data->state.drain++; + } + + H3BUGF(infof(data, "msh3_getsock %u", (uint32_t)data->state.drain)); + + return bitmap; +} + +static CURLcode msh3_do_it(struct Curl_easy *data, bool *done) +{ + struct HTTP *stream = data->req.p.http; + H3BUGF(infof(data, "msh3_do_it")); + stream->recv_buf = malloc(MSH3_REQ_INIT_BUF_LEN); + if(!stream->recv_buf) { + return CURLE_OUT_OF_MEMORY; + } + stream->req = ZERO_NULL; + msh3_lock_initialize(&stream->recv_lock); + stream->recv_buf_alloc = MSH3_REQ_INIT_BUF_LEN; + stream->recv_header_len = 0; + stream->recv_header_complete = false; + stream->recv_data_len = 0; + stream->recv_data_complete = false; + stream->recv_error = CURLE_OK; + return Curl_http(data, done); +} + +static unsigned int msh3_conncheck(struct Curl_easy *data, + struct connectdata *conn, + unsigned int checks_to_perform) +{ + (void)data; + (void)conn; + (void)checks_to_perform; + H3BUGF(infof(data, "msh3_conncheck")); + return CONNRESULT_NONE; +} + +static void disconnect(struct quicsocket *qs) +{ + if(qs->conn) { + MsH3ConnectionClose(qs->conn); + qs->conn = ZERO_NULL; + } + if(qs->api) { + MsH3ApiClose(qs->api); + qs->api = ZERO_NULL; + } +} + +static CURLcode msh3_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool dead_connection) +{ + (void)data; + (void)dead_connection; + H3BUGF(infof(data, "disconnecting (msh3)")); + disconnect(conn->quic); + return CURLE_OK; +} + +void Curl_quic_disconnect(struct Curl_easy *data, struct connectdata *conn, + int tempindex) +{ + (void)data; + if(conn->transport == TRNSPRT_QUIC) { + H3BUGF(infof(data, "disconnecting QUIC index %u", tempindex)); + disconnect(&conn->hequic[tempindex]); + } +} + +/* Requires stream->recv_lock to be held */ +static bool msh3request_ensure_room(struct HTTP *stream, size_t len) +{ + uint8_t *new_recv_buf; + const size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len; + if(cur_recv_len + len > stream->recv_buf_alloc) { + size_t new_recv_buf_alloc_len = stream->recv_buf_alloc; + do { + new_recv_buf_alloc_len <<= 1; /* TODO - handle overflow */ + } while(cur_recv_len + len > new_recv_buf_alloc_len); + new_recv_buf = malloc(new_recv_buf_alloc_len); + if(!new_recv_buf) { + return false; + } + if(cur_recv_len) { + memcpy(new_recv_buf, stream->recv_buf, cur_recv_len); + } + stream->recv_buf_alloc = new_recv_buf_alloc_len; + free(stream->recv_buf); + stream->recv_buf = new_recv_buf; + } + return true; +} + +static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request, + void *IfContext, + const MSH3_HEADER *Header) +{ + struct HTTP *stream = IfContext; + size_t total_len; + (void)Request; + + if(stream->recv_header_complete) { + H3BUGF(printf("* ignoring header after data\n")); + return; + } + + msh3_lock_acquire(&stream->recv_lock); + + if((Header->NameLength == 7) && + !strncmp(H2H3_PSEUDO_STATUS, (char *)Header->Name, 7)) { + total_len = 9 + Header->ValueLength; + if(!msh3request_ensure_room(stream, total_len)) { + /* TODO - handle error */ + goto release_lock; + } + msnprintf((char *)stream->recv_buf + stream->recv_header_len, + stream->recv_buf_alloc - stream->recv_header_len, + "HTTP/3 %.*s\n", (int)Header->ValueLength, Header->Value); + } + else { + total_len = Header->NameLength + 4 + Header->ValueLength; + if(!msh3request_ensure_room(stream, total_len)) { + /* TODO - handle error */ + goto release_lock; + } + msnprintf((char *)stream->recv_buf + stream->recv_header_len, + stream->recv_buf_alloc - stream->recv_header_len, + "%.*s: %.*s\n", + (int)Header->NameLength, Header->Name, + (int)Header->ValueLength, Header->Value); + } + + stream->recv_header_len += total_len - 1; /* don't include null-terminator */ + +release_lock: + msh3_lock_release(&stream->recv_lock); +} + +static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request, + void *IfContext, uint32_t Length, + const uint8_t *Data) +{ + struct HTTP *stream = IfContext; + size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len; + (void)Request; + H3BUGF(printf("* msh3_data_received %u. %zu buffered, %zu allocated\n", + Length, cur_recv_len, stream->recv_buf_alloc)); + msh3_lock_acquire(&stream->recv_lock); + if(!stream->recv_header_complete) { + H3BUGF(printf("* Headers complete!\n")); + if(!msh3request_ensure_room(stream, 2)) { + /* TODO - handle error */ + goto release_lock; + } + stream->recv_buf[stream->recv_header_len++] = '\r'; + stream->recv_buf[stream->recv_header_len++] = '\n'; + stream->recv_header_complete = true; + cur_recv_len += 2; + } + if(!msh3request_ensure_room(stream, Length)) { + /* TODO - handle error */ + goto release_lock; + } + memcpy(stream->recv_buf + cur_recv_len, Data, Length); + stream->recv_data_len += (size_t)Length; +release_lock: + msh3_lock_release(&stream->recv_lock); +} + +static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext, + bool Aborted, uint64_t AbortError) +{ + struct HTTP *stream = IfContext; + (void)Request; + (void)AbortError; + H3BUGF(printf("* msh3_complete, aborted=%s\n", Aborted ? "true" : "false")); + msh3_lock_acquire(&stream->recv_lock); + if(Aborted) { + stream->recv_error = CURLE_HTTP3; /* TODO - how do we pass AbortError? */ + } + stream->recv_header_complete = true; + stream->recv_data_complete = true; + msh3_lock_release(&stream->recv_lock); +} + +static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext) +{ + struct HTTP *stream = IfContext; + (void)Request; + (void)stream; +} + +static ssize_t msh3_stream_send(struct Curl_easy *data, + int sockindex, + const void *mem, + size_t len, + CURLcode *curlcode) +{ + struct connectdata *conn = data->conn; + struct HTTP *stream = data->req.p.http; + struct quicsocket *qs = conn->quic; + struct h2h3req *hreq; + + (void)sockindex; + /* Sizes must match for cast below to work" */ + DEBUGASSERT(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo)); + + H3BUGF(infof(data, "msh3_stream_send %zu", len)); + + if(!stream->req) { + *curlcode = Curl_pseudo_headers(data, mem, len, &hreq); + if(*curlcode) { + failf(data, "Curl_pseudo_headers failed"); + return -1; + } + H3BUGF(infof(data, "starting request with %zu headers", hreq->entries)); + stream->req = MsH3RequestOpen(qs->conn, &msh3_request_if, stream, + (MSH3_HEADER*)hreq->header, hreq->entries); + Curl_pseudo_free(hreq); + if(!stream->req) { + failf(data, "request open failed"); + *curlcode = CURLE_SEND_ERROR; + return -1; + } + *curlcode = CURLE_OK; + return len; + } + H3BUGF(infof(data, "send %zd body bytes on request %p", len, + (void *)stream->req)); + *curlcode = CURLE_SEND_ERROR; + return -1; +} + +static ssize_t msh3_stream_recv(struct Curl_easy *data, + int sockindex, + char *buf, + size_t buffersize, + CURLcode *curlcode) +{ + struct HTTP *stream = data->req.p.http; + size_t outsize = 0; + (void)sockindex; + H3BUGF(infof(data, "msh3_stream_recv %zu", buffersize)); + + if(stream->recv_error) { + failf(data, "request aborted"); + *curlcode = stream->recv_error; + return -1; + } + + msh3_lock_acquire(&stream->recv_lock); + + if(stream->recv_header_len) { + outsize = buffersize; + if(stream->recv_header_len < outsize) { + outsize = stream->recv_header_len; + } + memcpy(buf, stream->recv_buf, outsize); + if(outsize < stream->recv_header_len + stream->recv_data_len) { + memmove(stream->recv_buf, stream->recv_buf + outsize, + stream->recv_header_len + stream->recv_data_len - outsize); + } + stream->recv_header_len -= outsize; + H3BUGF(infof(data, "returned %zu bytes of headers", outsize)); + } + else if(stream->recv_data_len) { + outsize = buffersize; + if(stream->recv_data_len < outsize) { + outsize = stream->recv_data_len; + } + memcpy(buf, stream->recv_buf, outsize); + if(outsize < stream->recv_data_len) { + memmove(stream->recv_buf, stream->recv_buf + outsize, + stream->recv_data_len - outsize); + } + stream->recv_data_len -= outsize; + H3BUGF(infof(data, "returned %zu bytes of data", outsize)); + } + else if(stream->recv_data_complete) { + H3BUGF(infof(data, "receive complete")); + } + + msh3_lock_release(&stream->recv_lock); + + return (ssize_t)outsize; +} + +CURLcode Curl_quic_done_sending(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + H3BUGF(infof(data, "Curl_quic_done_sending")); + if(conn->handler == &msh3_curl_handler_http3) { + struct HTTP *stream = data->req.p.http; + stream->upload_done = TRUE; + } + + return CURLE_OK; +} + +void Curl_quic_done(struct Curl_easy *data, bool premature) +{ + struct HTTP *stream = data->req.p.http; + (void)premature; + H3BUGF(infof(data, "Curl_quic_done")); + if(stream) { + if(stream->recv_buf) { + Curl_safefree(stream->recv_buf); + msh3_lock_uninitialize(&stream->recv_lock); + } + if(stream->req) { + MsH3RequestClose(stream->req); + stream->req = ZERO_NULL; + } + } +} + +bool Curl_quic_data_pending(const struct Curl_easy *data) +{ + struct HTTP *stream = data->req.p.http; + H3BUGF(infof((struct Curl_easy *)data, "Curl_quic_data_pending")); + return stream->recv_header_len || stream->recv_data_len; +} + +/* + * Called from transfer.c:Curl_readwrite when neither HTTP level read + * nor write is performed. It is a good place to handle timer expiry + * for QUIC transport. + */ +CURLcode Curl_quic_idle(struct Curl_easy *data) +{ + (void)data; + H3BUGF(infof(data, "Curl_quic_idle")); + return CURLE_OK; +} + +#endif /* USE_MSH3 */ diff --git a/contrib/libs/curl/lib/vquic/ngtcp2.c b/contrib/libs/curl/lib/vquic/ngtcp2.c new file mode 100644 index 0000000000..feabd5fa89 --- /dev/null +++ b/contrib/libs/curl/lib/vquic/ngtcp2.c @@ -0,0 +1,2254 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_NGTCP2 +#error #include <ngtcp2/ngtcp2.h> +#error #include <nghttp3/nghttp3.h> +#ifdef USE_OPENSSL +#include <openssl/err.h> +#ifdef OPENSSL_IS_BORINGSSL +#error #include <ngtcp2/ngtcp2_crypto_boringssl.h> +#else +#error #include <ngtcp2/ngtcp2_crypto_openssl.h> +#endif +#include "vtls/openssl.h" +#elif defined(USE_GNUTLS) +#error #include <ngtcp2/ngtcp2_crypto_gnutls.h> +#include "vtls/gtls.h" +#elif defined(USE_WOLFSSL) +#error #include <ngtcp2/ngtcp2_crypto_wolfssl.h> +#include "vtls/wolfssl.h" +#endif +#include "urldata.h" +#include "sendf.h" +#include "strdup.h" +#include "rand.h" +#error #include "ngtcp2.h" +#include "multiif.h" +#include "strcase.h" +#include "connect.h" +#include "strerror.h" +#include "dynbuf.h" +#error #include "vquic.h" +#include "h2h3.h" +#include "vtls/keylog.h" +#include "vtls/vtls.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* #define DEBUG_NGTCP2 */ +#ifdef CURLDEBUG +#define DEBUG_HTTP3 +#endif +#ifdef DEBUG_HTTP3 +#define H3BUGF(x) x +#else +#define H3BUGF(x) do { } while(0) +#endif + +#define H3_ALPN_H3_29 "\x5h3-29" +#define H3_ALPN_H3 "\x2h3" + +/* + * This holds outgoing HTTP/3 stream data that is used by nghttp3 until acked. + * It is used as a circular buffer. Add new bytes at the end until it reaches + * the far end, then start over at index 0 again. + */ + +#define H3_SEND_SIZE (256*1024) +struct h3out { + uint8_t buf[H3_SEND_SIZE]; + size_t used; /* number of bytes used in the buffer */ + size_t windex; /* index in the buffer where to start writing the next + data block */ +}; + +#define QUIC_MAX_STREAMS (256*1024) +#define QUIC_MAX_DATA (1*1024*1024) +#define QUIC_IDLE_TIMEOUT (60*NGTCP2_SECONDS) + +#ifdef USE_OPENSSL +#define QUIC_CIPHERS \ + "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \ + "POLY1305_SHA256:TLS_AES_128_CCM_SHA256" +#define QUIC_GROUPS "P-256:X25519:P-384:P-521" +#elif defined(USE_GNUTLS) +#define QUIC_PRIORITY \ + "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \ + "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \ + "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \ + "%DISABLE_TLS13_COMPAT_MODE" +#elif defined(USE_WOLFSSL) +#define QUIC_CIPHERS \ + "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \ + "POLY1305_SHA256:TLS_AES_128_CCM_SHA256" +#define QUIC_GROUPS "P-256:P-384:P-521" +#endif + +/* ngtcp2 default congestion controller does not perform pacing. Limit + the maximum packet burst to MAX_PKT_BURST packets. */ +#define MAX_PKT_BURST 10 + +static CURLcode ng_process_ingress(struct Curl_easy *data, + curl_socket_t sockfd, + struct quicsocket *qs); +static CURLcode ng_flush_egress(struct Curl_easy *data, int sockfd, + struct quicsocket *qs); +static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id, + uint64_t datalen, void *user_data, + void *stream_user_data); + +static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref) +{ + struct quicsocket *qs = conn_ref->user_data; + return qs->qconn; +} + +static ngtcp2_tstamp timestamp(void) +{ + struct curltime ct = Curl_now(); + return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS; +} + +#ifdef DEBUG_NGTCP2 +static void quic_printf(void *user_data, const char *fmt, ...) +{ + va_list ap; + (void)user_data; /* TODO, use this to do infof() instead long-term */ + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); +} +#endif + +static void qlog_callback(void *user_data, uint32_t flags, + const void *data, size_t datalen) +{ + struct quicsocket *qs = (struct quicsocket *)user_data; + (void)flags; + if(qs->qlogfd != -1) { + ssize_t rc = write(qs->qlogfd, data, datalen); + if(rc == -1) { + /* on write error, stop further write attempts */ + close(qs->qlogfd); + qs->qlogfd = -1; + } + } + +} + +static void quic_settings(struct quicsocket *qs, + uint64_t stream_buffer_size) +{ + ngtcp2_settings *s = &qs->settings; + ngtcp2_transport_params *t = &qs->transport_params; + ngtcp2_settings_default(s); + ngtcp2_transport_params_default(t); +#ifdef DEBUG_NGTCP2 + s->log_printf = quic_printf; +#else + s->log_printf = NULL; +#endif + s->initial_ts = timestamp(); + t->initial_max_stream_data_bidi_local = stream_buffer_size; + t->initial_max_stream_data_bidi_remote = QUIC_MAX_STREAMS; + t->initial_max_stream_data_uni = QUIC_MAX_STREAMS; + t->initial_max_data = QUIC_MAX_DATA; + t->initial_max_streams_bidi = 1; + t->initial_max_streams_uni = 3; + t->max_idle_timeout = QUIC_IDLE_TIMEOUT; + if(qs->qlogfd != -1) { + s->qlog.write = qlog_callback; + } +} + +#ifdef USE_OPENSSL +static void keylog_callback(const SSL *ssl, const char *line) +{ + (void)ssl; + Curl_tls_keylog_write_line(line); +} +#elif defined(USE_GNUTLS) +static int keylog_callback(gnutls_session_t session, const char *label, + const gnutls_datum_t *secret) +{ + gnutls_datum_t crandom; + gnutls_datum_t srandom; + + gnutls_session_get_random(session, &crandom, &srandom); + if(crandom.size != 32) { + return -1; + } + + Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size); + return 0; +} +#elif defined(USE_WOLFSSL) +#if defined(HAVE_SECRET_CALLBACK) +static void keylog_callback(const WOLFSSL *ssl, const char *line) +{ + (void)ssl; + Curl_tls_keylog_write_line(line); +} +#endif +#endif + +static int init_ngh3_conn(struct quicsocket *qs); + +#ifdef USE_OPENSSL +static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); + +#ifdef OPENSSL_IS_BORINGSSL + if(ngtcp2_crypto_boringssl_configure_client_context(ssl_ctx) != 0) { + failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed"); + return NULL; + } +#else + if(ngtcp2_crypto_openssl_configure_client_context(ssl_ctx) != 0) { + failf(data, "ngtcp2_crypto_openssl_configure_client_context failed"); + return NULL; + } +#endif + + SSL_CTX_set_default_verify_paths(ssl_ctx); + +#ifdef OPENSSL_IS_BORINGSSL + if(SSL_CTX_set1_curves_list(ssl_ctx, QUIC_GROUPS) != 1) { + failf(data, "SSL_CTX_set1_curves_list failed"); + return NULL; + } +#else + if(SSL_CTX_set_ciphersuites(ssl_ctx, QUIC_CIPHERS) != 1) { + char error_buffer[256]; + ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer)); + failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer); + return NULL; + } + + if(SSL_CTX_set1_groups_list(ssl_ctx, QUIC_GROUPS) != 1) { + failf(data, "SSL_CTX_set1_groups_list failed"); + return NULL; + } +#endif + + /* Open the file if a TLS or QUIC backend has not done this before. */ + Curl_tls_keylog_open(); + if(Curl_tls_keylog_enabled()) { + SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); + } + + if(conn->ssl_config.verifypeer) { + const char * const ssl_cafile = conn->ssl_config.CAfile; + const char * const ssl_capath = conn->ssl_config.CApath; + + if(ssl_cafile || ssl_capath) { + SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); + /* tell OpenSSL where to find CA certificates that are used to verify + the server's certificate. */ + if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate verify locations:" + " CAfile: %s CApath: %s", + ssl_cafile ? ssl_cafile : "none", + ssl_capath ? ssl_capath : "none"); + return NULL; + } + infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); + infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); + } +#ifdef CURL_CA_FALLBACK + else { + /* 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(ssl_ctx); + } +#endif + } + return ssl_ctx; +} + +static CURLcode quic_set_client_cert(struct Curl_easy *data, + struct quicsocket *qs) +{ + struct connectdata *conn = data->conn; + SSL_CTX *ssl_ctx = qs->sslctx; + char *const ssl_cert = SSL_SET_OPTION(primary.clientcert); + const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(primary.cert_blob); + const char *const ssl_cert_type = SSL_SET_OPTION(cert_type); + + if(ssl_cert || ssl_cert_blob || ssl_cert_type) { + return Curl_ossl_set_client_cert( + data, ssl_ctx, ssl_cert, ssl_cert_blob, ssl_cert_type, + SSL_SET_OPTION(key), SSL_SET_OPTION(key_blob), + SSL_SET_OPTION(key_type), SSL_SET_OPTION(key_passwd)); + } + + return CURLE_OK; +} + +/** SSL callbacks ***/ + +static int quic_init_ssl(struct quicsocket *qs) +{ + const uint8_t *alpn = NULL; + size_t alpnlen = 0; + /* this will need some attention when HTTPS proxy over QUIC get fixed */ + const char * const hostname = qs->conn->host.name; + + DEBUGASSERT(!qs->ssl); + qs->ssl = SSL_new(qs->sslctx); + + SSL_set_app_data(qs->ssl, &qs->conn_ref); + SSL_set_connect_state(qs->ssl); + SSL_set_quic_use_legacy_codepoint(qs->ssl, 0); + + alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3; + alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1; + if(alpn) + SSL_set_alpn_protos(qs->ssl, alpn, (int)alpnlen); + + /* set SNI */ + SSL_set_tlsext_host_name(qs->ssl, hostname); + return 0; +} +#elif defined(USE_GNUTLS) +static int quic_init_ssl(struct quicsocket *qs) +{ + gnutls_datum_t alpn[2]; + /* this will need some attention when HTTPS proxy over QUIC get fixed */ + const char * const hostname = qs->conn->host.name; + int rc; + + DEBUGASSERT(!qs->ssl); + + gnutls_init(&qs->ssl, GNUTLS_CLIENT); + gnutls_session_set_ptr(qs->ssl, &qs->conn_ref); + + if(ngtcp2_crypto_gnutls_configure_client_session(qs->ssl) != 0) { + H3BUGF(fprintf(stderr, + "ngtcp2_crypto_gnutls_configure_client_session failed\n")); + return 1; + } + + rc = gnutls_priority_set_direct(qs->ssl, QUIC_PRIORITY, NULL); + if(rc < 0) { + H3BUGF(fprintf(stderr, "gnutls_priority_set_direct failed: %s\n", + gnutls_strerror(rc))); + return 1; + } + + /* Open the file if a TLS or QUIC backend has not done this before. */ + Curl_tls_keylog_open(); + if(Curl_tls_keylog_enabled()) { + gnutls_session_set_keylog_function(qs->ssl, keylog_callback); + } + + if(qs->cred) + gnutls_certificate_free_credentials(qs->cred); + + rc = gnutls_certificate_allocate_credentials(&qs->cred); + if(rc < 0) { + H3BUGF(fprintf(stderr, + "gnutls_certificate_allocate_credentials failed: %s\n", + gnutls_strerror(rc))); + return 1; + } + + rc = gnutls_certificate_set_x509_system_trust(qs->cred); + if(rc < 0) { + H3BUGF(fprintf(stderr, + "gnutls_certificate_set_x509_system_trust failed: %s\n", + gnutls_strerror(rc))); + return 1; + } + + rc = gnutls_credentials_set(qs->ssl, GNUTLS_CRD_CERTIFICATE, qs->cred); + if(rc < 0) { + H3BUGF(fprintf(stderr, "gnutls_credentials_set failed: %s\n", + gnutls_strerror(rc))); + return 1; + } + + /* strip the first byte (the length) from NGHTTP3_ALPN_H3 */ + alpn[0].data = (unsigned char *)H3_ALPN_H3_29 + 1; + alpn[0].size = sizeof(H3_ALPN_H3_29) - 2; + alpn[1].data = (unsigned char *)H3_ALPN_H3 + 1; + alpn[1].size = sizeof(H3_ALPN_H3) - 2; + + gnutls_alpn_set_protocols(qs->ssl, alpn, 2, GNUTLS_ALPN_MANDATORY); + + /* set SNI */ + gnutls_server_name_set(qs->ssl, GNUTLS_NAME_DNS, hostname, strlen(hostname)); + return 0; +} +#elif defined(USE_WOLFSSL) + +static WOLFSSL_CTX *quic_ssl_ctx(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + WOLFSSL_CTX *ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); + + if(ngtcp2_crypto_wolfssl_configure_client_context(ssl_ctx) != 0) { + failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed"); + return NULL; + } + + wolfSSL_CTX_set_default_verify_paths(ssl_ctx); + + if(wolfSSL_CTX_set_cipher_list(ssl_ctx, QUIC_CIPHERS) != 1) { + char error_buffer[256]; + ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer)); + failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer); + return NULL; + } + + if(wolfSSL_CTX_set1_groups_list(ssl_ctx, (char *)QUIC_GROUPS) != 1) { + failf(data, "SSL_CTX_set1_groups_list failed"); + return NULL; + } + + /* Open the file if a TLS or QUIC backend has not done this before. */ + Curl_tls_keylog_open(); + if(Curl_tls_keylog_enabled()) { +#if defined(HAVE_SECRET_CALLBACK) + wolfSSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); +#else + failf(data, "wolfSSL was built without keylog callback"); + return NULL; +#endif + } + + if(conn->ssl_config.verifypeer) { + const char * const ssl_cafile = conn->ssl_config.CAfile; + const char * const ssl_capath = conn->ssl_config.CApath; + + if(ssl_cafile || ssl_capath) { + wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); + /* tell wolfSSL where to find CA certificates that are used to verify + the server's certificate. */ + if(!wolfSSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate verify locations:" + " CAfile: %s CApath: %s", + ssl_cafile ? ssl_cafile : "none", + ssl_capath ? ssl_capath : "none"); + return NULL; + } + infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); + infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); + } +#ifdef CURL_CA_FALLBACK + else { + /* verifying the peer without any CA certificates won't work so + use wolfssl's built-in default as fallback */ + wolfSSL_CTX_set_default_verify_paths(ssl_ctx); + } +#endif + } + else { + wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL); + } + + return ssl_ctx; +} + +/** SSL callbacks ***/ + +static int quic_init_ssl(struct quicsocket *qs) +{ + const uint8_t *alpn = NULL; + size_t alpnlen = 0; + /* this will need some attention when HTTPS proxy over QUIC get fixed */ + const char * const hostname = qs->conn->host.name; + + DEBUGASSERT(!qs->ssl); + qs->ssl = SSL_new(qs->sslctx); + + wolfSSL_set_app_data(qs->ssl, &qs->conn_ref); + wolfSSL_set_connect_state(qs->ssl); + wolfSSL_set_quic_use_legacy_codepoint(qs->ssl, 0); + + alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3; + alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1; + if(alpn) + wolfSSL_set_alpn_protos(qs->ssl, alpn, (int)alpnlen); + + /* set SNI */ + wolfSSL_UseSNI(qs->ssl, WOLFSSL_SNI_HOST_NAME, + hostname, (unsigned short)strlen(hostname)); + + return 0; +} +#endif /* defined(USE_WOLFSSL) */ + +static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data) +{ + (void)user_data; + (void)tconn; + return 0; +} + +static void extend_stream_window(ngtcp2_conn *tconn, + struct HTTP *stream) +{ + size_t thismuch = stream->unacked_window; + ngtcp2_conn_extend_max_stream_offset(tconn, stream->stream3_id, thismuch); + ngtcp2_conn_extend_max_offset(tconn, thismuch); + stream->unacked_window = 0; +} + + +static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, + int64_t stream_id, uint64_t offset, + const uint8_t *buf, size_t buflen, + void *user_data, void *stream_user_data) +{ + struct quicsocket *qs = (struct quicsocket *)user_data; + nghttp3_ssize nconsumed; + int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0; + (void)offset; + (void)stream_user_data; + + nconsumed = + nghttp3_conn_read_stream(qs->h3conn, stream_id, buf, buflen, fin); + if(nconsumed < 0) { + ngtcp2_connection_close_error_set_application_error( + &qs->last_error, nghttp3_err_infer_quic_app_error_code((int)nconsumed), + NULL, 0); + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + /* number of bytes inside buflen which consists of framing overhead + * including QPACK HEADERS. In other words, it does not consume payload of + * DATA frame. */ + ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed); + ngtcp2_conn_extend_max_offset(tconn, nconsumed); + + return 0; +} + +static int +cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id, + uint64_t offset, uint64_t datalen, void *user_data, + void *stream_user_data) +{ + struct quicsocket *qs = (struct quicsocket *)user_data; + int rv; + (void)stream_id; + (void)tconn; + (void)offset; + (void)datalen; + (void)stream_user_data; + + rv = nghttp3_conn_add_ack_offset(qs->h3conn, stream_id, datalen); + if(rv) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, + int64_t stream_id, uint64_t app_error_code, + void *user_data, void *stream_user_data) +{ + struct quicsocket *qs = (struct quicsocket *)user_data; + int rv; + (void)tconn; + (void)stream_user_data; + /* stream is closed... */ + + if(!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) { + app_error_code = NGHTTP3_H3_NO_ERROR; + } + + rv = nghttp3_conn_close_stream(qs->h3conn, stream_id, + app_error_code); + if(rv) { + ngtcp2_connection_close_error_set_application_error( + &qs->last_error, nghttp3_err_infer_quic_app_error_code(rv), NULL, 0); + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id, + uint64_t final_size, uint64_t app_error_code, + void *user_data, void *stream_user_data) +{ + struct quicsocket *qs = (struct quicsocket *)user_data; + int rv; + (void)tconn; + (void)final_size; + (void)app_error_code; + (void)stream_user_data; + + rv = nghttp3_conn_shutdown_stream_read(qs->h3conn, stream_id); + if(rv) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) +{ + struct quicsocket *qs = (struct quicsocket *)user_data; + int rv; + (void)tconn; + (void)app_error_code; + (void)stream_user_data; + + rv = nghttp3_conn_shutdown_stream_read(qs->h3conn, stream_id); + if(rv) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn, + uint64_t max_streams, + void *user_data) +{ + (void)tconn; + (void)max_streams; + (void)user_data; + + return 0; +} + +static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id, + uint64_t max_data, void *user_data, + void *stream_user_data) +{ + struct quicsocket *qs = (struct quicsocket *)user_data; + int rv; + (void)tconn; + (void)max_data; + (void)stream_user_data; + + rv = nghttp3_conn_unblock_stream(qs->h3conn, stream_id); + if(rv) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static void cb_rand(uint8_t *dest, size_t destlen, + const ngtcp2_rand_ctx *rand_ctx) +{ + CURLcode result; + (void)rand_ctx; + + result = Curl_rand(NULL, dest, destlen); + if(result) { + /* cb_rand is only used for non-cryptographic context. If Curl_rand + failed, just fill 0 and call it *random*. */ + memset(dest, 0, destlen); + } +} + +static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid, + uint8_t *token, size_t cidlen, + void *user_data) +{ + CURLcode result; + (void)tconn; + (void)user_data; + + result = Curl_rand(NULL, cid->data, cidlen); + if(result) + return NGTCP2_ERR_CALLBACK_FAILURE; + cid->datalen = cidlen; + + result = Curl_rand(NULL, token, NGTCP2_STATELESS_RESET_TOKENLEN); + if(result) + return NGTCP2_ERR_CALLBACK_FAILURE; + + return 0; +} + +static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_crypto_level level, + void *user_data) +{ + struct quicsocket *qs = (struct quicsocket *)user_data; + (void)tconn; + + if(level != NGTCP2_CRYPTO_LEVEL_APPLICATION) { + return 0; + } + + if(init_ngh3_conn(qs) != CURLE_OK) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static ngtcp2_callbacks ng_callbacks = { + ngtcp2_crypto_client_initial_cb, + NULL, /* recv_client_initial */ + ngtcp2_crypto_recv_crypto_data_cb, + cb_handshake_completed, + NULL, /* recv_version_negotiation */ + ngtcp2_crypto_encrypt_cb, + ngtcp2_crypto_decrypt_cb, + ngtcp2_crypto_hp_mask_cb, + cb_recv_stream_data, + cb_acked_stream_data_offset, + NULL, /* stream_open */ + cb_stream_close, + NULL, /* recv_stateless_reset */ + ngtcp2_crypto_recv_retry_cb, + cb_extend_max_local_streams_bidi, + NULL, /* extend_max_local_streams_uni */ + cb_rand, + cb_get_new_connection_id, + NULL, /* remove_connection_id */ + ngtcp2_crypto_update_key_cb, /* update_key */ + NULL, /* path_validation */ + NULL, /* select_preferred_addr */ + cb_stream_reset, + NULL, /* extend_max_remote_streams_bidi */ + NULL, /* extend_max_remote_streams_uni */ + cb_extend_max_stream_data, + NULL, /* dcid_status */ + NULL, /* handshake_confirmed */ + NULL, /* recv_new_token */ + ngtcp2_crypto_delete_crypto_aead_ctx_cb, + ngtcp2_crypto_delete_crypto_cipher_ctx_cb, + NULL, /* recv_datagram */ + NULL, /* ack_datagram */ + NULL, /* lost_datagram */ + ngtcp2_crypto_get_path_challenge_data_cb, + cb_stream_stop_sending, + NULL, /* version_negotiation */ + cb_recv_rx_key, + NULL, /* recv_tx_key */ + NULL, /* early_data_rejected */ +}; + +/* + * Might be called twice for happy eyeballs. + */ +CURLcode Curl_quic_connect(struct Curl_easy *data, + struct connectdata *conn, + curl_socket_t sockfd, + int sockindex, + const struct sockaddr *addr, + socklen_t addrlen) +{ + int rc; + int rv; + CURLcode result; + ngtcp2_path path; /* TODO: this must be initialized properly */ + struct quicsocket *qs = &conn->hequic[sockindex]; + char ipbuf[40]; + int port; + int qfd; + + if(qs->conn) + Curl_quic_disconnect(data, conn, sockindex); + qs->conn = conn; + + /* extract the used address as a string */ + if(!Curl_addr2string((struct sockaddr*)addr, addrlen, ipbuf, &port)) { + char buffer[STRERROR_LEN]; + failf(data, "ssrem inet_ntop() failed with errno %d: %s", + SOCKERRNO, Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + + infof(data, "Connect socket %d over QUIC to %s:%d", + sockfd, ipbuf, port); + + qs->version = NGTCP2_PROTO_VER_MAX; +#ifdef USE_OPENSSL + qs->sslctx = quic_ssl_ctx(data); + if(!qs->sslctx) + return CURLE_QUIC_CONNECT_ERROR; + + result = quic_set_client_cert(data, qs); + if(result) + return result; +#elif defined(USE_WOLFSSL) + qs->sslctx = quic_ssl_ctx(data); + if(!qs->sslctx) + return CURLE_QUIC_CONNECT_ERROR; +#endif + + if(quic_init_ssl(qs)) + return CURLE_QUIC_CONNECT_ERROR; + + qs->dcid.datalen = NGTCP2_MAX_CIDLEN; + result = Curl_rand(data, qs->dcid.data, NGTCP2_MAX_CIDLEN); + if(result) + return result; + + qs->scid.datalen = NGTCP2_MAX_CIDLEN; + result = Curl_rand(data, qs->scid.data, NGTCP2_MAX_CIDLEN); + if(result) + return result; + + (void)Curl_qlogdir(data, qs->scid.data, NGTCP2_MAX_CIDLEN, &qfd); + qs->qlogfd = qfd; /* -1 if failure above */ + quic_settings(qs, data->set.buffer_size); + + qs->local_addrlen = sizeof(qs->local_addr); + rv = getsockname(sockfd, (struct sockaddr *)&qs->local_addr, + &qs->local_addrlen); + if(rv == -1) + return CURLE_QUIC_CONNECT_ERROR; + + ngtcp2_addr_init(&path.local, (struct sockaddr *)&qs->local_addr, + qs->local_addrlen); + ngtcp2_addr_init(&path.remote, addr, addrlen); + + rc = ngtcp2_conn_client_new(&qs->qconn, &qs->dcid, &qs->scid, &path, + NGTCP2_PROTO_VER_V1, &ng_callbacks, + &qs->settings, &qs->transport_params, NULL, qs); + if(rc) + return CURLE_QUIC_CONNECT_ERROR; + + ngtcp2_conn_set_tls_native_handle(qs->qconn, qs->ssl); + + ngtcp2_connection_close_error_default(&qs->last_error); + +#if defined(__linux__) && defined(UDP_SEGMENT) && defined(HAVE_SENDMSG) + qs->no_gso = FALSE; +#else + qs->no_gso = TRUE; +#endif + + qs->num_blocked_pkt = 0; + qs->num_blocked_pkt_sent = 0; + memset(&qs->blocked_pkt, 0, sizeof(qs->blocked_pkt)); + + qs->pktbuflen = NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE * MAX_PKT_BURST; + qs->pktbuf = malloc(qs->pktbuflen); + if(!qs->pktbuf) { + ngtcp2_conn_del(qs->qconn); + qs->qconn = NULL; + return CURLE_OUT_OF_MEMORY; + } + + qs->conn_ref.get_conn = get_conn; + qs->conn_ref.user_data = qs; + + return CURLE_OK; +} + +/* + * Store ngtcp2 version info in this buffer. + */ +void Curl_quic_ver(char *p, size_t len) +{ + const ngtcp2_info *ng2 = ngtcp2_version(0); + const nghttp3_info *ht3 = nghttp3_version(0); + (void)msnprintf(p, len, "ngtcp2/%s nghttp3/%s", + ng2->version_str, ht3->version_str); +} + +static int ng_getsock(struct Curl_easy *data, struct connectdata *conn, + curl_socket_t *socks) +{ + struct SingleRequest *k = &data->req; + int bitmap = GETSOCK_BLANK; + struct HTTP *stream = data->req.p.http; + struct quicsocket *qs = conn->quic; + + socks[0] = conn->sock[FIRSTSOCKET]; + + /* in a HTTP/2 connection we can basically always get a frame so we should + always be ready for one */ + bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); + + /* we're still uploading or the HTTP/2 layer wants to send data */ + if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND && + (!stream->h3out || stream->h3out->used < H3_SEND_SIZE) && + ngtcp2_conn_get_cwnd_left(qs->qconn) && + ngtcp2_conn_get_max_data_left(qs->qconn) && + nghttp3_conn_is_stream_writable(qs->h3conn, stream->stream3_id)) + bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); + + return bitmap; +} + +static void qs_disconnect(struct quicsocket *qs) +{ + char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; + ngtcp2_tstamp ts; + ngtcp2_ssize rc; + + if(!qs->conn) /* already closed */ + return; + ts = timestamp(); + rc = ngtcp2_conn_write_connection_close(qs->qconn, NULL, /* path */ + NULL, /* pkt_info */ + (uint8_t *)buffer, sizeof(buffer), + &qs->last_error, ts); + if(rc > 0) { + while((send(qs->conn->sock[FIRSTSOCKET], buffer, rc, 0) == -1) && + SOCKERRNO == EINTR); + } + + qs->conn = NULL; + if(qs->qlogfd != -1) { + close(qs->qlogfd); + qs->qlogfd = -1; + } + if(qs->ssl) +#ifdef USE_OPENSSL + SSL_free(qs->ssl); +#elif defined(USE_GNUTLS) + gnutls_deinit(qs->ssl); +#elif defined(USE_WOLFSSL) + wolfSSL_free(qs->ssl); +#endif + qs->ssl = NULL; +#ifdef USE_GNUTLS + if(qs->cred) { + gnutls_certificate_free_credentials(qs->cred); + qs->cred = NULL; + } +#endif + free(qs->pktbuf); + nghttp3_conn_del(qs->h3conn); + ngtcp2_conn_del(qs->qconn); +#ifdef USE_OPENSSL + SSL_CTX_free(qs->sslctx); +#elif defined(USE_WOLFSSL) + wolfSSL_CTX_free(qs->sslctx); +#endif +} + +void Curl_quic_disconnect(struct Curl_easy *data, + struct connectdata *conn, + int tempindex) +{ + (void)data; + if(conn->transport == TRNSPRT_QUIC) + qs_disconnect(&conn->hequic[tempindex]); +} + +static CURLcode ng_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead_connection) +{ + (void)dead_connection; + Curl_quic_disconnect(data, conn, 0); + Curl_quic_disconnect(data, conn, 1); + return CURLE_OK; +} + +static unsigned int ng_conncheck(struct Curl_easy *data, + struct connectdata *conn, + unsigned int checks_to_perform) +{ + (void)data; + (void)conn; + (void)checks_to_perform; + return CONNRESULT_NONE; +} + +static const struct Curl_handler Curl_handler_http3 = { + "HTTPS", /* scheme */ + ZERO_NULL, /* setup_connection */ + Curl_http, /* do_it */ + Curl_http_done, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ng_getsock, /* proto_getsock */ + ng_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ng_getsock, /* perform_getsock */ + ng_disconnect, /* disconnect */ + ZERO_NULL, /* readwrite */ + ng_conncheck, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_HTTP, /* defport */ + CURLPROTO_HTTPS, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_SSL | PROTOPT_STREAM /* flags */ +}; + +static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) +{ + struct Curl_easy *data = stream_user_data; + struct HTTP *stream = data->req.p.http; + (void)conn; + (void)stream_id; + (void)app_error_code; + (void)user_data; + H3BUGF(infof(data, "cb_h3_stream_close CALLED")); + + stream->closed = TRUE; + stream->error3 = app_error_code; + Curl_expire(data, 0, EXPIRE_QUIC); + /* make sure that ngh3_stream_recv is called again to complete the transfer + even if there are no more packets to be received from the server. */ + data->state.drain = 1; + return 0; +} + +/* + * write_data() copies data to the stream's receive buffer. If not enough + * space is available in the receive buffer, it copies the rest to the + * stream's overflow buffer. + */ +static CURLcode write_data(struct HTTP *stream, const void *mem, size_t memlen) +{ + CURLcode result = CURLE_OK; + const char *buf = mem; + size_t ncopy = memlen; + /* copy as much as possible to the receive buffer */ + if(stream->len) { + size_t len = CURLMIN(ncopy, stream->len); + memcpy(stream->mem, buf, len); + stream->len -= len; + stream->memlen += len; + stream->mem += len; + buf += len; + ncopy -= len; + } + /* copy the rest to the overflow buffer */ + if(ncopy) + result = Curl_dyn_addn(&stream->overflow, buf, ncopy); + return result; +} + +static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream_id, + const uint8_t *buf, size_t buflen, + void *user_data, void *stream_user_data) +{ + struct Curl_easy *data = stream_user_data; + struct HTTP *stream = data->req.p.http; + CURLcode result = CURLE_OK; + (void)conn; + + result = write_data(stream, buf, buflen); + if(result) { + return -1; + } + stream->unacked_window += buflen; + (void)stream_id; + (void)user_data; + return 0; +} + +static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id, + size_t consumed, void *user_data, + void *stream_user_data) +{ + struct quicsocket *qs = user_data; + (void)conn; + (void)stream_user_data; + (void)stream_id; + + ngtcp2_conn_extend_max_stream_offset(qs->qconn, stream_id, consumed); + ngtcp2_conn_extend_max_offset(qs->qconn, consumed); + return 0; +} + +/* Decode HTTP status code. Returns -1 if no valid status code was + decoded. (duplicate from http2.c) */ +static int decode_status_code(const uint8_t *value, size_t len) +{ + int i; + int res; + + if(len != 3) { + return -1; + } + + res = 0; + + for(i = 0; i < 3; ++i) { + char c = value[i]; + + if(c < '0' || c > '9') { + return -1; + } + + res *= 10; + res += c - '0'; + } + + return res; +} + +static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, + int fin, void *user_data, void *stream_user_data) +{ + struct Curl_easy *data = stream_user_data; + struct HTTP *stream = data->req.p.http; + CURLcode result = CURLE_OK; + (void)conn; + (void)stream_id; + (void)user_data; + (void)fin; + + /* add a CRLF only if we've received some headers */ + if(stream->firstheader) { + result = write_data(stream, "\r\n", 2); + if(result) { + return -1; + } + } + + if(stream->status_code / 100 != 1) { + stream->bodystarted = TRUE; + } + return 0; +} + +static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, + int32_t token, nghttp3_rcbuf *name, + nghttp3_rcbuf *value, uint8_t flags, + void *user_data, void *stream_user_data) +{ + nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); + nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); + struct Curl_easy *data = stream_user_data; + struct HTTP *stream = data->req.p.http; + CURLcode result = CURLE_OK; + (void)conn; + (void)stream_id; + (void)token; + (void)flags; + (void)user_data; + + if(token == NGHTTP3_QPACK_TOKEN__STATUS) { + char line[14]; /* status line is always 13 characters long */ + size_t ncopy; + stream->status_code = decode_status_code(h3val.base, h3val.len); + DEBUGASSERT(stream->status_code != -1); + ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n", + stream->status_code); + result = write_data(stream, line, ncopy); + if(result) { + return -1; + } + } + else { + /* store as a HTTP1-style header */ + result = write_data(stream, h3name.base, h3name.len); + if(result) { + return -1; + } + result = write_data(stream, ": ", 2); + if(result) { + return -1; + } + result = write_data(stream, h3val.base, h3val.len); + if(result) { + return -1; + } + result = write_data(stream, "\r\n", 2); + if(result) { + return -1; + } + } + + stream->firstheader = TRUE; + return 0; +} + +static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) +{ + struct quicsocket *qs = user_data; + int rv; + (void)conn; + (void)stream_user_data; + + rv = ngtcp2_conn_shutdown_stream_read(qs->qconn, stream_id, app_error_code); + if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) { + struct quicsocket *qs = user_data; + int rv; + (void)conn; + (void)stream_user_data; + + rv = ngtcp2_conn_shutdown_stream_write(qs->qconn, stream_id, app_error_code); + if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static nghttp3_callbacks ngh3_callbacks = { + cb_h3_acked_stream_data, /* acked_stream_data */ + cb_h3_stream_close, + cb_h3_recv_data, + cb_h3_deferred_consume, + NULL, /* begin_headers */ + cb_h3_recv_header, + cb_h3_end_headers, + NULL, /* begin_trailers */ + cb_h3_recv_header, + NULL, /* end_trailers */ + cb_h3_stop_sending, + NULL, /* end_stream */ + cb_h3_reset_stream, + NULL /* shutdown */ +}; + +static int init_ngh3_conn(struct quicsocket *qs) +{ + CURLcode result; + int rc; + int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id; + + if(ngtcp2_conn_get_max_local_streams_uni(qs->qconn) < 3) { + return CURLE_QUIC_CONNECT_ERROR; + } + + nghttp3_settings_default(&qs->h3settings); + + rc = nghttp3_conn_client_new(&qs->h3conn, + &ngh3_callbacks, + &qs->h3settings, + nghttp3_mem_default(), + qs); + if(rc) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + + rc = ngtcp2_conn_open_uni_stream(qs->qconn, &ctrl_stream_id, NULL); + if(rc) { + result = CURLE_QUIC_CONNECT_ERROR; + goto fail; + } + + rc = nghttp3_conn_bind_control_stream(qs->h3conn, ctrl_stream_id); + if(rc) { + result = CURLE_QUIC_CONNECT_ERROR; + goto fail; + } + + rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_enc_stream_id, NULL); + if(rc) { + result = CURLE_QUIC_CONNECT_ERROR; + goto fail; + } + + rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_dec_stream_id, NULL); + if(rc) { + result = CURLE_QUIC_CONNECT_ERROR; + goto fail; + } + + rc = nghttp3_conn_bind_qpack_streams(qs->h3conn, qpack_enc_stream_id, + qpack_dec_stream_id); + if(rc) { + result = CURLE_QUIC_CONNECT_ERROR; + goto fail; + } + + return CURLE_OK; + fail: + + return result; +} + +static Curl_recv ngh3_stream_recv; +static Curl_send ngh3_stream_send; + +static size_t drain_overflow_buffer(struct HTTP *stream) +{ + size_t overlen = Curl_dyn_len(&stream->overflow); + size_t ncopy = CURLMIN(overlen, stream->len); + if(ncopy > 0) { + memcpy(stream->mem, Curl_dyn_ptr(&stream->overflow), ncopy); + stream->len -= ncopy; + stream->mem += ncopy; + stream->memlen += ncopy; + if(ncopy != overlen) + /* make the buffer only keep the tail */ + (void)Curl_dyn_tail(&stream->overflow, overlen - ncopy); + else + Curl_dyn_reset(&stream->overflow); + } + return ncopy; +} + +/* incoming data frames on the h3 stream */ +static ssize_t ngh3_stream_recv(struct Curl_easy *data, + int sockindex, + char *buf, + size_t buffersize, + CURLcode *curlcode) +{ + struct connectdata *conn = data->conn; + curl_socket_t sockfd = conn->sock[sockindex]; + struct HTTP *stream = data->req.p.http; + struct quicsocket *qs = conn->quic; + + if(!stream->memlen) { + /* remember where to store incoming data for this stream and how big the + buffer is */ + stream->mem = buf; + stream->len = buffersize; + } + /* else, there's data in the buffer already */ + + /* if there's data in the overflow buffer from a previous call, copy as much + as possible to the receive buffer before receiving more */ + drain_overflow_buffer(stream); + + if(ng_process_ingress(data, sockfd, qs)) { + *curlcode = CURLE_RECV_ERROR; + return -1; + } + if(ng_flush_egress(data, sockfd, qs)) { + *curlcode = CURLE_SEND_ERROR; + return -1; + } + + if(stream->memlen) { + ssize_t memlen = stream->memlen; + /* data arrived */ + *curlcode = CURLE_OK; + /* reset to allow more data to come */ + stream->memlen = 0; + stream->mem = buf; + stream->len = buffersize; + /* extend the stream window with the data we're consuming and send out + any additional packets to tell the server that we can receive more */ + extend_stream_window(qs->qconn, stream); + if(ng_flush_egress(data, sockfd, qs)) { + *curlcode = CURLE_SEND_ERROR; + return -1; + } + return memlen; + } + + if(stream->closed) { + if(stream->error3 != NGHTTP3_H3_NO_ERROR) { + failf(data, + "HTTP/3 stream %" PRId64 " was not closed cleanly: (err %" PRIu64 + ")", + stream->stream3_id, stream->error3); + *curlcode = CURLE_HTTP3; + return -1; + } + + if(!stream->bodystarted) { + failf(data, + "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting" + " all response header fields, treated as error", + stream->stream3_id); + *curlcode = CURLE_HTTP3; + return -1; + } + + *curlcode = CURLE_OK; + return 0; + } + + infof(data, "ngh3_stream_recv returns 0 bytes and EAGAIN"); + *curlcode = CURLE_AGAIN; + return -1; +} + +/* this amount of data has now been acked on this stream */ +static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id, + uint64_t datalen, void *user_data, + void *stream_user_data) +{ + struct Curl_easy *data = stream_user_data; + struct HTTP *stream = data->req.p.http; + (void)user_data; + + if(!data->set.postfields) { + stream->h3out->used -= datalen; + H3BUGF(infof(data, + "cb_h3_acked_stream_data, %zd bytes, %zd left unacked", + datalen, stream->h3out->used)); + DEBUGASSERT(stream->h3out->used < H3_SEND_SIZE); + + if(stream->h3out->used == 0) { + int rv = nghttp3_conn_resume_stream(conn, stream_id); + if(rv) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + } + } + return 0; +} + +static nghttp3_ssize cb_h3_readfunction(nghttp3_conn *conn, int64_t stream_id, + nghttp3_vec *vec, size_t veccnt, + uint32_t *pflags, void *user_data, + void *stream_user_data) +{ + struct Curl_easy *data = stream_user_data; + size_t nread; + struct HTTP *stream = data->req.p.http; + (void)conn; + (void)stream_id; + (void)user_data; + (void)veccnt; + + if(data->set.postfields) { + vec[0].base = data->set.postfields; + vec[0].len = data->state.infilesize; + *pflags = NGHTTP3_DATA_FLAG_EOF; + return 1; + } + + if(stream->upload_len && H3_SEND_SIZE <= stream->h3out->used) { + return NGHTTP3_ERR_WOULDBLOCK; + } + + nread = CURLMIN(stream->upload_len, H3_SEND_SIZE - stream->h3out->used); + if(nread > 0) { + /* nghttp3 wants us to hold on to the data until it tells us it is okay to + delete it. Append the data at the end of the h3out buffer. Since we can + only return consecutive data, copy the amount that fits and the next + part comes in next invoke. */ + struct h3out *out = stream->h3out; + if(nread + out->windex > H3_SEND_SIZE) + nread = H3_SEND_SIZE - out->windex; + + memcpy(&out->buf[out->windex], stream->upload_mem, nread); + + /* that's the chunk we return to nghttp3 */ + vec[0].base = &out->buf[out->windex]; + vec[0].len = nread; + + out->windex += nread; + out->used += nread; + + if(out->windex == H3_SEND_SIZE) + out->windex = 0; /* wrap */ + stream->upload_mem += nread; + stream->upload_len -= nread; + if(data->state.infilesize != -1) { + stream->upload_left -= nread; + if(!stream->upload_left) + *pflags = NGHTTP3_DATA_FLAG_EOF; + } + H3BUGF(infof(data, "cb_h3_readfunction %zd bytes%s (at %zd unacked)", + nread, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"", + out->used)); + } + if(stream->upload_done && !stream->upload_len && + (stream->upload_left <= 0)) { + H3BUGF(infof(data, "cb_h3_readfunction sets EOF")); + *pflags = NGHTTP3_DATA_FLAG_EOF; + return nread ? 1 : 0; + } + else if(!nread) { + return NGHTTP3_ERR_WOULDBLOCK; + } + return 1; +} + +/* Index where :authority header field will appear in request header + field list. */ +#define AUTHORITY_DST_IDX 3 + +static CURLcode http_request(struct Curl_easy *data, const void *mem, + size_t len) +{ + struct connectdata *conn = data->conn; + struct HTTP *stream = data->req.p.http; + size_t nheader; + struct quicsocket *qs = conn->quic; + CURLcode result = CURLE_OK; + nghttp3_nv *nva = NULL; + int64_t stream3_id; + int rc; + struct h3out *h3out = NULL; + struct h2h3req *hreq = NULL; + + rc = ngtcp2_conn_open_bidi_stream(qs->qconn, &stream3_id, NULL); + if(rc) { + failf(data, "can get bidi streams"); + result = CURLE_SEND_ERROR; + goto fail; + } + + stream->stream3_id = stream3_id; + stream->h3req = TRUE; /* senf off! */ + Curl_dyn_init(&stream->overflow, CURL_MAX_READ_SIZE); + + result = Curl_pseudo_headers(data, mem, len, &hreq); + if(result) + goto fail; + nheader = hreq->entries; + + nva = malloc(sizeof(nghttp3_nv) * nheader); + if(!nva) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + else { + unsigned int i; + for(i = 0; i < nheader; i++) { + nva[i].name = (unsigned char *)hreq->header[i].name; + nva[i].namelen = hreq->header[i].namelen; + nva[i].value = (unsigned char *)hreq->header[i].value; + nva[i].valuelen = hreq->header[i].valuelen; + nva[i].flags = NGHTTP3_NV_FLAG_NONE; + } + } + + switch(data->state.httpreq) { + case HTTPREQ_POST: + case HTTPREQ_POST_FORM: + case HTTPREQ_POST_MIME: + case HTTPREQ_PUT: { + nghttp3_data_reader data_reader; + if(data->state.infilesize != -1) + stream->upload_left = data->state.infilesize; + else + /* data sending without specifying the data amount up front */ + stream->upload_left = -1; /* unknown, but not zero */ + + data_reader.read_data = cb_h3_readfunction; + + h3out = calloc(sizeof(struct h3out), 1); + if(!h3out) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + stream->h3out = h3out; + + rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id, + nva, nheader, &data_reader, data); + if(rc) { + result = CURLE_SEND_ERROR; + goto fail; + } + break; + } + default: + stream->upload_left = 0; /* nothing left to send */ + rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id, + nva, nheader, NULL, data); + if(rc) { + result = CURLE_SEND_ERROR; + goto fail; + } + break; + } + + Curl_safefree(nva); + + infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)", + stream3_id, (void *)data); + + Curl_pseudo_free(hreq); + return CURLE_OK; + +fail: + free(nva); + Curl_pseudo_free(hreq); + return result; +} +static ssize_t ngh3_stream_send(struct Curl_easy *data, + int sockindex, + const void *mem, + size_t len, + CURLcode *curlcode) +{ + ssize_t sent = 0; + struct connectdata *conn = data->conn; + struct quicsocket *qs = conn->quic; + curl_socket_t sockfd = conn->sock[sockindex]; + struct HTTP *stream = data->req.p.http; + + if(stream->closed) { + *curlcode = CURLE_HTTP3; + return -1; + } + + if(!stream->h3req) { + CURLcode result = http_request(data, mem, len); + if(result) { + *curlcode = CURLE_SEND_ERROR; + return -1; + } + /* Assume that mem of length len only includes HTTP/1.1 style + header fields. In other words, it does not contain request + body. */ + sent = len; + } + else { + H3BUGF(infof(data, "ngh3_stream_send() wants to send %zd bytes", + len)); + if(!stream->upload_len) { + stream->upload_mem = mem; + stream->upload_len = len; + (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id); + } + else { + *curlcode = CURLE_AGAIN; + return -1; + } + } + + if(ng_flush_egress(data, sockfd, qs)) { + *curlcode = CURLE_SEND_ERROR; + return -1; + } + + /* Reset post upload buffer after resumed. */ + if(stream->upload_mem) { + if(data->set.postfields) { + sent = len; + } + else { + sent = len - stream->upload_len; + } + + stream->upload_mem = NULL; + stream->upload_len = 0; + + if(sent == 0) { + *curlcode = CURLE_AGAIN; + return -1; + } + } + + *curlcode = CURLE_OK; + return sent; +} + +static CURLcode ng_has_connected(struct Curl_easy *data, + struct connectdata *conn, int tempindex) +{ + CURLcode result = CURLE_OK; + conn->recv[FIRSTSOCKET] = ngh3_stream_recv; + conn->send[FIRSTSOCKET] = ngh3_stream_send; + conn->handler = &Curl_handler_http3; + conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ + conn->httpversion = 30; + conn->bundle->multiuse = BUNDLE_MULTIPLEX; + conn->quic = &conn->hequic[tempindex]; + + if(conn->ssl_config.verifyhost) { +#ifdef USE_OPENSSL + X509 *server_cert; + server_cert = SSL_get_peer_certificate(conn->quic->ssl); + if(!server_cert) { + return CURLE_PEER_FAILED_VERIFICATION; + } + result = Curl_ossl_verifyhost(data, conn, server_cert); + X509_free(server_cert); + if(result) + return result; + infof(data, "Verified certificate just fine"); +#elif defined(USE_GNUTLS) + result = Curl_gtls_verifyserver(data, conn, conn->quic->ssl, FIRSTSOCKET); +#elif defined(USE_WOLFSSL) + char *snihost = Curl_ssl_snihost(data, SSL_HOST_NAME(), NULL); + if(!snihost || + (wolfSSL_check_domain_name(conn->quic->ssl, snihost) == SSL_FAILURE)) + return CURLE_PEER_FAILED_VERIFICATION; + infof(data, "Verified certificate just fine"); +#endif + } + else + infof(data, "Skipped certificate verification"); +#ifdef USE_OPENSSL + if(data->set.ssl.certinfo) + /* asked to gather certificate info */ + (void)Curl_ossl_certchain(data, conn->quic->ssl); +#endif + return result; +} + +/* + * There can be multiple connection attempts going on in parallel. + */ +CURLcode Curl_quic_is_connected(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + bool *done) +{ + CURLcode result; + struct quicsocket *qs = &conn->hequic[sockindex]; + curl_socket_t sockfd = conn->tempsock[sockindex]; + + result = ng_process_ingress(data, sockfd, qs); + if(result) + goto error; + + result = ng_flush_egress(data, sockfd, qs); + if(result) + goto error; + + if(ngtcp2_conn_get_handshake_completed(qs->qconn)) { + result = ng_has_connected(data, conn, sockindex); + if(!result) + *done = TRUE; + } + + return result; + error: + (void)qs_disconnect(qs); + return result; + +} + +static CURLcode ng_process_ingress(struct Curl_easy *data, + curl_socket_t sockfd, + struct quicsocket *qs) +{ + ssize_t recvd; + int rv; + uint8_t buf[65536]; + size_t bufsize = sizeof(buf); + struct sockaddr_storage remote_addr; + socklen_t remote_addrlen; + ngtcp2_path path; + ngtcp2_tstamp ts = timestamp(); + ngtcp2_pkt_info pi = { 0 }; + + for(;;) { + remote_addrlen = sizeof(remote_addr); + while((recvd = recvfrom(sockfd, (char *)buf, bufsize, 0, + (struct sockaddr *)&remote_addr, + &remote_addrlen)) == -1 && + SOCKERRNO == EINTR) + ; + if(recvd == -1) { + if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) + break; + + failf(data, "ngtcp2: recvfrom() unexpectedly returned %zd", recvd); + return CURLE_RECV_ERROR; + } + + ngtcp2_addr_init(&path.local, (struct sockaddr *)&qs->local_addr, + qs->local_addrlen); + ngtcp2_addr_init(&path.remote, (struct sockaddr *)&remote_addr, + remote_addrlen); + + rv = ngtcp2_conn_read_pkt(qs->qconn, &path, &pi, buf, recvd, ts); + if(rv) { + if(!qs->last_error.error_code) { + if(rv == NGTCP2_ERR_CRYPTO) { + ngtcp2_connection_close_error_set_transport_error_tls_alert( + &qs->last_error, ngtcp2_conn_get_tls_alert(qs->qconn), NULL, 0); + } + else { + ngtcp2_connection_close_error_set_transport_error_liberr( + &qs->last_error, rv, NULL, 0); + } + } + + if(rv == NGTCP2_ERR_CRYPTO) + /* this is a "TLS problem", but a failed certificate verification + is a common reason for this */ + return CURLE_PEER_FAILED_VERIFICATION; + return CURLE_RECV_ERROR; + } + } + + return CURLE_OK; +} + +static CURLcode do_sendmsg(size_t *sent, struct Curl_easy *data, int sockfd, + struct quicsocket *qs, const uint8_t *pkt, + size_t pktlen, size_t gsolen); + +static CURLcode send_packet_no_gso(size_t *psent, struct Curl_easy *data, + int sockfd, struct quicsocket *qs, + const uint8_t *pkt, size_t pktlen, + size_t gsolen) +{ + const uint8_t *p, *end = pkt + pktlen; + size_t sent; + + *psent = 0; + + for(p = pkt; p < end; p += gsolen) { + size_t len = CURLMIN(gsolen, (size_t)(end - p)); + CURLcode curlcode = do_sendmsg(&sent, data, sockfd, qs, p, len, len); + if(curlcode != CURLE_OK) { + return curlcode; + } + *psent += sent; + } + + return CURLE_OK; +} + +static CURLcode do_sendmsg(size_t *psent, struct Curl_easy *data, int sockfd, + struct quicsocket *qs, const uint8_t *pkt, + size_t pktlen, size_t gsolen) +{ +#ifdef HAVE_SENDMSG + struct iovec msg_iov; + struct msghdr msg = {0}; + ssize_t sent; +#if defined(__linux__) && defined(UDP_SEGMENT) + uint8_t msg_ctrl[32]; + struct cmsghdr *cm; +#endif + + *psent = 0; + msg_iov.iov_base = (uint8_t *)pkt; + msg_iov.iov_len = pktlen; + msg.msg_iov = &msg_iov; + msg.msg_iovlen = 1; + +#if defined(__linux__) && defined(UDP_SEGMENT) + if(pktlen > gsolen) { + /* Only set this, when we need it. macOS, for example, + * does not seem to like a msg_control of length 0. */ + msg.msg_control = msg_ctrl; + assert(sizeof(msg_ctrl) >= CMSG_SPACE(sizeof(uint16_t))); + msg.msg_controllen = CMSG_SPACE(sizeof(uint16_t)); + cm = CMSG_FIRSTHDR(&msg); + cm->cmsg_level = SOL_UDP; + cm->cmsg_type = UDP_SEGMENT; + cm->cmsg_len = CMSG_LEN(sizeof(uint16_t)); + *(uint16_t *)(void *)CMSG_DATA(cm) = gsolen & 0xffff; + } +#endif + + + while((sent = sendmsg(sockfd, &msg, 0)) == -1 && SOCKERRNO == EINTR) + ; + + if(sent == -1) { + switch(SOCKERRNO) { + case EAGAIN: +#if EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif + return CURLE_AGAIN; + case EMSGSIZE: + /* UDP datagram is too large; caused by PMTUD. Just let it be lost. */ + break; + case EIO: + if(pktlen > gsolen) { + /* GSO failure */ + failf(data, "sendmsg() returned %zd (errno %d); disable GSO", sent, + SOCKERRNO); + qs->no_gso = TRUE; + return send_packet_no_gso(psent, data, sockfd, qs, pkt, pktlen, + gsolen); + } + /* FALLTHROUGH */ + default: + failf(data, "sendmsg() returned %zd (errno %d)", sent, SOCKERRNO); + return CURLE_SEND_ERROR; + } + } + else { + assert(pktlen == (size_t)sent); + } +#else + ssize_t sent; + (void)qs; + (void)gsolen; + + *psent = 0; + + while((sent = send(sockfd, (const char *)pkt, pktlen, 0)) == -1 && + SOCKERRNO == EINTR) + ; + + if(sent == -1) { + if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { + return CURLE_AGAIN; + } + else { + failf(data, "send() returned %zd (errno %d)", sent, SOCKERRNO); + if(SOCKERRNO != EMSGSIZE) { + return CURLE_SEND_ERROR; + } + /* UDP datagram is too large; caused by PMTUD. Just let it be + lost. */ + } + } +#endif + + *psent = pktlen; + + return CURLE_OK; +} + +static CURLcode send_packet(size_t *psent, struct Curl_easy *data, int sockfd, + struct quicsocket *qs, const uint8_t *pkt, + size_t pktlen, size_t gsolen) +{ + if(qs->no_gso && pktlen > gsolen) { + return send_packet_no_gso(psent, data, sockfd, qs, pkt, pktlen, gsolen); + } + + return do_sendmsg(psent, data, sockfd, qs, pkt, pktlen, gsolen); +} + +static void push_blocked_pkt(struct quicsocket *qs, const uint8_t *pkt, + size_t pktlen, size_t gsolen) +{ + struct blocked_pkt *blkpkt; + + assert(qs->num_blocked_pkt < + sizeof(qs->blocked_pkt) / sizeof(qs->blocked_pkt[0])); + + blkpkt = &qs->blocked_pkt[qs->num_blocked_pkt++]; + + blkpkt->pkt = pkt; + blkpkt->pktlen = pktlen; + blkpkt->gsolen = gsolen; +} + +static CURLcode send_blocked_pkt(struct Curl_easy *data, int sockfd, + struct quicsocket *qs) +{ + size_t sent; + CURLcode curlcode; + struct blocked_pkt *blkpkt; + + for(; qs->num_blocked_pkt_sent < qs->num_blocked_pkt; + ++qs->num_blocked_pkt_sent) { + blkpkt = &qs->blocked_pkt[qs->num_blocked_pkt_sent]; + curlcode = send_packet(&sent, data, sockfd, qs, blkpkt->pkt, + blkpkt->pktlen, blkpkt->gsolen); + + if(curlcode) { + if(curlcode == CURLE_AGAIN) { + blkpkt->pkt += sent; + blkpkt->pktlen -= sent; + } + return curlcode; + } + } + + qs->num_blocked_pkt = 0; + qs->num_blocked_pkt_sent = 0; + + return CURLE_OK; +} + +static CURLcode ng_flush_egress(struct Curl_easy *data, + int sockfd, + struct quicsocket *qs) +{ + int rv; + size_t sent; + ngtcp2_ssize outlen; + uint8_t *outpos = qs->pktbuf; + size_t max_udp_payload_size = + ngtcp2_conn_get_max_tx_udp_payload_size(qs->qconn); + size_t path_max_udp_payload_size = + ngtcp2_conn_get_path_max_tx_udp_payload_size(qs->qconn); + size_t max_pktcnt = + CURLMIN(MAX_PKT_BURST, qs->pktbuflen / max_udp_payload_size); + size_t pktcnt = 0; + size_t gsolen; + ngtcp2_path_storage ps; + ngtcp2_tstamp ts = timestamp(); + ngtcp2_tstamp expiry; + ngtcp2_duration timeout; + int64_t stream_id; + nghttp3_ssize veccnt; + int fin; + nghttp3_vec vec[16]; + ngtcp2_ssize ndatalen; + uint32_t flags; + CURLcode curlcode; + + rv = ngtcp2_conn_handle_expiry(qs->qconn, ts); + if(rv) { + failf(data, "ngtcp2_conn_handle_expiry returned error: %s", + ngtcp2_strerror(rv)); + ngtcp2_connection_close_error_set_transport_error_liberr(&qs->last_error, + rv, NULL, 0); + return CURLE_SEND_ERROR; + } + + if(qs->num_blocked_pkt) { + curlcode = send_blocked_pkt(data, sockfd, qs); + if(curlcode) { + if(curlcode == CURLE_AGAIN) { + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return curlcode; + } + } + + ngtcp2_path_storage_zero(&ps); + + for(;;) { + veccnt = 0; + stream_id = -1; + fin = 0; + + if(qs->h3conn && ngtcp2_conn_get_max_data_left(qs->qconn)) { + veccnt = nghttp3_conn_writev_stream(qs->h3conn, &stream_id, &fin, vec, + sizeof(vec) / sizeof(vec[0])); + if(veccnt < 0) { + failf(data, "nghttp3_conn_writev_stream returned error: %s", + nghttp3_strerror((int)veccnt)); + ngtcp2_connection_close_error_set_application_error( + &qs->last_error, + nghttp3_err_infer_quic_app_error_code((int)veccnt), NULL, 0); + return CURLE_SEND_ERROR; + } + } + + flags = NGTCP2_WRITE_STREAM_FLAG_MORE | + (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0); + outlen = ngtcp2_conn_writev_stream(qs->qconn, &ps.path, NULL, outpos, + max_udp_payload_size, + &ndatalen, flags, stream_id, + (const ngtcp2_vec *)vec, veccnt, ts); + if(outlen == 0) { + if(outpos != qs->pktbuf) { + curlcode = send_packet(&sent, data, sockfd, qs, qs->pktbuf, + outpos - qs->pktbuf, gsolen); + if(curlcode) { + if(curlcode == CURLE_AGAIN) { + push_blocked_pkt(qs, qs->pktbuf + sent, outpos - qs->pktbuf - sent, + gsolen); + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return curlcode; + } + } + + break; + } + if(outlen < 0) { + switch(outlen) { + case NGTCP2_ERR_STREAM_DATA_BLOCKED: + assert(ndatalen == -1); + nghttp3_conn_block_stream(qs->h3conn, stream_id); + continue; + case NGTCP2_ERR_STREAM_SHUT_WR: + assert(ndatalen == -1); + nghttp3_conn_shutdown_stream_write(qs->h3conn, stream_id); + continue; + case NGTCP2_ERR_WRITE_MORE: + assert(ndatalen >= 0); + rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id, ndatalen); + if(rv) { + failf(data, "nghttp3_conn_add_write_offset returned error: %s\n", + nghttp3_strerror(rv)); + return CURLE_SEND_ERROR; + } + continue; + default: + assert(ndatalen == -1); + failf(data, "ngtcp2_conn_writev_stream returned error: %s", + ngtcp2_strerror((int)outlen)); + ngtcp2_connection_close_error_set_transport_error_liberr( + &qs->last_error, (int)outlen, NULL, 0); + return CURLE_SEND_ERROR; + } + } + else if(ndatalen >= 0) { + rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id, ndatalen); + if(rv) { + failf(data, "nghttp3_conn_add_write_offset returned error: %s\n", + nghttp3_strerror(rv)); + return CURLE_SEND_ERROR; + } + } + + outpos += outlen; + + if(pktcnt == 0) { + gsolen = outlen; + } + else if((size_t)outlen > gsolen || + (gsolen > path_max_udp_payload_size && + (size_t)outlen != gsolen)) { + /* Packet larger than path_max_udp_payload_size is PMTUD probe + packet and it might not be sent because of EMSGSIZE. Send + them separately to minimize the loss. */ + curlcode = send_packet(&sent, data, sockfd, qs, qs->pktbuf, + outpos - outlen - qs->pktbuf, gsolen); + if(curlcode) { + if(curlcode == CURLE_AGAIN) { + push_blocked_pkt(qs, qs->pktbuf + sent, + outpos - outlen - qs->pktbuf - sent, gsolen); + push_blocked_pkt(qs, outpos - outlen, outlen, outlen); + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return curlcode; + } + curlcode = send_packet(&sent, data, sockfd, qs, outpos - outlen, outlen, + outlen); + if(curlcode) { + if(curlcode == CURLE_AGAIN) { + assert(0 == sent); + push_blocked_pkt(qs, outpos - outlen, outlen, outlen); + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return curlcode; + } + + pktcnt = 0; + outpos = qs->pktbuf; + continue; + } + + if(++pktcnt >= max_pktcnt || (size_t)outlen < gsolen) { + curlcode = send_packet(&sent, data, sockfd, qs, qs->pktbuf, + outpos - qs->pktbuf, gsolen); + if(curlcode) { + if(curlcode == CURLE_AGAIN) { + push_blocked_pkt(qs, qs->pktbuf + sent, outpos - qs->pktbuf - sent, + gsolen); + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return curlcode; + } + + pktcnt = 0; + outpos = qs->pktbuf; + } + } + + expiry = ngtcp2_conn_get_expiry(qs->qconn); + if(expiry != UINT64_MAX) { + if(expiry <= ts) { + timeout = 0; + } + else { + timeout = expiry - ts; + if(timeout % NGTCP2_MILLISECONDS) { + timeout += NGTCP2_MILLISECONDS; + } + } + Curl_expire(data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC); + } + + return CURLE_OK; +} + +/* + * Called from transfer.c:done_sending when we stop HTTP/3 uploading. + */ +CURLcode Curl_quic_done_sending(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + DEBUGASSERT(conn); + if(conn->handler == &Curl_handler_http3) { + /* only for HTTP/3 transfers */ + struct HTTP *stream = data->req.p.http; + struct quicsocket *qs = conn->quic; + stream->upload_done = TRUE; + (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id); + } + + return CURLE_OK; +} + +/* + * Called from http.c:Curl_http_done when a request completes. + */ +void Curl_quic_done(struct Curl_easy *data, bool premature) +{ + (void)premature; + if(data->conn->handler == &Curl_handler_http3) { + /* only for HTTP/3 transfers */ + struct HTTP *stream = data->req.p.http; + Curl_dyn_free(&stream->overflow); + free(stream->h3out); + } +} + +/* + * Called from transfer.c:data_pending to know if we should keep looping + * to receive more data from the connection. + */ +bool Curl_quic_data_pending(const struct Curl_easy *data) +{ + /* We may have received more data than we're able to hold in the receive + buffer and allocated an overflow buffer. Since it's possible that + there's no more data coming on the socket, we need to keep reading + until the overflow buffer is empty. */ + const struct HTTP *stream = data->req.p.http; + return Curl_dyn_len(&stream->overflow) > 0; +} + +/* + * Called from transfer.c:Curl_readwrite when neither HTTP level read + * nor write is performed. It is a good place to handle timer expiry + * for QUIC transport. + */ +CURLcode Curl_quic_idle(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; + struct quicsocket *qs = conn->quic; + + if(ngtcp2_conn_get_expiry(qs->qconn) > timestamp()) { + return CURLE_OK; + } + + if(ng_flush_egress(data, sockfd, qs)) { + return CURLE_SEND_ERROR; + } + + return CURLE_OK; +} + +#endif diff --git a/contrib/libs/curl/lib/vquic/quiche.c b/contrib/libs/curl/lib/vquic/quiche.c new file mode 100644 index 0000000000..5a3d6fe378 --- /dev/null +++ b/contrib/libs/curl/lib/vquic/quiche.c @@ -0,0 +1,895 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_QUICHE +#error #include <quiche.h> +#include <openssl/err.h> +#include <openssl/ssl.h> +#include "urldata.h" +#include "sendf.h" +#include "strdup.h" +#include "rand.h" +#include "quic.h" +#include "strcase.h" +#include "multiif.h" +#include "connect.h" +#include "strerror.h" +#error #include "vquic.h" +#include "transfer.h" +#include "h2h3.h" +#include "vtls/openssl.h" +#include "vtls/keylog.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#define DEBUG_HTTP3 +/* #define DEBUG_QUICHE */ +#ifdef DEBUG_HTTP3 +#define H3BUGF(x) x +#else +#define H3BUGF(x) do { } while(0) +#endif + +#define QUIC_MAX_STREAMS (256*1024) +#define QUIC_MAX_DATA (1*1024*1024) +#define QUIC_IDLE_TIMEOUT (60 * 1000) /* milliseconds */ + +static CURLcode process_ingress(struct Curl_easy *data, + curl_socket_t sockfd, + struct quicsocket *qs); + +static CURLcode flush_egress(struct Curl_easy *data, curl_socket_t sockfd, + struct quicsocket *qs); + +static CURLcode http_request(struct Curl_easy *data, const void *mem, + size_t len); +static Curl_recv h3_stream_recv; +static Curl_send h3_stream_send; + +static int quiche_getsock(struct Curl_easy *data, + struct connectdata *conn, curl_socket_t *socks) +{ + struct SingleRequest *k = &data->req; + int bitmap = GETSOCK_BLANK; + + socks[0] = conn->sock[FIRSTSOCKET]; + + /* in a HTTP/2 connection we can basically always get a frame so we should + always be ready for one */ + bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); + + /* we're still uploading or the HTTP/2 layer wants to send data */ + if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) + bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); + + return bitmap; +} + +static CURLcode qs_disconnect(struct Curl_easy *data, + struct quicsocket *qs) +{ + DEBUGASSERT(qs); + if(qs->conn) { + (void)quiche_conn_close(qs->conn, TRUE, 0, NULL, 0); + /* flushing the egress is not a failsafe way to deliver all the + outstanding packets, but we also don't want to get stuck here... */ + (void)flush_egress(data, qs->sockfd, qs); + quiche_conn_free(qs->conn); + qs->conn = NULL; + } + if(qs->h3config) + quiche_h3_config_free(qs->h3config); + if(qs->h3c) + quiche_h3_conn_free(qs->h3c); + if(qs->cfg) { + quiche_config_free(qs->cfg); + qs->cfg = NULL; + } + return CURLE_OK; +} + +static CURLcode quiche_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead_connection) +{ + struct quicsocket *qs = conn->quic; + (void)dead_connection; + return qs_disconnect(data, qs); +} + +void Curl_quic_disconnect(struct Curl_easy *data, + struct connectdata *conn, + int tempindex) +{ + if(conn->transport == TRNSPRT_QUIC) + qs_disconnect(data, &conn->hequic[tempindex]); +} + +static unsigned int quiche_conncheck(struct Curl_easy *data, + struct connectdata *conn, + unsigned int checks_to_perform) +{ + (void)data; + (void)conn; + (void)checks_to_perform; + return CONNRESULT_NONE; +} + +static CURLcode quiche_do(struct Curl_easy *data, bool *done) +{ + struct HTTP *stream = data->req.p.http; + stream->h3req = FALSE; /* not sent */ + return Curl_http(data, done); +} + +static const struct Curl_handler Curl_handler_http3 = { + "HTTPS", /* scheme */ + ZERO_NULL, /* setup_connection */ + quiche_do, /* do_it */ + Curl_http_done, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + quiche_getsock, /* proto_getsock */ + quiche_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + quiche_getsock, /* perform_getsock */ + quiche_disconnect, /* disconnect */ + ZERO_NULL, /* readwrite */ + quiche_conncheck, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_HTTP, /* defport */ + CURLPROTO_HTTPS, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_SSL | PROTOPT_STREAM /* flags */ +}; + +#ifdef DEBUG_QUICHE +static void quiche_debug_log(const char *line, void *argp) +{ + (void)argp; + fprintf(stderr, "%s\n", line); +} +#endif + +static void keylog_callback(const SSL *ssl, const char *line) +{ + (void)ssl; + Curl_tls_keylog_write_line(line); +} + +static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data) +{ + SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); + + SSL_CTX_set_alpn_protos(ssl_ctx, + (const uint8_t *)QUICHE_H3_APPLICATION_PROTOCOL, + sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1); + + SSL_CTX_set_default_verify_paths(ssl_ctx); + + /* Open the file if a TLS or QUIC backend has not done this before. */ + Curl_tls_keylog_open(); + if(Curl_tls_keylog_enabled()) { + SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); + } + + { + struct connectdata *conn = data->conn; + if(conn->ssl_config.verifypeer) { + const char * const ssl_cafile = conn->ssl_config.CAfile; + const char * const ssl_capath = conn->ssl_config.CApath; + if(ssl_cafile || ssl_capath) { + SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); + /* tell OpenSSL where to find CA certificates that are used to verify + the server's certificate. */ + if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate verify locations:" + " CAfile: %s CApath: %s", + ssl_cafile ? ssl_cafile : "none", + ssl_capath ? ssl_capath : "none"); + return NULL; + } + infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); + infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); + } +#ifdef CURL_CA_FALLBACK + else { + /* 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(ssl_ctx); + } +#endif + } + } + return ssl_ctx; +} + +static int quic_init_ssl(struct quicsocket *qs, struct connectdata *conn) +{ + /* this will need some attention when HTTPS proxy over QUIC get fixed */ + const char * const hostname = conn->host.name; + + DEBUGASSERT(!qs->ssl); + qs->ssl = SSL_new(qs->sslctx); + + SSL_set_app_data(qs->ssl, qs); + + /* set SNI */ + SSL_set_tlsext_host_name(qs->ssl, hostname); + return 0; +} + + +CURLcode Curl_quic_connect(struct Curl_easy *data, + struct connectdata *conn, curl_socket_t sockfd, + int sockindex, + const struct sockaddr *addr, socklen_t addrlen) +{ + CURLcode result; + struct quicsocket *qs = &conn->hequic[sockindex]; + char ipbuf[40]; + int port; + int rv; + +#ifdef DEBUG_QUICHE + /* initialize debug log callback only once */ + static int debug_log_init = 0; + if(!debug_log_init) { + quiche_enable_debug_logging(quiche_debug_log, NULL); + debug_log_init = 1; + } +#endif + + (void)addr; + (void)addrlen; + + qs->sockfd = sockfd; + qs->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION); + if(!qs->cfg) { + failf(data, "can't create quiche config"); + return CURLE_FAILED_INIT; + } + + quiche_config_set_max_idle_timeout(qs->cfg, QUIC_IDLE_TIMEOUT); + quiche_config_set_initial_max_data(qs->cfg, QUIC_MAX_DATA); + quiche_config_set_initial_max_stream_data_bidi_local(qs->cfg, QUIC_MAX_DATA); + quiche_config_set_initial_max_stream_data_bidi_remote(qs->cfg, + QUIC_MAX_DATA); + quiche_config_set_initial_max_stream_data_uni(qs->cfg, QUIC_MAX_DATA); + quiche_config_set_initial_max_streams_bidi(qs->cfg, QUIC_MAX_STREAMS); + quiche_config_set_initial_max_streams_uni(qs->cfg, QUIC_MAX_STREAMS); + quiche_config_set_application_protos(qs->cfg, + (uint8_t *) + QUICHE_H3_APPLICATION_PROTOCOL, + sizeof(QUICHE_H3_APPLICATION_PROTOCOL) + - 1); + + qs->sslctx = quic_ssl_ctx(data); + if(!qs->sslctx) + return CURLE_QUIC_CONNECT_ERROR; + + if(quic_init_ssl(qs, conn)) + return CURLE_QUIC_CONNECT_ERROR; + + result = Curl_rand(data, qs->scid, sizeof(qs->scid)); + if(result) + return result; + + qs->local_addrlen = sizeof(qs->local_addr); + rv = getsockname(sockfd, (struct sockaddr *)&qs->local_addr, + &qs->local_addrlen); + if(rv == -1) + return CURLE_QUIC_CONNECT_ERROR; + + qs->conn = quiche_conn_new_with_tls((const uint8_t *) qs->scid, + sizeof(qs->scid), NULL, 0, + (struct sockaddr *)&qs->local_addr, + qs->local_addrlen, addr, addrlen, + qs->cfg, qs->ssl, false); + if(!qs->conn) { + failf(data, "can't create quiche connection"); + return CURLE_OUT_OF_MEMORY; + } + + /* Known to not work on Windows */ +#if !defined(WIN32) && defined(HAVE_QUICHE_CONN_SET_QLOG_FD) + { + int qfd; + (void)Curl_qlogdir(data, qs->scid, sizeof(qs->scid), &qfd); + if(qfd != -1) + quiche_conn_set_qlog_fd(qs->conn, qfd, + "qlog title", "curl qlog"); + } +#endif + + result = flush_egress(data, sockfd, qs); + if(result) + return result; + + /* extract the used address as a string */ + if(!Curl_addr2string((struct sockaddr*)addr, addrlen, ipbuf, &port)) { + char buffer[STRERROR_LEN]; + failf(data, "ssrem inet_ntop() failed with errno %d: %s", + SOCKERRNO, Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + + infof(data, "Connect socket %d over QUIC to %s:%ld", + sockfd, ipbuf, port); + + Curl_persistconninfo(data, conn, NULL, -1); + + /* for connection reuse purposes: */ + conn->ssl[FIRSTSOCKET].state = ssl_connection_complete; + + { + unsigned char alpn_protocols[] = QUICHE_H3_APPLICATION_PROTOCOL; + unsigned alpn_len, offset = 0; + + /* Replace each ALPN length prefix by a comma. */ + while(offset < sizeof(alpn_protocols) - 1) { + alpn_len = alpn_protocols[offset]; + alpn_protocols[offset] = ','; + offset += 1 + alpn_len; + } + + infof(data, "Sent QUIC client Initial, ALPN: %s", + alpn_protocols + 1); + } + + return CURLE_OK; +} + +static CURLcode quiche_has_connected(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + int tempindex) +{ + CURLcode result; + struct quicsocket *qs = conn->quic = &conn->hequic[tempindex]; + + conn->recv[sockindex] = h3_stream_recv; + conn->send[sockindex] = h3_stream_send; + conn->handler = &Curl_handler_http3; + conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ + conn->httpversion = 30; + conn->bundle->multiuse = BUNDLE_MULTIPLEX; + + if(conn->ssl_config.verifyhost) { + X509 *server_cert; + server_cert = SSL_get_peer_certificate(qs->ssl); + if(!server_cert) { + return CURLE_PEER_FAILED_VERIFICATION; + } + result = Curl_ossl_verifyhost(data, conn, server_cert); + X509_free(server_cert); + if(result) + return result; + infof(data, "Verified certificate just fine"); + } + else + infof(data, "Skipped certificate verification"); + + qs->h3config = quiche_h3_config_new(); + if(!qs->h3config) + return CURLE_OUT_OF_MEMORY; + + /* Create a new HTTP/3 connection on the QUIC connection. */ + qs->h3c = quiche_h3_conn_new_with_transport(qs->conn, qs->h3config); + if(!qs->h3c) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + if(conn->hequic[1-tempindex].cfg) { + qs = &conn->hequic[1-tempindex]; + quiche_config_free(qs->cfg); + quiche_conn_free(qs->conn); + qs->cfg = NULL; + qs->conn = NULL; + } + if(data->set.ssl.certinfo) + /* asked to gather certificate info */ + (void)Curl_ossl_certchain(data, qs->ssl); + + return CURLE_OK; + fail: + quiche_h3_config_free(qs->h3config); + quiche_h3_conn_free(qs->h3c); + return result; +} + +/* + * This function gets polled to check if this QUIC connection has connected. + */ +CURLcode Curl_quic_is_connected(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + bool *done) +{ + CURLcode result; + struct quicsocket *qs = &conn->hequic[sockindex]; + curl_socket_t sockfd = conn->tempsock[sockindex]; + + result = process_ingress(data, sockfd, qs); + if(result) + goto error; + + result = flush_egress(data, sockfd, qs); + if(result) + goto error; + + if(quiche_conn_is_established(qs->conn)) { + *done = TRUE; + result = quiche_has_connected(data, conn, 0, sockindex); + DEBUGF(infof(data, "quiche established connection")); + } + + return result; + error: + qs_disconnect(data, qs); + return result; +} + +static CURLcode process_ingress(struct Curl_easy *data, int sockfd, + struct quicsocket *qs) +{ + ssize_t recvd; + uint8_t *buf = (uint8_t *)data->state.buffer; + size_t bufsize = data->set.buffer_size; + struct sockaddr_storage from; + socklen_t from_len; + quiche_recv_info recv_info; + + DEBUGASSERT(qs->conn); + + /* in case the timeout expired */ + quiche_conn_on_timeout(qs->conn); + + do { + from_len = sizeof(from); + + recvd = recvfrom(sockfd, buf, bufsize, 0, + (struct sockaddr *)&from, &from_len); + + if((recvd < 0) && ((SOCKERRNO == EAGAIN) || (SOCKERRNO == EWOULDBLOCK))) + break; + + if(recvd < 0) { + failf(data, "quiche: recvfrom() unexpectedly returned %zd " + "(errno: %d, socket %d)", recvd, SOCKERRNO, sockfd); + return CURLE_RECV_ERROR; + } + + recv_info.from = (struct sockaddr *) &from; + recv_info.from_len = from_len; + recv_info.to = (struct sockaddr *) &qs->local_addr; + recv_info.to_len = qs->local_addrlen; + + recvd = quiche_conn_recv(qs->conn, buf, recvd, &recv_info); + if(recvd == QUICHE_ERR_DONE) + break; + + if(recvd < 0) { + if(QUICHE_ERR_TLS_FAIL == recvd) { + long verify_ok = SSL_get_verify_result(qs->ssl); + if(verify_ok != X509_V_OK) { + failf(data, "SSL certificate problem: %s", + X509_verify_cert_error_string(verify_ok)); + + return CURLE_PEER_FAILED_VERIFICATION; + } + } + + failf(data, "quiche_conn_recv() == %zd", recvd); + + return CURLE_RECV_ERROR; + } + } while(1); + + return CURLE_OK; +} + +/* + * flush_egress drains the buffers and sends off data. + * Calls failf() on errors. + */ +static CURLcode flush_egress(struct Curl_easy *data, int sockfd, + struct quicsocket *qs) +{ + ssize_t sent; + uint8_t out[1200]; + int64_t timeout_ns; + quiche_send_info send_info; + + do { + sent = quiche_conn_send(qs->conn, out, sizeof(out), &send_info); + if(sent == QUICHE_ERR_DONE) + break; + + if(sent < 0) { + failf(data, "quiche_conn_send returned %zd", sent); + return CURLE_SEND_ERROR; + } + + sent = send(sockfd, out, sent, 0); + if(sent < 0) { + failf(data, "send() returned %zd", sent); + return CURLE_SEND_ERROR; + } + } while(1); + + /* time until the next timeout event, as nanoseconds. */ + timeout_ns = quiche_conn_timeout_as_nanos(qs->conn); + if(timeout_ns) + /* expire uses milliseconds */ + Curl_expire(data, (timeout_ns + 999999) / 1000000, EXPIRE_QUIC); + + return CURLE_OK; +} + +struct h3h1header { + char *dest; + size_t destlen; /* left to use */ + size_t nlen; /* used */ +}; + +static int cb_each_header(uint8_t *name, size_t name_len, + uint8_t *value, size_t value_len, + void *argp) +{ + struct h3h1header *headers = (struct h3h1header *)argp; + size_t olen = 0; + + if((name_len == 7) && !strncmp(H2H3_PSEUDO_STATUS, (char *)name, 7)) { + msnprintf(headers->dest, + headers->destlen, "HTTP/3 %.*s\n", + (int) value_len, value); + } + else if(!headers->nlen) { + return CURLE_HTTP3; + } + else { + msnprintf(headers->dest, + headers->destlen, "%.*s: %.*s\n", + (int)name_len, name, (int) value_len, value); + } + olen = strlen(headers->dest); + headers->destlen -= olen; + headers->nlen += olen; + headers->dest += olen; + return 0; +} + +static ssize_t h3_stream_recv(struct Curl_easy *data, + int sockindex, + char *buf, + size_t buffersize, + CURLcode *curlcode) +{ + ssize_t recvd = -1; + ssize_t rcode; + struct connectdata *conn = data->conn; + struct quicsocket *qs = conn->quic; + curl_socket_t sockfd = conn->sock[sockindex]; + quiche_h3_event *ev; + int rc; + struct h3h1header headers; + struct HTTP *stream = data->req.p.http; + headers.dest = buf; + headers.destlen = buffersize; + headers.nlen = 0; + + if(process_ingress(data, sockfd, qs)) { + infof(data, "h3_stream_recv returns on ingress"); + *curlcode = CURLE_RECV_ERROR; + return -1; + } + + if(qs->h3_recving) { + /* body receiving state */ + rcode = quiche_h3_recv_body(qs->h3c, qs->conn, stream->stream3_id, + (unsigned char *)buf, buffersize); + if(rcode <= 0) { + recvd = -1; + qs->h3_recving = FALSE; + /* fall through into the while loop below */ + } + else + recvd = rcode; + } + + while(recvd < 0) { + int64_t s = quiche_h3_conn_poll(qs->h3c, qs->conn, &ev); + if(s < 0) + /* nothing more to do */ + break; + + if(s != stream->stream3_id) { + /* another transfer, ignore for now */ + infof(data, "Got h3 for stream %u, expects %u", + s, stream->stream3_id); + continue; + } + + switch(quiche_h3_event_type(ev)) { + case QUICHE_H3_EVENT_HEADERS: + rc = quiche_h3_event_for_each_header(ev, cb_each_header, &headers); + if(rc) { + *curlcode = rc; + failf(data, "Error in HTTP/3 response header"); + break; + } + recvd = headers.nlen; + break; + case QUICHE_H3_EVENT_DATA: + if(!stream->firstbody) { + /* add a header-body separator CRLF */ + buf[0] = '\r'; + buf[1] = '\n'; + buf += 2; + buffersize -= 2; + stream->firstbody = TRUE; + recvd = 2; /* two bytes already */ + } + else + recvd = 0; + rcode = quiche_h3_recv_body(qs->h3c, qs->conn, s, (unsigned char *)buf, + buffersize); + if(rcode <= 0) { + recvd = -1; + break; + } + qs->h3_recving = TRUE; + recvd += rcode; + break; + + case QUICHE_H3_EVENT_RESET: + streamclose(conn, "Stream reset"); + *curlcode = CURLE_PARTIAL_FILE; + return -1; + + case QUICHE_H3_EVENT_FINISHED: + streamclose(conn, "End of stream"); + recvd = 0; /* end of stream */ + break; + default: + break; + } + + quiche_h3_event_free(ev); + } + if(flush_egress(data, sockfd, qs)) { + *curlcode = CURLE_SEND_ERROR; + return -1; + } + + *curlcode = (-1 == recvd)? CURLE_AGAIN : CURLE_OK; + if(recvd >= 0) + /* Get this called again to drain the event queue */ + Curl_expire(data, 0, EXPIRE_QUIC); + + data->state.drain = (recvd >= 0) ? 1 : 0; + return recvd; +} + +static ssize_t h3_stream_send(struct Curl_easy *data, + int sockindex, + const void *mem, + size_t len, + CURLcode *curlcode) +{ + ssize_t sent; + struct connectdata *conn = data->conn; + struct quicsocket *qs = conn->quic; + curl_socket_t sockfd = conn->sock[sockindex]; + struct HTTP *stream = data->req.p.http; + + if(!stream->h3req) { + CURLcode result = http_request(data, mem, len); + if(result) { + *curlcode = CURLE_SEND_ERROR; + return -1; + } + sent = len; + } + else { + sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id, + (uint8_t *)mem, len, FALSE); + if(sent == QUICHE_H3_ERR_DONE) { + sent = 0; + } + else if(sent < 0) { + *curlcode = CURLE_SEND_ERROR; + return -1; + } + } + + if(flush_egress(data, sockfd, qs)) { + *curlcode = CURLE_SEND_ERROR; + return -1; + } + + *curlcode = CURLE_OK; + return sent; +} + +/* + * Store quiche version info in this buffer. + */ +void Curl_quic_ver(char *p, size_t len) +{ + (void)msnprintf(p, len, "quiche/%s", quiche_version()); +} + +/* Index where :authority header field will appear in request header + field list. */ +#define AUTHORITY_DST_IDX 3 + +static CURLcode http_request(struct Curl_easy *data, const void *mem, + size_t len) +{ + struct connectdata *conn = data->conn; + struct HTTP *stream = data->req.p.http; + size_t nheader; + int64_t stream3_id; + quiche_h3_header *nva = NULL; + struct quicsocket *qs = conn->quic; + CURLcode result = CURLE_OK; + struct h2h3req *hreq = NULL; + + stream->h3req = TRUE; /* senf off! */ + + result = Curl_pseudo_headers(data, mem, len, &hreq); + if(result) + goto fail; + nheader = hreq->entries; + + nva = malloc(sizeof(quiche_h3_header) * nheader); + if(!nva) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + else { + unsigned int i; + for(i = 0; i < nheader; i++) { + nva[i].name = (unsigned char *)hreq->header[i].name; + nva[i].name_len = hreq->header[i].namelen; + nva[i].value = (unsigned char *)hreq->header[i].value; + nva[i].value_len = hreq->header[i].valuelen; + } + } + + switch(data->state.httpreq) { + case HTTPREQ_POST: + case HTTPREQ_POST_FORM: + case HTTPREQ_POST_MIME: + case HTTPREQ_PUT: + if(data->state.infilesize != -1) + stream->upload_left = data->state.infilesize; + else + /* data sending without specifying the data amount up front */ + stream->upload_left = -1; /* unknown, but not zero */ + + stream3_id = quiche_h3_send_request(qs->h3c, qs->conn, nva, nheader, + stream->upload_left ? FALSE: TRUE); + if((stream3_id >= 0) && data->set.postfields) { + ssize_t sent = quiche_h3_send_body(qs->h3c, qs->conn, stream3_id, + (uint8_t *)data->set.postfields, + stream->upload_left, TRUE); + if(sent <= 0) { + failf(data, "quiche_h3_send_body failed"); + result = CURLE_SEND_ERROR; + } + stream->upload_left = 0; /* nothing left to send */ + } + break; + default: + stream3_id = quiche_h3_send_request(qs->h3c, qs->conn, nva, nheader, + TRUE); + break; + } + + Curl_safefree(nva); + + if(stream3_id < 0) { + H3BUGF(infof(data, "quiche_h3_send_request returned %d", + stream3_id)); + result = CURLE_SEND_ERROR; + goto fail; + } + + infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)", + stream3_id, (void *)data); + stream->stream3_id = stream3_id; + + Curl_pseudo_free(hreq); + return CURLE_OK; + +fail: + free(nva); + Curl_pseudo_free(hreq); + return result; +} + +/* + * Called from transfer.c:done_sending when we stop HTTP/3 uploading. + */ +CURLcode Curl_quic_done_sending(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + DEBUGASSERT(conn); + if(conn->handler == &Curl_handler_http3) { + /* only for HTTP/3 transfers */ + ssize_t sent; + struct HTTP *stream = data->req.p.http; + struct quicsocket *qs = conn->quic; + stream->upload_done = TRUE; + sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id, + NULL, 0, TRUE); + if(sent < 0) + return CURLE_SEND_ERROR; + } + + return CURLE_OK; +} + +/* + * Called from http.c:Curl_http_done when a request completes. + */ +void Curl_quic_done(struct Curl_easy *data, bool premature) +{ + (void)data; + (void)premature; +} + +/* + * Called from transfer.c:data_pending to know if we should keep looping + * to receive more data from the connection. + */ +bool Curl_quic_data_pending(const struct Curl_easy *data) +{ + (void)data; + return FALSE; +} + +/* + * Called from transfer.c:Curl_readwrite when neither HTTP level read + * nor write is performed. It is a good place to handle timer expiry + * for QUIC transport. + */ +CURLcode Curl_quic_idle(struct Curl_easy *data) +{ + (void)data; + return CURLE_OK; +} + +#endif diff --git a/contrib/libs/curl/lib/vquic/vquic.c b/contrib/libs/curl/lib/vquic/vquic.c index 399de0b2ca..7ad14414bc 100644 --- a/contrib/libs/curl/lib/vquic/vquic.c +++ b/contrib/libs/curl/lib/vquic/vquic.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -22,41 +22,17 @@ * ***************************************************************************/ -/* WIP, experimental: use recvmmsg() on linux - * we have no configure check, yet - * and also it is only available for _GNU_SOURCE, which - * we do not use otherwise. -#define HAVE_SENDMMSG - */ -#if defined(HAVE_SENDMMSG) -#define _GNU_SOURCE -#include <sys/socket.h> -#undef _GNU_SOURCE -#endif - #include "curl_setup.h" +#ifdef ENABLE_QUIC + #ifdef HAVE_FCNTL_H #include <fcntl.h> #endif #include "urldata.h" -#include "bufq.h" #include "dynbuf.h" -#include "cfilters.h" -#include "curl_log.h" -#include "curl_msh3.h" -#include "curl_ngtcp2.h" -#include "curl_quiche.h" -#include "vquic.h" -#include "vquic_int.h" - -/* The last 3 #include files should be in this order */ #include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - - -#ifdef ENABLE_QUIC +#error #include "vquic.h" #ifdef O_BINARY #define QLOGMODE O_WRONLY|O_CREAT|O_BINARY @@ -64,445 +40,6 @@ #define QLOGMODE O_WRONLY|O_CREAT #endif -#define NW_CHUNK_SIZE (64 * 1024) -#define NW_SEND_CHUNKS 2 - - -void Curl_quic_ver(char *p, size_t len) -{ -#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) - Curl_ngtcp2_ver(p, len); -#elif defined(USE_QUICHE) - Curl_quiche_ver(p, len); -#elif defined(USE_MSH3) - Curl_msh3_ver(p, len); -#endif -} - -CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx) -{ - Curl_bufq_init2(&qctx->sendbuf, NW_CHUNK_SIZE, NW_SEND_CHUNKS, - BUFQ_OPT_SOFT_LIMIT); -#if defined(__linux__) && defined(UDP_SEGMENT) && defined(HAVE_SENDMSG) - qctx->no_gso = FALSE; -#else - qctx->no_gso = TRUE; -#endif - - return CURLE_OK; -} - -void vquic_ctx_free(struct cf_quic_ctx *qctx) -{ - Curl_bufq_free(&qctx->sendbuf); -} - -static CURLcode send_packet_no_gso(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct cf_quic_ctx *qctx, - const uint8_t *pkt, size_t pktlen, - size_t gsolen, size_t *psent); - -static CURLcode do_sendmsg(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct cf_quic_ctx *qctx, - const uint8_t *pkt, size_t pktlen, size_t gsolen, - size_t *psent) -{ -#ifdef HAVE_SENDMSG - struct iovec msg_iov; - struct msghdr msg = {0}; - ssize_t sent; -#if defined(__linux__) && defined(UDP_SEGMENT) - uint8_t msg_ctrl[32]; - struct cmsghdr *cm; -#endif - - *psent = 0; - msg_iov.iov_base = (uint8_t *)pkt; - msg_iov.iov_len = pktlen; - msg.msg_iov = &msg_iov; - msg.msg_iovlen = 1; - -#if defined(__linux__) && defined(UDP_SEGMENT) - if(pktlen > gsolen) { - /* Only set this, when we need it. macOS, for example, - * does not seem to like a msg_control of length 0. */ - msg.msg_control = msg_ctrl; - assert(sizeof(msg_ctrl) >= CMSG_SPACE(sizeof(uint16_t))); - msg.msg_controllen = CMSG_SPACE(sizeof(uint16_t)); - cm = CMSG_FIRSTHDR(&msg); - cm->cmsg_level = SOL_UDP; - cm->cmsg_type = UDP_SEGMENT; - cm->cmsg_len = CMSG_LEN(sizeof(uint16_t)); - *(uint16_t *)(void *)CMSG_DATA(cm) = gsolen & 0xffff; - } -#endif - - - while((sent = sendmsg(qctx->sockfd, &msg, 0)) == -1 && SOCKERRNO == EINTR) - ; - - if(sent == -1) { - switch(SOCKERRNO) { - case EAGAIN: -#if EAGAIN != EWOULDBLOCK - case EWOULDBLOCK: -#endif - return CURLE_AGAIN; - case EMSGSIZE: - /* UDP datagram is too large; caused by PMTUD. Just let it be lost. */ - break; - case EIO: - if(pktlen > gsolen) { - /* GSO failure */ - failf(data, "sendmsg() returned %zd (errno %d); disable GSO", sent, - SOCKERRNO); - qctx->no_gso = TRUE; - return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent); - } - /* FALLTHROUGH */ - default: - failf(data, "sendmsg() returned %zd (errno %d)", sent, SOCKERRNO); - return CURLE_SEND_ERROR; - } - } - else { - assert(pktlen == (size_t)sent); - } -#else - ssize_t sent; - (void)gsolen; - - *psent = 0; - - while((sent = send(qctx->sockfd, - (const char *)pkt, (SEND_TYPE_ARG3)pktlen, 0)) == -1 && - SOCKERRNO == EINTR) - ; - - if(sent == -1) { - if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { - return CURLE_AGAIN; - } - else { - failf(data, "send() returned %zd (errno %d)", sent, SOCKERRNO); - if(SOCKERRNO != EMSGSIZE) { - return CURLE_SEND_ERROR; - } - /* UDP datagram is too large; caused by PMTUD. Just let it be - lost. */ - } - } -#endif - (void)cf; - *psent = pktlen; - - return CURLE_OK; -} - -static CURLcode send_packet_no_gso(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct cf_quic_ctx *qctx, - const uint8_t *pkt, size_t pktlen, - size_t gsolen, size_t *psent) -{ - const uint8_t *p, *end = pkt + pktlen; - size_t sent; - - *psent = 0; - - for(p = pkt; p < end; p += gsolen) { - size_t len = CURLMIN(gsolen, (size_t)(end - p)); - CURLcode curlcode = do_sendmsg(cf, data, qctx, p, len, len, &sent); - if(curlcode != CURLE_OK) { - return curlcode; - } - *psent += sent; - } - - return CURLE_OK; -} - -static CURLcode vquic_send_packets(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct cf_quic_ctx *qctx, - const uint8_t *pkt, size_t pktlen, - size_t gsolen, size_t *psent) -{ - if(qctx->no_gso && pktlen > gsolen) { - return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent); - } - - return do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent); -} - -CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data, - struct cf_quic_ctx *qctx) -{ - const unsigned char *buf; - size_t blen, sent; - CURLcode result; - size_t gsolen; - - while(Curl_bufq_peek(&qctx->sendbuf, &buf, &blen)) { - gsolen = qctx->gsolen; - if(qctx->split_len) { - gsolen = qctx->split_gsolen; - if(blen > qctx->split_len) - blen = qctx->split_len; - } - - DEBUGF(LOG_CF(data, cf, "vquic_send(len=%zu, gso=%zu)", - blen, gsolen)); - result = vquic_send_packets(cf, data, qctx, buf, blen, gsolen, &sent); - DEBUGF(LOG_CF(data, cf, "vquic_send(len=%zu, gso=%zu) -> %d, sent=%zu", - blen, gsolen, result, sent)); - if(result) { - if(result == CURLE_AGAIN) { - Curl_bufq_skip(&qctx->sendbuf, sent); - if(qctx->split_len) - qctx->split_len -= sent; - } - return result; - } - Curl_bufq_skip(&qctx->sendbuf, sent); - if(qctx->split_len) - qctx->split_len -= sent; - } - return CURLE_OK; -} - -CURLcode vquic_send(struct Curl_cfilter *cf, struct Curl_easy *data, - struct cf_quic_ctx *qctx, size_t gsolen) -{ - qctx->gsolen = gsolen; - return vquic_flush(cf, data, qctx); -} - -CURLcode vquic_send_tail_split(struct Curl_cfilter *cf, struct Curl_easy *data, - struct cf_quic_ctx *qctx, size_t gsolen, - size_t tail_len, size_t tail_gsolen) -{ - DEBUGASSERT(Curl_bufq_len(&qctx->sendbuf) > tail_len); - qctx->split_len = Curl_bufq_len(&qctx->sendbuf) - tail_len; - qctx->split_gsolen = gsolen; - qctx->gsolen = tail_gsolen; - DEBUGF(LOG_CF(data, cf, "vquic_send_tail_split: [%zu gso=%zu][%zu gso=%zu]", - qctx->split_len, qctx->split_gsolen, - tail_len, qctx->gsolen)); - return vquic_flush(cf, data, qctx); -} - -#ifdef HAVE_SENDMMSG -static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct cf_quic_ctx *qctx, - size_t max_pkts, - vquic_recv_pkt_cb *recv_cb, void *userp) -{ -#define MMSG_NUM 64 - struct iovec msg_iov[MMSG_NUM]; - struct mmsghdr mmsg[MMSG_NUM]; - uint8_t bufs[MMSG_NUM][2*1024]; - struct sockaddr_storage remote_addr[MMSG_NUM]; - size_t total_nread, pkts; - int mcount, i, n; - CURLcode result = CURLE_OK; - - DEBUGASSERT(max_pkts > 0); - pkts = 0; - total_nread = 0; - while(pkts < max_pkts) { - n = (int)CURLMIN(MMSG_NUM, max_pkts); - memset(&mmsg, 0, sizeof(mmsg)); - for(i = 0; i < n; ++i) { - msg_iov[i].iov_base = bufs[i]; - msg_iov[i].iov_len = (int)sizeof(bufs[i]); - mmsg[i].msg_hdr.msg_iov = &msg_iov[i]; - mmsg[i].msg_hdr.msg_iovlen = 1; - mmsg[i].msg_hdr.msg_name = &remote_addr[i]; - mmsg[i].msg_hdr.msg_namelen = sizeof(remote_addr[i]); - } - - while((mcount = recvmmsg(qctx->sockfd, mmsg, n, 0, NULL)) == -1 && - SOCKERRNO == EINTR) - ; - if(mcount == -1) { - if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { - DEBUGF(LOG_CF(data, cf, "ingress, recvmmsg -> EAGAIN")); - goto out; - } - if(!cf->connected && SOCKERRNO == ECONNREFUSED) { - const char *r_ip; - int r_port; - Curl_cf_socket_peek(cf->next, data, NULL, NULL, - &r_ip, &r_port, NULL, NULL); - failf(data, "QUIC: connection to %s port %u refused", - r_ip, r_port); - result = CURLE_COULDNT_CONNECT; - goto out; - } - failf(data, "QUIC: recvmsg() unexpectedly returned %d (errno=%d)", - mcount, SOCKERRNO); - result = CURLE_RECV_ERROR; - goto out; - } - - DEBUGF(LOG_CF(data, cf, "recvmmsg() -> %d packets", mcount)); - pkts += mcount; - for(i = 0; i < mcount; ++i) { - total_nread += mmsg[i].msg_len; - result = recv_cb(bufs[i], mmsg[i].msg_len, - mmsg[i].msg_hdr.msg_name, mmsg[i].msg_hdr.msg_namelen, - 0, userp); - if(result) - goto out; - } - } - -out: - DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zu bytes -> %d", - pkts, total_nread, result)); - return result; -} - -#elif defined(HAVE_SENDMSG) -static CURLcode recvmsg_packets(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct cf_quic_ctx *qctx, - size_t max_pkts, - vquic_recv_pkt_cb *recv_cb, void *userp) -{ - struct iovec msg_iov; - struct msghdr msg; - uint8_t buf[64*1024]; - struct sockaddr_storage remote_addr; - size_t total_nread, pkts; - ssize_t nread; - CURLcode result = CURLE_OK; - - msg_iov.iov_base = buf; - msg_iov.iov_len = (int)sizeof(buf); - - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = &msg_iov; - msg.msg_iovlen = 1; - - DEBUGASSERT(max_pkts > 0); - for(pkts = 0, total_nread = 0; pkts < max_pkts;) { - msg.msg_name = &remote_addr; - msg.msg_namelen = sizeof(remote_addr); - while((nread = recvmsg(qctx->sockfd, &msg, 0)) == -1 && - SOCKERRNO == EINTR) - ; - if(nread == -1) { - if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { - goto out; - } - if(!cf->connected && SOCKERRNO == ECONNREFUSED) { - const char *r_ip; - int r_port; - Curl_cf_socket_peek(cf->next, data, NULL, NULL, - &r_ip, &r_port, NULL, NULL); - failf(data, "QUIC: connection to %s port %u refused", - r_ip, r_port); - result = CURLE_COULDNT_CONNECT; - goto out; - } - failf(data, "QUIC: recvmsg() unexpectedly returned %zd (errno=%d)", - nread, SOCKERRNO); - result = CURLE_RECV_ERROR; - goto out; - } - - ++pkts; - total_nread += (size_t)nread; - result = recv_cb(buf, (size_t)nread, msg.msg_name, msg.msg_namelen, - 0, userp); - if(result) - goto out; - } - -out: - DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zu bytes -> %d", - pkts, total_nread, result)); - return result; -} - -#else /* HAVE_SENDMMSG || HAVE_SENDMSG */ -static CURLcode recvfrom_packets(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct cf_quic_ctx *qctx, - size_t max_pkts, - vquic_recv_pkt_cb *recv_cb, void *userp) -{ - uint8_t buf[64*1024]; - int bufsize = (int)sizeof(buf); - struct sockaddr_storage remote_addr; - socklen_t remote_addrlen = sizeof(remote_addr); - size_t total_nread, pkts; - ssize_t nread; - CURLcode result = CURLE_OK; - - DEBUGASSERT(max_pkts > 0); - for(pkts = 0, total_nread = 0; pkts < max_pkts;) { - while((nread = recvfrom(qctx->sockfd, (char *)buf, bufsize, 0, - (struct sockaddr *)&remote_addr, - &remote_addrlen)) == -1 && - SOCKERRNO == EINTR) - ; - if(nread == -1) { - if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { - DEBUGF(LOG_CF(data, cf, "ingress, recvfrom -> EAGAIN")); - goto out; - } - if(!cf->connected && SOCKERRNO == ECONNREFUSED) { - const char *r_ip; - int r_port; - Curl_cf_socket_peek(cf->next, data, NULL, NULL, - &r_ip, &r_port, NULL, NULL); - failf(data, "QUIC: connection to %s port %u refused", - r_ip, r_port); - result = CURLE_COULDNT_CONNECT; - goto out; - } - failf(data, "QUIC: recvfrom() unexpectedly returned %zd (errno=%d)", - nread, SOCKERRNO); - result = CURLE_RECV_ERROR; - goto out; - } - - ++pkts; - total_nread += (size_t)nread; - result = recv_cb(buf, (size_t)nread, &remote_addr, remote_addrlen, - 0, userp); - if(result) - goto out; - } - -out: - DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zu bytes -> %d", - pkts, total_nread, result)); - return result; -} -#endif /* !HAVE_SENDMMSG && !HAVE_SENDMSG */ - -CURLcode vquic_recv_packets(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct cf_quic_ctx *qctx, - size_t max_pkts, - vquic_recv_pkt_cb *recv_cb, void *userp) -{ -#if defined(HAVE_SENDMMSG) - return recvmmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp); -#elif defined(HAVE_SENDMSG) - return recvmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp); -#else - return recvfrom_packets(cf, data, qctx, max_pkts, recv_cb, userp); -#endif -} - /* * If the QLOGDIR environment variable is set, open and return a file * descriptor to write the log to. @@ -547,80 +84,4 @@ CURLcode Curl_qlogdir(struct Curl_easy *data, return CURLE_OK; } - -CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai, - int transport) -{ - (void)transport; - DEBUGASSERT(transport == TRNSPRT_QUIC); -#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) - return Curl_cf_ngtcp2_create(pcf, data, conn, ai); -#elif defined(USE_QUICHE) - return Curl_cf_quiche_create(pcf, data, conn, ai); -#elif defined(USE_MSH3) - return Curl_cf_msh3_create(pcf, data, conn, ai); -#else - *pcf = NULL; - (void)data; - (void)conn; - (void)ai; - return CURLE_NOT_BUILT_IN; #endif -} - -bool Curl_conn_is_http3(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex) -{ -#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) - return Curl_conn_is_ngtcp2(data, conn, sockindex); -#elif defined(USE_QUICHE) - return Curl_conn_is_quiche(data, conn, sockindex); -#elif defined(USE_MSH3) - return Curl_conn_is_msh3(data, conn, sockindex); -#else - return ((conn->handler->protocol & PROTO_FAMILY_HTTP) && - (conn->httpversion == 30)); -#endif -} - -CURLcode Curl_conn_may_http3(struct Curl_easy *data, - const struct connectdata *conn) -{ - if(conn->transport == TRNSPRT_UNIX) { - /* cannot do QUIC over a unix domain socket */ - return CURLE_QUIC_CONNECT_ERROR; - } - if(!(conn->handler->flags & PROTOPT_SSL)) { - failf(data, "HTTP/3 requested for non-HTTPS URL"); - return CURLE_URL_MALFORMAT; - } -#ifndef CURL_DISABLE_PROXY - if(conn->bits.socksproxy) { - failf(data, "HTTP/3 is not supported over a SOCKS proxy"); - return CURLE_URL_MALFORMAT; - } - if(conn->bits.httpproxy && conn->bits.tunnel_proxy) { - failf(data, "HTTP/3 is not supported over a HTTP proxy"); - return CURLE_URL_MALFORMAT; - } -#endif - - return CURLE_OK; -} - -#else /* ENABLE_QUIC */ - -CURLcode Curl_conn_may_http3(struct Curl_easy *data, - const struct connectdata *conn) -{ - (void)conn; - (void)data; - DEBUGF(infof(data, "QUIC is not supported in this build")); - return CURLE_NOT_BUILT_IN; -} - -#endif /* !ENABLE_QUIC */ diff --git a/contrib/libs/curl/lib/vquic/vquic.h b/contrib/libs/curl/lib/vquic/vquic.h deleted file mode 100644 index dc73957aaf..0000000000 --- a/contrib/libs/curl/lib/vquic/vquic.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef HEADER_CURL_VQUIC_QUIC_H -#define HEADER_CURL_VQUIC_QUIC_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifdef ENABLE_QUIC -struct Curl_cfilter; -struct Curl_easy; -struct connectdata; -struct Curl_addrinfo; - -void Curl_quic_ver(char *p, size_t len); - -CURLcode Curl_qlogdir(struct Curl_easy *data, - unsigned char *scid, - size_t scidlen, - int *qlogfdp); - - -CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai, - int transport); - -bool Curl_conn_is_http3(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex); - -extern struct Curl_cftype Curl_cft_http3; - -#else /* ENABLE_QUIC */ - -#define Curl_conn_is_http3(a,b,c) FALSE - -#endif /* !ENABLE_QUIC */ - -CURLcode Curl_conn_may_http3(struct Curl_easy *data, - const struct connectdata *conn); - -#endif /* HEADER_CURL_VQUIC_QUIC_H */ diff --git a/contrib/libs/curl/lib/vquic/vquic_int.h b/contrib/libs/curl/lib/vquic/vquic_int.h deleted file mode 100644 index 8e08784e7d..0000000000 --- a/contrib/libs/curl/lib/vquic/vquic_int.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef HEADER_CURL_VQUIC_QUIC_INT_H -#define HEADER_CURL_VQUIC_QUIC_INT_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" -#include "bufq.h" - -#ifdef ENABLE_QUIC - -#define MAX_PKT_BURST 10 -#define MAX_UDP_PAYLOAD_SIZE 1452 - -struct cf_quic_ctx { - curl_socket_t sockfd; /* connected UDP socket */ - struct sockaddr_storage local_addr; /* address socket is bound to */ - socklen_t local_addrlen; /* length of local address */ - - struct bufq sendbuf; /* buffer for sending one or more packets */ - size_t gsolen; /* length of individual packets in send buf */ - size_t split_len; /* if != 0, buffer length after which GSO differs */ - size_t split_gsolen; /* length of individual packets after split_len */ - bool no_gso; /* do not use gso on sending */ -}; - -CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx); -void vquic_ctx_free(struct cf_quic_ctx *qctx); - -void vquic_push_blocked_pkt(struct Curl_cfilter *cf, - struct cf_quic_ctx *qctx, - const uint8_t *pkt, size_t pktlen, size_t gsolen); - -CURLcode vquic_send_blocked_pkts(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct cf_quic_ctx *qctx); - -CURLcode vquic_send(struct Curl_cfilter *cf, struct Curl_easy *data, - struct cf_quic_ctx *qctx, size_t gsolen); - -CURLcode vquic_send_tail_split(struct Curl_cfilter *cf, struct Curl_easy *data, - struct cf_quic_ctx *qctx, size_t gsolen, - size_t tail_len, size_t tail_gsolen); - -CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data, - struct cf_quic_ctx *qctx); - - -typedef CURLcode vquic_recv_pkt_cb(const unsigned char *pkt, size_t pktlen, - struct sockaddr_storage *remote_addr, - socklen_t remote_addrlen, int ecn, - void *userp); - -CURLcode vquic_recv_packets(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct cf_quic_ctx *qctx, - size_t max_pkts, - vquic_recv_pkt_cb *recv_cb, void *userp); - -#endif /* !ENABLE_QUIC */ - -#endif /* HEADER_CURL_VQUIC_QUIC_INT_H */ diff --git a/contrib/libs/curl/lib/vssh/libssh.c b/contrib/libs/curl/lib/vssh/libssh.c index 3ac4925d8b..dc98aa0413 100644 --- a/contrib/libs/curl/lib/vssh/libssh.c +++ b/contrib/libs/curl/lib/vssh/libssh.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Red Hat, Inc. + * Copyright (C) 2017 - 2022 Red Hat, Inc. * * Authors: Nikos Mavrogiannopoulos, Tomas Mraz, Stanislav Zidek, * Robert Kolcun, Andreas Schneider @@ -51,6 +51,11 @@ #include <inet.h> #endif +#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) +#undef in_addr_t +#define in_addr_t unsigned long +#endif + #include <curl/curl.h> #include "urldata.h" #include "sendf.h" @@ -66,7 +71,6 @@ #include "strdup.h" #include "strcase.h" #include "vtls/vtls.h" -#include "cfilters.h" #include "connect.h" #include "inet_ntop.h" #include "parsedate.h" /* for the week day and month names */ @@ -576,7 +580,7 @@ cleanup: rc = SSH_ERROR; \ } while(0) -#define MOVE_TO_PASSWD_AUTH do { \ +#define MOVE_TO_LAST_AUTH do { \ if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) { \ rc = SSH_OK; \ state(data, SSH_AUTH_PASS_INIT); \ @@ -586,23 +590,23 @@ cleanup: } \ } while(0) -#define MOVE_TO_KEY_AUTH do { \ +#define MOVE_TO_TERTIARY_AUTH do { \ if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) { \ rc = SSH_OK; \ state(data, SSH_AUTH_KEY_INIT); \ } \ else { \ - MOVE_TO_PASSWD_AUTH; \ + MOVE_TO_LAST_AUTH; \ } \ } while(0) -#define MOVE_TO_GSSAPI_AUTH do { \ +#define MOVE_TO_SECONDARY_AUTH do { \ if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) { \ rc = SSH_OK; \ state(data, SSH_AUTH_GSSAPI); \ } \ else { \ - MOVE_TO_KEY_AUTH; \ + MOVE_TO_TERTIARY_AUTH; \ } \ } while(0) @@ -685,6 +689,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) struct ssh_conn *sshc = &conn->proto.sshc; curl_socket_t sock = conn->sock[FIRSTSOCKET]; int rc = SSH_NO_ERROR, err; + char *new_readdir_line; int seekerr = CURL_SEEKFUNC_OK; const char *err_msg; *block = 0; /* we're not blocking by default */ @@ -753,16 +758,6 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) } sshc->auth_methods = ssh_userauth_list(sshc->ssh_session, NULL); - if(sshc->auth_methods) - infof(data, "SSH authentication methods available: %s%s%s%s", - sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY ? - "public key, ": "", - sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC ? - "GSSAPI, " : "", - sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE ? - "keyboard-interactive, " : "", - sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD ? - "password": ""); if(sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) { state(data, SSH_AUTH_PKEY_INIT); infof(data, "Authentication using SSH public key file"); @@ -785,7 +780,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) } case SSH_AUTH_PKEY_INIT: if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY)) { - MOVE_TO_GSSAPI_AUTH; + MOVE_TO_SECONDARY_AUTH; break; } @@ -801,7 +796,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) } if(rc != SSH_OK) { - MOVE_TO_GSSAPI_AUTH; + MOVE_TO_SECONDARY_AUTH; break; } } @@ -836,7 +831,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) break; } - MOVE_TO_GSSAPI_AUTH; + MOVE_TO_SECONDARY_AUTH; } break; case SSH_AUTH_PKEY: @@ -854,13 +849,13 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) } else { infof(data, "Failed public key authentication (rc: %d)", rc); - MOVE_TO_GSSAPI_AUTH; + MOVE_TO_SECONDARY_AUTH; } break; case SSH_AUTH_GSSAPI: if(!(data->set.ssh_auth_types & CURLSSH_AUTH_GSSAPI)) { - MOVE_TO_KEY_AUTH; + MOVE_TO_TERTIARY_AUTH; break; } @@ -878,7 +873,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) break; } - MOVE_TO_KEY_AUTH; + MOVE_TO_TERTIARY_AUTH; break; case SSH_AUTH_KEY_INIT: @@ -886,12 +881,13 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) state(data, SSH_AUTH_KEY); } else { - MOVE_TO_PASSWD_AUTH; + MOVE_TO_LAST_AUTH; } break; case SSH_AUTH_KEY: - /* keyboard-interactive authentication */ + + /* Authentication failed. Continue with keyboard-interactive now. */ rc = myssh_auth_interactive(conn); if(rc == SSH_AGAIN) { break; @@ -899,15 +895,13 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) if(rc == SSH_OK) { sshc->authed = TRUE; infof(data, "completed keyboard interactive authentication"); - state(data, SSH_AUTH_DONE); - } - else { - MOVE_TO_PASSWD_AUTH; } + state(data, SSH_AUTH_DONE); break; case SSH_AUTH_PASS_INIT: if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD)) { + /* Host key authentication is intentionally not implemented */ MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); break; } @@ -1220,7 +1214,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) } case SSH_SFTP_TRANS_INIT: - if(data->state.upload) + if(data->set.upload) state(data, SSH_SFTP_UPLOAD_INIT); else { if(protop->path[strlen(protop->path)-1] == '/') @@ -1421,7 +1415,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) case SSH_SFTP_READDIR_INIT: Curl_pgrsSetDownloadSize(data, -1); - if(data->req.no_body) { + if(data->set.opt_no_body) { state(data, SSH_STOP); break; } @@ -1442,7 +1436,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) break; case SSH_SFTP_READDIR: - Curl_dyn_reset(&sshc->readdir_buf); + if(sshc->readdir_attrs) sftp_attributes_free(sshc->readdir_attrs); @@ -1478,12 +1472,17 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) sshc->readdir_len); } else { - if(Curl_dyn_add(&sshc->readdir_buf, sshc->readdir_longentry)) { + sshc->readdir_currLen = strlen(sshc->readdir_longentry); + sshc->readdir_totalLen = 80 + sshc->readdir_currLen; + sshc->readdir_line = calloc(sshc->readdir_totalLen, 1); + if(!sshc->readdir_line) { + state(data, SSH_SFTP_CLOSE); sshc->actualcode = CURLE_OUT_OF_MEMORY; - state(data, SSH_STOP); break; } + memcpy(sshc->readdir_line, sshc->readdir_longentry, + sshc->readdir_currLen); if((sshc->readdir_attrs->flags & SSH_FILEXFER_ATTR_PERMISSIONS) && ((sshc->readdir_attrs->permissions & SSH_S_IFMT) == SSH_S_IFLNK)) { @@ -1546,11 +1545,24 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) Curl_safefree(sshc->readdir_linkPath); - if(Curl_dyn_addf(&sshc->readdir_buf, " -> %s", - sshc->readdir_filename)) { + /* get room for the filename and extra output */ + sshc->readdir_totalLen += 4 + sshc->readdir_len; + new_readdir_line = Curl_saferealloc(sshc->readdir_line, + sshc->readdir_totalLen); + if(!new_readdir_line) { + sshc->readdir_line = NULL; + state(data, SSH_SFTP_CLOSE); sshc->actualcode = CURLE_OUT_OF_MEMORY; break; } + sshc->readdir_line = new_readdir_line; + + sshc->readdir_currLen += msnprintf(sshc->readdir_line + + sshc->readdir_currLen, + sshc->readdir_totalLen - + sshc->readdir_currLen, + " -> %s", + sshc->readdir_filename); sftp_attributes_free(sshc->readdir_link_attrs); sshc->readdir_link_attrs = NULL; @@ -1560,19 +1572,21 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) state(data, SSH_SFTP_READDIR_BOTTOM); /* FALLTHROUGH */ case SSH_SFTP_READDIR_BOTTOM: - if(Curl_dyn_addn(&sshc->readdir_buf, "\n", 1)) - result = CURLE_OUT_OF_MEMORY; - else - result = Curl_client_write(data, CLIENTWRITE_BODY, - Curl_dyn_ptr(&sshc->readdir_buf), - Curl_dyn_len(&sshc->readdir_buf)); + sshc->readdir_currLen += msnprintf(sshc->readdir_line + + sshc->readdir_currLen, + sshc->readdir_totalLen - + sshc->readdir_currLen, "\n"); + result = Curl_client_write(data, CLIENTWRITE_BODY, + sshc->readdir_line, + sshc->readdir_currLen); if(!result) { /* output debug output if that is requested */ - Curl_debug(data, CURLINFO_DATA_OUT, Curl_dyn_ptr(&sshc->readdir_buf), - Curl_dyn_len(&sshc->readdir_buf)); - data->req.bytecount += Curl_dyn_len(&sshc->readdir_buf); + Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_line, + sshc->readdir_currLen); + data->req.bytecount += sshc->readdir_currLen; } + Curl_safefree(sshc->readdir_line); ssh_string_free_char(sshc->readdir_tmp); sshc->readdir_tmp = NULL; @@ -1608,7 +1622,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) MOVE_TO_SFTP_CLOSE_STATE(); break; } - sftp_file_set_nonblocking(sshc->sftp_file); + state(data, SSH_SFTP_DOWNLOAD_STAT); break; @@ -1648,13 +1662,13 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) CURLofft to_t; CURLofft from_t; - from_t = curlx_strtoofft(data->state.range, &ptr, 10, &from); + from_t = curlx_strtoofft(data->state.range, &ptr, 0, &from); if(from_t == CURL_OFFT_FLOW) { return CURLE_RANGE_ERROR; } while(*ptr && (ISBLANK(*ptr) || (*ptr == '-'))) ptr++; - to_t = curlx_strtoofft(ptr, &ptr2, 10, &to); + to_t = curlx_strtoofft(ptr, &ptr2, 0, &to); if(to_t == CURL_OFFT_FLOW) { return CURLE_RANGE_ERROR; } @@ -1813,7 +1827,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) /* Functions from the SCP subsystem cannot handle/return SSH_AGAIN */ ssh_set_blocking(sshc->ssh_session, 1); - if(data->state.upload) { + if(data->set.upload) { if(data->state.infilesize < 0) { failf(data, "SCP requires a known file size for upload"); sshc->actualcode = CURLE_UPLOAD_FAILED; @@ -1918,7 +1932,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) break; } case SSH_SCP_DONE: - if(data->state.upload) + if(data->set.upload) state(data, SSH_SCP_SEND_EOF); else state(data, SSH_SCP_CHANNEL_FREE); @@ -2011,7 +2025,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) Curl_safefree(sshc->rsa); Curl_safefree(sshc->quote_path1); Curl_safefree(sshc->quote_path2); - Curl_dyn_free(&sshc->readdir_buf); + Curl_safefree(sshc->readdir_line); Curl_safefree(sshc->readdir_linkPath); SSH_STRING_FREE_CHAR(sshc->homedir); @@ -2156,12 +2170,11 @@ static CURLcode myssh_setup_connection(struct Curl_easy *data, struct connectdata *conn) { struct SSHPROTO *ssh; - struct ssh_conn *sshc = &conn->proto.sshc; + (void)conn; data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO)); if(!ssh) return CURLE_OUT_OF_MEMORY; - Curl_dyn_init(&sshc->readdir_buf, PATH_MAX * 2); return CURLE_OK; } @@ -2310,6 +2323,7 @@ CURLcode scp_perform(struct Curl_easy *data, bool *connected, bool *dophase_done) { CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; DEBUGF(infof(data, "DO phase starts")); @@ -2320,7 +2334,7 @@ CURLcode scp_perform(struct Curl_easy *data, result = myssh_multi_statemach(data, dophase_done); - *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); + *connected = conn->bits.tcpconnect[FIRSTSOCKET]; if(*dophase_done) { DEBUGF(infof(data, "DO phase is complete")); @@ -2490,6 +2504,7 @@ CURLcode sftp_perform(struct Curl_easy *data, bool *dophase_done) { CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; DEBUGF(infof(data, "DO phase starts")); @@ -2501,7 +2516,7 @@ CURLcode sftp_perform(struct Curl_easy *data, /* run the state-machine */ result = myssh_multi_statemach(data, dophase_done); - *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); + *connected = conn->bits.tcpconnect[FIRSTSOCKET]; if(*dophase_done) { DEBUGF(infof(data, "DO phase is complete")); diff --git a/contrib/libs/curl/lib/vssh/libssh2.c b/contrib/libs/curl/lib/vssh/libssh2.c index 74b4ff1db6..0536f5a09e 100644 --- a/contrib/libs/curl/lib/vssh/libssh2.c +++ b/contrib/libs/curl/lib/vssh/libssh2.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -54,6 +54,11 @@ #include <inet.h> #endif +#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) +#undef in_addr_t +#define in_addr_t unsigned long +#endif + #include <curl/curl.h> #include "urldata.h" #include "sendf.h" @@ -69,7 +74,6 @@ #include "strdup.h" #include "strcase.h" #include "vtls/vtls.h" -#include "cfilters.h" #include "connect.h" #include "inet_ntop.h" #include "parsedate.h" /* for the week day and month names */ @@ -79,6 +83,7 @@ #include "select.h" #include "warnless.h" #error #include "curl_path.h" +#include "strcase.h" #include <curl_base64.h> /* for base64 encoding/decoding */ #include <curl_sha256.h> @@ -103,6 +108,7 @@ static const char *sftp_libssh2_strerror(unsigned long err); static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc); static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc); static LIBSSH2_FREE_FUNC(my_libssh2_free); + static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data); static CURLcode ssh_connect(struct Curl_easy *data, bool *done); static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done); @@ -143,7 +149,7 @@ const struct Curl_handler Curl_handler_scp = { scp_disconnect, /* disconnect */ ZERO_NULL, /* readwrite */ ZERO_NULL, /* connection_check */ - ssh_attach, /* attach */ + ssh_attach, PORT_SSH, /* defport */ CURLPROTO_SCP, /* protocol */ CURLPROTO_SCP, /* family */ @@ -172,7 +178,7 @@ const struct Curl_handler Curl_handler_sftp = { sftp_disconnect, /* disconnect */ ZERO_NULL, /* readwrite */ ZERO_NULL, /* connection_check */ - ssh_attach, /* attach */ + ssh_attach, PORT_SSH, /* defport */ CURLPROTO_SFTP, /* protocol */ CURLPROTO_SFTP, /* family */ @@ -604,9 +610,9 @@ static CURLcode ssh_knownhost(struct Curl_easy *data) /* remove old host+key that doesn't match */ if(host) libssh2_knownhost_del(sshc->kh, host); - /* FALLTHROUGH */ + /*FALLTHROUGH*/ case CURLKHSTAT_FINE: - /* FALLTHROUGH */ + /*FALLTHROUGH*/ case CURLKHSTAT_FINE_ADD_TO_FILE: /* proceed */ if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) { @@ -722,10 +728,11 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data) */ if((pub_pos != b64_pos) || strncmp(fingerprint_b64, pubkey_sha256, pub_pos)) { + free(fingerprint_b64); + failf(data, "Denied establishing ssh session: mismatch sha256 fingerprint. " "Remote %s is not equal to %s", fingerprint_b64, pubkey_sha256); - free(fingerprint_b64); state(data, SSH_SESSION_FREE); sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; return sshc->actualcode; @@ -778,7 +785,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data) size_t keylen = 0; int sshkeytype = 0; int rc = 0; - /* we handle the process to the callback */ + /* we handle the process to the callback*/ const char *remotekey = libssh2_session_hostkey(sshc->ssh_session, &keylen, &sshkeytype); if(remotekey) { @@ -789,14 +796,10 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data) Curl_set_in_callback(data, false); if(rc!= CURLKHMATCH_OK) { state(data, SSH_SESSION_FREE); - sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; - return sshc->actualcode; } } else { state(data, SSH_SESSION_FREE); - sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; - return sshc->actualcode; } return CURLE_OK; } @@ -838,8 +841,6 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data) #endif static const char * const hostkey_method_ssh_rsa = "ssh-rsa"; - static const char * const hostkey_method_ssh_rsa_all - = "rsa-sha2-256,rsa-sha2-512,ssh-rsa"; static const char * const hostkey_method_ssh_dss = "ssh-dss"; @@ -889,7 +890,6 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data) } if(found) { - int rc; infof(data, "Found host %s in %s", conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]); @@ -915,16 +915,7 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data) break; #endif case LIBSSH2_KNOWNHOST_KEY_SSHRSA: -#ifdef HAVE_LIBSSH2_VERSION - if(libssh2_version(0x010900)) - /* since 1.9.0 libssh2_session_method_pref() works as expected */ - hostkey_method = hostkey_method_ssh_rsa_all; - else -#endif - /* old libssh2 which cannot correctly remove unsupported methods due - * to bug in src/kex.c or does not support the new methods anyways. - */ - hostkey_method = hostkey_method_ssh_rsa; + hostkey_method = hostkey_method_ssh_rsa; break; case LIBSSH2_KNOWNHOST_KEY_SSHDSS: hostkey_method = hostkey_method_ssh_dss; @@ -939,15 +930,9 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data) } infof(data, "Set \"%s\" as SSH hostkey type", hostkey_method); - rc = libssh2_session_method_pref(sshc->ssh_session, - LIBSSH2_METHOD_HOSTKEY, hostkey_method); - if(rc) { - char *errmsg = NULL; - int errlen; - libssh2_session_last_error(sshc->ssh_session, &errmsg, &errlen, 0); - failf(data, "libssh2: %s", errmsg); - result = libssh2_session_error_to_CURLE(rc); - } + result = libssh2_session_error_to_CURLE( + libssh2_session_method_pref( + sshc->ssh_session, LIBSSH2_METHOD_HOSTKEY, hostkey_method)); } else { infof(data, "Did not find host %s in %s", @@ -2019,7 +2004,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) } case SSH_SFTP_TRANS_INIT: - if(data->state.upload) + if(data->set.upload) state(data, SSH_SFTP_UPLOAD_INIT); else { if(sshp->path[strlen(sshp->path)-1] == '/') @@ -2266,7 +2251,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) case SSH_SFTP_READDIR_INIT: Curl_pgrsSetDownloadSize(data, -1); - if(data->req.no_body) { + if(data->set.opt_no_body) { state(data, SSH_STOP); break; } @@ -2405,6 +2390,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) result = Curl_dyn_addf(&sshp->readdir, " -> %s", sshp->readdir_filename); if(result) { + sshc->readdir_line = NULL; Curl_safefree(sshp->readdir_filename); Curl_safefree(sshp->readdir_longentry); state(data, SSH_SFTP_CLOSE); @@ -2517,12 +2503,12 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) CURLofft to_t; CURLofft from_t; - from_t = curlx_strtoofft(data->state.range, &ptr, 10, &from); + from_t = curlx_strtoofft(data->state.range, &ptr, 0, &from); if(from_t == CURL_OFFT_FLOW) return CURLE_RANGE_ERROR; while(*ptr && (ISBLANK(*ptr) || (*ptr == '-'))) ptr++; - to_t = curlx_strtoofft(ptr, &ptr2, 10, &to); + to_t = curlx_strtoofft(ptr, &ptr2, 0, &to); if(to_t == CURL_OFFT_FLOW) return CURLE_RANGE_ERROR; if((to_t == CURL_OFFT_INVAL) /* no "to" value given */ @@ -2691,7 +2677,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) break; } - if(data->state.upload) { + if(data->set.upload) { if(data->state.infilesize < 0) { failf(data, "SCP requires a known file size for upload"); sshc->actualcode = CURLE_UPLOAD_FAILED; @@ -2831,7 +2817,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) break; case SSH_SCP_DONE: - if(data->state.upload) + if(data->set.upload) state(data, SSH_SCP_SEND_EOF); else state(data, SSH_SCP_CHANNEL_FREE); @@ -3008,9 +2994,12 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) Curl_safefree(sshc->rsa_pub); Curl_safefree(sshc->rsa); + Curl_safefree(sshc->quote_path1); Curl_safefree(sshc->quote_path2); + Curl_safefree(sshc->homedir); + Curl_safefree(sshc->readdir_line); /* the code we are about to return */ result = sshc->actualcode; @@ -3269,27 +3258,14 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) sock = conn->sock[FIRSTSOCKET]; #endif /* CURL_LIBSSH2_DEBUG */ - /* libcurl MUST to set custom memory functions so that the kbd_callback - funciton's memory allocations can be properled freed */ sshc->ssh_session = libssh2_session_init_ex(my_libssh2_malloc, my_libssh2_free, my_libssh2_realloc, data); - if(!sshc->ssh_session) { failf(data, "Failure initialising ssh session"); return CURLE_FAILED_INIT; } -#ifdef HAVE_LIBSSH2_VERSION - /* Set the packet read timeout if the libssh2 version supports it */ -#if LIBSSH2_VERSION_NUM >= 0x010B00 - if(data->set.server_response_timeout > 0) { - libssh2_session_set_read_timeout(sshc->ssh_session, - data->set.server_response_timeout / 1000); - } -#endif -#endif - #ifndef CURL_DISABLE_PROXY if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) { /* @@ -3399,6 +3375,7 @@ CURLcode scp_perform(struct Curl_easy *data, bool *dophase_done) { CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; DEBUGF(infof(data, "DO phase starts")); @@ -3410,7 +3387,7 @@ CURLcode scp_perform(struct Curl_easy *data, /* run the state-machine */ result = ssh_multi_statemach(data, dophase_done); - *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); + *connected = conn->bits.tcpconnect[FIRSTSOCKET]; if(*dophase_done) { DEBUGF(infof(data, "DO phase is complete")); @@ -3599,7 +3576,7 @@ CURLcode sftp_perform(struct Curl_easy *data, /* run the state-machine */ result = ssh_multi_statemach(data, dophase_done); - *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); + *connected = data->conn->bits.tcpconnect[FIRSTSOCKET]; if(*dophase_done) { DEBUGF(infof(data, "DO phase is complete")); diff --git a/contrib/libs/curl/lib/vssh/ssh.h b/contrib/libs/curl/lib/vssh/ssh.h index 1f5f37e680..33f369bf42 100644 --- a/contrib/libs/curl/lib/vssh/ssh.h +++ b/contrib/libs/curl/lib/vssh/ssh.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -147,6 +147,7 @@ struct ssh_conn { char *homedir; /* when doing SFTP we figure out home dir in the connect phase */ + char *readdir_line; /* end of READDIR stuff */ int secondCreateDirs; /* counter use by the code to see if the @@ -157,8 +158,7 @@ struct ssh_conn { #if defined(USE_LIBSSH) char *readdir_linkPath; - size_t readdir_len; - struct dynbuf readdir_buf; + size_t readdir_len, readdir_totalLen, readdir_currLen; /* our variables */ unsigned kbd_state; /* 0 or 1 */ ssh_key privkey; diff --git a/contrib/libs/curl/lib/vssh/wolfssh.c b/contrib/libs/curl/lib/vssh/wolfssh.c index 260f1e7bfe..539be40d66 100644 --- a/contrib/libs/curl/lib/vssh/wolfssh.c +++ b/contrib/libs/curl/lib/vssh/wolfssh.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2019 - 2022, 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 @@ -31,7 +31,6 @@ #error #include <wolfssh/ssh.h> #error #include <wolfssh/wolfsftp.h> #include "urldata.h" -#include "cfilters.h" #include "connect.h" #include "sendf.h" #include "progress.h" @@ -277,7 +276,7 @@ static ssize_t wsftp_send(struct Curl_easy *data, int sockindex, return -1; } DEBUGASSERT(rc == (int)len); - infof(data, "sent %zu bytes SFTP from offset %" CURL_FORMAT_CURL_OFF_T, + infof(data, "sent %zd bytes SFTP from offset %zd", len, sshc->offset); sshc->offset += len; return (ssize_t)rc; @@ -425,7 +424,7 @@ static CURLcode wssh_connect(struct Curl_easy *data, bool *done) state(data, SSH_SFTP_INIT); return wssh_multi_statemach(data, done); -error: + error: wolfSSH_free(sshc->ssh_session); wolfSSH_CTX_free(sshc->ctx); return CURLE_FAILED_INIT; @@ -557,7 +556,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block) } break; case SSH_SFTP_TRANS_INIT: - if(data->state.upload) + if(data->set.upload) state(data, SSH_SFTP_UPLOAD_INIT); else { if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/') @@ -837,7 +836,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block) case SSH_SFTP_READDIR_INIT: Curl_pgrsSetDownloadSize(data, -1); - if(data->req.no_body) { + if(data->set.opt_no_body) { state(data, SSH_STOP); break; } @@ -940,6 +939,7 @@ CURLcode wsftp_perform(struct Curl_easy *data, bool *dophase_done) { CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; DEBUGF(infof(data, "DO phase starts")); @@ -951,7 +951,7 @@ CURLcode wsftp_perform(struct Curl_easy *data, /* run the state-machine */ result = wssh_multi_statemach(data, dophase_done); - *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); + *connected = conn->bits.tcpconnect[FIRSTSOCKET]; if(*dophase_done) { DEBUGF(infof(data, "DO phase is complete")); diff --git a/contrib/libs/curl/lib/vtls/bearssl.c b/contrib/libs/curl/lib/vtls/bearssl.c index 6ed453b981..1221ce8c84 100644 --- a/contrib/libs/curl/lib/vtls/bearssl.c +++ b/contrib/libs/curl/lib/vtls/bearssl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Michael Forney, <mforney@mforney.org> + * Copyright (C) 2019 - 2022, Michael Forney, <mforney@mforney.org> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -32,16 +32,12 @@ #include "sendf.h" #include "inet_pton.h" #include "vtls.h" -#include "vtls_int.h" #include "connect.h" #include "select.h" #include "multiif.h" #include "curl_printf.h" -#include "strcase.h" - -/* The last #include files should be: */ #include "curl_memory.h" -#include "memdebug.h" +#include "strcase.h" struct x509_context { const br_x509_class *vtable; @@ -52,13 +48,13 @@ struct x509_context { int cert_num; }; -struct bearssl_ssl_backend_data { +struct ssl_backend_data { br_ssl_client_context ctx; struct x509_context x509; unsigned char buf[BR_SSL_BUFSIZE_BIDI]; br_x509_trust_anchor *anchors; size_t anchors_len; - const char *protocols[ALPN_ENTRIES_MAX]; + const char *protocols[2]; /* SSL client context is active */ bool active; /* size of pending write, yet to be flushed */ @@ -570,21 +566,18 @@ static CURLcode bearssl_set_selected_ciphers(struct Curl_easy *data, return CURLE_OK; } -static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode bearssl_connect_step1(struct Curl_easy *data, + struct connectdata *conn, int sockindex) { - struct ssl_connect_data *connssl = cf->ctx; - struct bearssl_ssl_backend_data *backend = - (struct bearssl_ssl_backend_data *)connssl->backend; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; + const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob); const char * const ssl_cafile = /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ - (ca_info_blob ? NULL : conn_config->CAfile); - const char *hostname = connssl->hostname; - const bool verifypeer = conn_config->verifypeer; - const bool verifyhost = conn_config->verifyhost; + (ca_info_blob ? NULL : SSL_CONN_CONFIG(CAfile)); + const char *hostname = SSL_HOST_NAME(); + const bool verifypeer = SSL_CONN_CONFIG(verifypeer); + const bool verifyhost = SSL_CONN_CONFIG(verifyhost); CURLcode ret; unsigned version_min, version_max; #ifdef ENABLE_IPV6 @@ -595,7 +588,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, DEBUGASSERT(backend); - switch(conn_config->version) { + switch(SSL_CONN_CONFIG(version)) { case CURL_SSLVERSION_SSLv2: failf(data, "BearSSL does not support SSLv2"); return CURLE_SSL_CONNECT_ERROR; @@ -666,11 +659,11 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, br_ssl_engine_set_buffer(&backend->ctx.eng, backend->buf, sizeof(backend->buf), 1); - if(conn_config->cipher_list) { + if(SSL_CONN_CONFIG(cipher_list)) { /* Override the ciphers as specified. For the default cipher list see the BearSSL source code of br_ssl_client_init_full() */ ret = bearssl_set_selected_ciphers(data, &backend->ctx.eng, - conn_config->cipher_list); + SSL_CONN_CONFIG(cipher_list)); if(ret) return ret; } @@ -681,28 +674,41 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, backend->x509.verifyhost = verifyhost; br_ssl_engine_set_x509(&backend->ctx.eng, &backend->x509.vtable); - if(ssl_config->primary.sessionid) { + if(SSL_SET_OPTION(primary.sessionid)) { void *session; Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &session, NULL)) { + if(!Curl_ssl_getsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE, + &session, NULL, sockindex)) { br_ssl_engine_set_session_parameters(&backend->ctx.eng, session); infof(data, "BearSSL: re-using session ID"); } Curl_ssl_sessionid_unlock(data); } - if(connssl->alpn) { - struct alpn_proto_buf proto; - size_t i; + if(conn->bits.tls_enable_alpn) { + int cur = 0; + + /* NOTE: when adding more protocols here, increase the size of the + * protocols array in `struct ssl_backend_data`. + */ - for(i = 0; i < connssl->alpn->count; ++i) { - backend->protocols[i] = connssl->alpn->entries[i]; +#ifdef USE_HTTP2 + if(data->state.httpwant >= CURL_HTTP_VERSION_2 +#ifndef CURL_DISABLE_PROXY + && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy) +#endif + ) { + backend->protocols[cur++] = ALPN_H2; + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); } - br_ssl_engine_set_protocol_names(&backend->ctx.eng, backend->protocols, - connssl->alpn->count); - Curl_alpn_to_proto_str(&proto, connssl->alpn); - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); +#endif + + backend->protocols[cur++] = ALPN_HTTP_1_1; + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); + + br_ssl_engine_set_protocol_names(&backend->ctx.eng, + backend->protocols, cur); } if((1 == Curl_inet_pton(AF_INET, hostname, &addr)) @@ -747,18 +753,17 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, return CURLE_OK; } -static CURLcode bearssl_run_until(struct Curl_cfilter *cf, - struct Curl_easy *data, +static CURLcode bearssl_run_until(struct Curl_easy *data, + struct connectdata *conn, int sockindex, unsigned target) { - struct ssl_connect_data *connssl = cf->ctx; - struct bearssl_ssl_backend_data *backend = - (struct bearssl_ssl_backend_data *)connssl->backend; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; + curl_socket_t sockfd = conn->sock[sockindex]; unsigned state; unsigned char *buf; size_t len; ssize_t ret; - CURLcode result; int err; DEBUGASSERT(backend); @@ -797,38 +802,48 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf, return CURLE_OK; if(state & BR_SSL_SENDREC) { buf = br_ssl_engine_sendrec_buf(&backend->ctx.eng, &len); - ret = Curl_conn_cf_send(cf->next, data, (char *)buf, len, &result); - if(ret <= 0) { - return result; + ret = swrite(sockfd, buf, len); + if(ret == -1) { + if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { + if(connssl->state != ssl_connection_complete) + connssl->connecting_state = ssl_connect_2_writing; + return CURLE_AGAIN; + } + return CURLE_WRITE_ERROR; } br_ssl_engine_sendrec_ack(&backend->ctx.eng, ret); } else if(state & BR_SSL_RECVREC) { buf = br_ssl_engine_recvrec_buf(&backend->ctx.eng, &len); - ret = Curl_conn_cf_recv(cf->next, data, (char *)buf, len, &result); + ret = sread(sockfd, buf, len); if(ret == 0) { failf(data, "SSL: EOF without close notify"); return CURLE_READ_ERROR; } - if(ret <= 0) { - return result; + if(ret == -1) { + if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { + if(connssl->state != ssl_connection_complete) + connssl->connecting_state = ssl_connect_2_reading; + return CURLE_AGAIN; + } + return CURLE_READ_ERROR; } br_ssl_engine_recvrec_ack(&backend->ctx.eng, ret); } } } -static CURLcode bearssl_connect_step2(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode bearssl_connect_step2(struct Curl_easy *data, + struct connectdata *conn, int sockindex) { - struct ssl_connect_data *connssl = cf->ctx; - struct bearssl_ssl_backend_data *backend = - (struct bearssl_ssl_backend_data *)connssl->backend; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; CURLcode ret; DEBUGASSERT(backend); - ret = bearssl_run_until(cf, data, BR_SSL_SENDAPP | BR_SSL_RECVAPP); + ret = bearssl_run_until(data, conn, sockindex, + BR_SSL_SENDAPP | BR_SSL_RECVAPP); if(ret == CURLE_AGAIN) return CURLE_OK; if(ret == CURLE_OK) { @@ -841,27 +856,40 @@ static CURLcode bearssl_connect_step2(struct Curl_cfilter *cf, return ret; } -static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode bearssl_connect_step3(struct Curl_easy *data, + struct connectdata *conn, int sockindex) { - struct ssl_connect_data *connssl = cf->ctx; - struct bearssl_ssl_backend_data *backend = - (struct bearssl_ssl_backend_data *)connssl->backend; - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; CURLcode ret; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); DEBUGASSERT(backend); - if(connssl->alpn) { - const char *proto; + if(conn->bits.tls_enable_alpn) { + const char *protocol; - proto = br_ssl_engine_get_selected_protocol(&backend->ctx.eng); - Curl_alpn_set_negotiated(cf, data, (const unsigned char *)proto, - proto? strlen(proto) : 0); + protocol = br_ssl_engine_get_selected_protocol(&backend->ctx.eng); + if(protocol) { + infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, protocol); + +#ifdef USE_HTTP2 + if(!strcmp(protocol, ALPN_H2)) + conn->alpn = CURL_HTTP_VERSION_2; + else +#endif + if(!strcmp(protocol, ALPN_HTTP_1_1)) + conn->alpn = CURL_HTTP_VERSION_1_1; + else + infof(data, "ALPN, unrecognized protocol %s", protocol); + Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ? + BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); + } + else + infof(data, VTLS_INFOF_NO_ALPN); } - if(ssl_config->primary.sessionid) { + if(SSL_SET_OPTION(primary.sessionid)) { bool incache; bool added = FALSE; void *oldsession; @@ -872,10 +900,14 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf, return CURLE_OUT_OF_MEMORY; br_ssl_engine_get_session_parameters(&backend->ctx.eng, session); Curl_ssl_sessionid_lock(data); - incache = !(Curl_ssl_getsessionid(cf, data, &oldsession, NULL)); + incache = !(Curl_ssl_getsessionid(data, conn, + SSL_IS_PROXY() ? TRUE : FALSE, + &oldsession, NULL, sockindex)); if(incache) Curl_ssl_delsessionid(data, oldsession); - ret = Curl_ssl_addsessionid(cf, data, session, 0, &added); + ret = Curl_ssl_addsessionid(data, conn, + SSL_IS_PROXY() ? TRUE : FALSE, + session, 0, sockindex, &added); Curl_ssl_sessionid_unlock(data); if(!added) free(session); @@ -889,20 +921,20 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf, return CURLE_OK; } -static ssize_t bearssl_send(struct Curl_cfilter *cf, struct Curl_easy *data, +static ssize_t bearssl_send(struct Curl_easy *data, int sockindex, const void *buf, size_t len, CURLcode *err) { - struct ssl_connect_data *connssl = cf->ctx; - struct bearssl_ssl_backend_data *backend = - (struct bearssl_ssl_backend_data *)connssl->backend; + struct connectdata *conn = data->conn; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; unsigned char *app; size_t applen; DEBUGASSERT(backend); for(;;) { - *err = bearssl_run_until(cf, data, BR_SSL_SENDAPP); - if(*err) + *err = bearssl_run_until(data, conn, sockindex, BR_SSL_SENDAPP); + if (*err != CURLE_OK) return -1; app = br_ssl_engine_sendapp_buf(&backend->ctx.eng, &applen); if(!app) { @@ -924,18 +956,18 @@ static ssize_t bearssl_send(struct Curl_cfilter *cf, struct Curl_easy *data, } } -static ssize_t bearssl_recv(struct Curl_cfilter *cf, struct Curl_easy *data, +static ssize_t bearssl_recv(struct Curl_easy *data, int sockindex, char *buf, size_t len, CURLcode *err) { - struct ssl_connect_data *connssl = cf->ctx; - struct bearssl_ssl_backend_data *backend = - (struct bearssl_ssl_backend_data *)connssl->backend; + struct connectdata *conn = data->conn; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; unsigned char *app; size_t applen; DEBUGASSERT(backend); - *err = bearssl_run_until(cf, data, BR_SSL_RECVAPP); + *err = bearssl_run_until(data, conn, sockindex, BR_SSL_RECVAPP); if(*err != CURLE_OK) return -1; app = br_ssl_engine_recvapp_buf(&backend->ctx.eng, &applen); @@ -949,14 +981,15 @@ static ssize_t bearssl_recv(struct Curl_cfilter *cf, struct Curl_easy *data, return applen; } -static CURLcode bearssl_connect_common(struct Curl_cfilter *cf, - struct Curl_easy *data, +static CURLcode bearssl_connect_common(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, bool nonblocking, bool *done) { CURLcode ret; - struct ssl_connect_data *connssl = cf->ctx; - curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + curl_socket_t sockfd = conn->sock[sockindex]; timediff_t timeout_ms; int what; @@ -967,7 +1000,7 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf, } if(ssl_connect_1 == connssl->connecting_state) { - ret = bearssl_connect_step1(cf, data); + ret = bearssl_connect_step1(data, conn, sockindex); if(ret) return ret; } @@ -1020,7 +1053,7 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf, * before step2 has completed while ensuring that a client using select() * or epoll() will always have a valid fdset to wait on. */ - ret = bearssl_connect_step2(cf, data); + ret = bearssl_connect_step2(data, conn, sockindex); if(ret || (nonblocking && (ssl_connect_2 == connssl->connecting_state || ssl_connect_2_reading == connssl->connecting_state || @@ -1029,13 +1062,15 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf, } if(ssl_connect_3 == connssl->connecting_state) { - ret = bearssl_connect_step3(cf, data); + ret = bearssl_connect_step3(data, conn, sockindex); if(ret) return ret; } if(ssl_connect_done == connssl->connecting_state) { connssl->state = ssl_connection_complete; + conn->recv[sockindex] = bearssl_recv; + conn->send[sockindex] = bearssl_send; *done = TRUE; } else @@ -1052,15 +1087,12 @@ static size_t bearssl_version(char *buffer, size_t size) return msnprintf(buffer, size, "BearSSL"); } -static bool bearssl_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data) +static bool bearssl_data_pending(const struct connectdata *conn, + int connindex) { - struct ssl_connect_data *ctx = cf->ctx; - struct bearssl_ssl_backend_data *backend; - - (void)data; - DEBUGASSERT(ctx && ctx->backend); - backend = (struct bearssl_ssl_backend_data *)ctx->backend; + const struct ssl_connect_data *connssl = &conn->ssl[connindex]; + struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); return br_ssl_engine_current_state(&backend->ctx.eng) & BR_SSL_RECVAPP; } @@ -1084,13 +1116,13 @@ static CURLcode bearssl_random(struct Curl_easy *data UNUSED_PARAM, return CURLE_OK; } -static CURLcode bearssl_connect(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode bearssl_connect(struct Curl_easy *data, + struct connectdata *conn, int sockindex) { CURLcode ret; bool done = FALSE; - ret = bearssl_connect_common(cf, data, FALSE, &done); + ret = bearssl_connect_common(data, conn, sockindex, FALSE, &done); if(ret) return ret; @@ -1099,41 +1131,37 @@ static CURLcode bearssl_connect(struct Curl_cfilter *cf, return CURLE_OK; } -static CURLcode bearssl_connect_nonblocking(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) +static CURLcode bearssl_connect_nonblocking(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, bool *done) { - return bearssl_connect_common(cf, data, TRUE, done); + return bearssl_connect_common(data, conn, sockindex, TRUE, done); } static void *bearssl_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { - struct bearssl_ssl_backend_data *backend = - (struct bearssl_ssl_backend_data *)connssl->backend; + struct ssl_backend_data *backend = connssl->backend; DEBUGASSERT(backend); return &backend->ctx; } -static void bearssl_close(struct Curl_cfilter *cf, struct Curl_easy *data) +static void bearssl_close(struct Curl_easy *data, + struct connectdata *conn, int sockindex) { - struct ssl_connect_data *connssl = cf->ctx; - struct bearssl_ssl_backend_data *backend = - (struct bearssl_ssl_backend_data *)connssl->backend; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; size_t i; DEBUGASSERT(backend); if(backend->active) { - backend->active = FALSE; br_ssl_engine_close(&backend->ctx.eng); - (void)bearssl_run_until(cf, data, BR_SSL_CLOSED); - } - if(backend->anchors) { - for(i = 0; i < backend->anchors_len; ++i) - free(backend->anchors[i].dn.data); - Curl_safefree(backend->anchors); + (void)bearssl_run_until(data, conn, sockindex, BR_SSL_CLOSED); } + for(i = 0; i < backend->anchors_len; ++i) + free(backend->anchors[i].dn.data); + free(backend->anchors); } static void bearssl_session_free(void *ptr) @@ -1156,8 +1184,8 @@ static CURLcode bearssl_sha256sum(const unsigned char *input, const struct Curl_ssl Curl_ssl_bearssl = { { CURLSSLBACKEND_BEARSSL, "bearssl" }, /* info */ - SSLSUPP_CAINFO_BLOB | SSLSUPP_SSL_CTX | SSLSUPP_HTTPS_PROXY, - sizeof(struct bearssl_ssl_backend_data), + SSLSUPP_CAINFO_BLOB | SSLSUPP_SSL_CTX, + sizeof(struct ssl_backend_data), Curl_none_init, /* init */ Curl_none_cleanup, /* cleanup */ @@ -1169,7 +1197,7 @@ const struct Curl_ssl Curl_ssl_bearssl = { Curl_none_cert_status_request, /* cert_status_request */ bearssl_connect, /* connect */ bearssl_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks, /* getsock */ + Curl_ssl_getsock, /* getsock */ bearssl_get_internals, /* get_internals */ bearssl_close, /* close_one */ Curl_none_close_all, /* close_all */ @@ -1180,10 +1208,7 @@ const struct Curl_ssl Curl_ssl_bearssl = { Curl_none_false_start, /* false_start */ bearssl_sha256sum, /* sha256sum */ NULL, /* associate_connection */ - NULL, /* disassociate_connection */ - NULL, /* free_multi_ssl_backend_data */ - bearssl_recv, /* recv decrypted data */ - bearssl_send, /* send data to encrypt */ + NULL /* disassociate_connection */ }; #endif /* USE_BEARSSL */ diff --git a/contrib/libs/curl/lib/vtls/bearssl.h b/contrib/libs/curl/lib/vtls/bearssl.h index b3651b092c..5125359961 100644 --- a/contrib/libs/curl/lib/vtls/bearssl.h +++ b/contrib/libs/curl/lib/vtls/bearssl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Michael Forney, <mforney@mforney.org> + * Copyright (C) 2019 - 2022, Michael Forney, <mforney@mforney.org> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/contrib/libs/curl/lib/vtls/gskit.h b/contrib/libs/curl/lib/vtls/gskit.h index c71e6a0117..cf923f6b85 100644 --- a/contrib/libs/curl/lib/vtls/gskit.h +++ b/contrib/libs/curl/lib/vtls/gskit.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/vtls/gtls.h b/contrib/libs/curl/lib/vtls/gtls.h index ac141e1c61..abade73f80 100644 --- a/contrib/libs/curl/lib/vtls/gtls.h +++ b/contrib/libs/curl/lib/vtls/gtls.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -25,50 +25,15 @@ ***************************************************************************/ #include "curl_setup.h" -#include <curl/curl.h> #ifdef USE_GNUTLS +#include "urldata.h" #include <gnutls/gnutls.h> - -#ifdef HAVE_GNUTLS_SRP -/* the function exists */ -#ifdef USE_TLS_SRP -/* the functionality is not disabled */ -#define USE_GNUTLS_SRP -#endif -#endif - -struct Curl_easy; -struct Curl_cfilter; -struct ssl_primary_config; -struct ssl_config_data; - -struct gtls_instance { - gnutls_session_t session; - gnutls_certificate_credentials_t cred; -#ifdef USE_GNUTLS_SRP - gnutls_srp_client_credentials_t srp_client_cred; -#endif -}; - CURLcode -gtls_client_init(struct Curl_easy *data, - struct ssl_primary_config *config, - struct ssl_config_data *ssl_config, - const char *hostname, - struct gtls_instance *gtls, - long *pverifyresult); - -CURLcode -Curl_gtls_verifyserver(struct Curl_easy *data, +Curl_gtls_verifyserver(struct Curl_easy *data, struct connectdata *conn, gnutls_session_t session, - struct ssl_primary_config *config, - struct ssl_config_data *ssl_config, - const char *hostname, - const char *dispname, - const char *pinned_key); - + int sockindex); extern const struct Curl_ssl Curl_ssl_gnutls; #endif /* USE_GNUTLS */ diff --git a/contrib/libs/curl/lib/vtls/hostcheck.c b/contrib/libs/curl/lib/vtls/hostcheck.c index 45abb626e3..e849cbc6fe 100644 --- a/contrib/libs/curl/lib/vtls/hostcheck.c +++ b/contrib/libs/curl/lib/vtls/hostcheck.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -71,12 +71,7 @@ static bool pmatch(const char *hostname, size_t hostlen, * apparent distinction between a name and an IP. We need to detect the use of * an IP address and not wildcard match on such names. * - * Only match on "*" being used for the leftmost label, not "a*", "a*b" nor - * "*b". - * * Return TRUE on a match. FALSE if not. - * - * @unittest: 1397 */ static bool hostmatch(const char *hostname, @@ -84,42 +79,53 @@ static bool hostmatch(const char *hostname, const char *pattern, size_t patternlen) { - const char *pattern_label_end; - - DEBUGASSERT(pattern); - DEBUGASSERT(patternlen); - DEBUGASSERT(hostname); - DEBUGASSERT(hostlen); + const char *pattern_label_end, *wildcard, *hostname_label_end; + size_t prefixlen, suffixlen; /* normalize pattern and hostname by stripping off trailing dots */ + DEBUGASSERT(patternlen); if(hostname[hostlen-1]=='.') hostlen--; if(pattern[patternlen-1]=='.') patternlen--; - if(strncmp(pattern, "*.", 2)) + wildcard = memchr(pattern, '*', patternlen); + if(!wildcard) return pmatch(hostname, hostlen, pattern, patternlen); /* detect IP address as hostname and fail the match if so */ - else if(Curl_host_is_ipnum(hostname)) + if(Curl_host_is_ipnum(hostname)) return FALSE; /* We require at least 2 dots in the pattern to avoid too wide wildcard match. */ pattern_label_end = memchr(pattern, '.', patternlen); if(!pattern_label_end || - (memrchr(pattern, '.', patternlen) == pattern_label_end)) + (memrchr(pattern, '.', patternlen) == pattern_label_end) || + strncasecompare(pattern, "xn--", 4)) return pmatch(hostname, hostlen, pattern, patternlen); + + hostname_label_end = memchr(hostname, '.', hostlen); + if(!hostname_label_end) + return FALSE; else { - const char *hostname_label_end = memchr(hostname, '.', hostlen); - if(hostname_label_end) { - size_t skiphost = hostname_label_end - hostname; - size_t skiplen = pattern_label_end - pattern; - return pmatch(hostname_label_end, hostlen - skiphost, - pattern_label_end, patternlen - skiplen); - } + size_t skiphost = hostname_label_end - hostname; + size_t skiplen = pattern_label_end - pattern; + if(!pmatch(hostname_label_end, hostlen - skiphost, + pattern_label_end, patternlen - skiplen)) + return FALSE; } - return FALSE; + /* The wildcard must match at least one character, so the left-most + label of the hostname is at least as large as the left-most label + of the pattern. */ + if(hostname_label_end - hostname < pattern_label_end - pattern) + return FALSE; + + prefixlen = wildcard - pattern; + suffixlen = pattern_label_end - (wildcard + 1); + return strncasecompare(pattern, hostname, prefixlen) && + strncasecompare(wildcard + 1, hostname_label_end - suffixlen, + suffixlen) ? TRUE : FALSE; } /* diff --git a/contrib/libs/curl/lib/vtls/hostcheck.h b/contrib/libs/curl/lib/vtls/hostcheck.h index 22a1ac2e56..d3c4eab56d 100644 --- a/contrib/libs/curl/lib/vtls/hostcheck.h +++ b/contrib/libs/curl/lib/vtls/hostcheck.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/vtls/keylog.c b/contrib/libs/curl/lib/vtls/keylog.c index d37bb183e7..1952a690ca 100644 --- a/contrib/libs/curl/lib/vtls/keylog.c +++ b/contrib/libs/curl/lib/vtls/keylog.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/vtls/keylog.h b/contrib/libs/curl/lib/vtls/keylog.h index eff5bf38f3..5d3c675b3e 100644 --- a/contrib/libs/curl/lib/vtls/keylog.h +++ b/contrib/libs/curl/lib/vtls/keylog.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/vtls/mbedtls.h b/contrib/libs/curl/lib/vtls/mbedtls.h index d8a0a06eb6..ec3b43bf9c 100644 --- a/contrib/libs/curl/lib/vtls/mbedtls.h +++ b/contrib/libs/curl/lib/vtls/mbedtls.h @@ -7,8 +7,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) Hoi-Ho Chan, <hoiho.chan@gmail.com> + * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2010, Hoi-Ho Chan, <hoiho.chan@gmail.com> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/contrib/libs/curl/lib/vtls/mbedtls_threadlock.c b/contrib/libs/curl/lib/vtls/mbedtls_threadlock.c index 22e579d08c..fa9fd634c1 100644 --- a/contrib/libs/curl/lib/vtls/mbedtls_threadlock.c +++ b/contrib/libs/curl/lib/vtls/mbedtls_threadlock.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) Hoi-Ho Chan, <hoiho.chan@gmail.com> + * Copyright (C) 2013 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2010, 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,12 +26,13 @@ #if defined(USE_MBEDTLS) && \ ((defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \ - defined(USE_THREADS_WIN32)) + (defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H))) #if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) # include <pthread.h> # define MBEDTLS_MUTEX_T pthread_mutex_t -#elif defined(USE_THREADS_WIN32) +#elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H) +# include <process.h> # define MBEDTLS_MUTEX_T HANDLE #endif @@ -59,7 +60,7 @@ int Curl_mbedtlsthreadlock_thread_setup(void) #if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) if(pthread_mutex_init(&mutex_buf[i], NULL)) return 0; /* pthread_mutex_init failed */ -#elif defined(USE_THREADS_WIN32) +#elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H) mutex_buf[i] = CreateMutex(0, FALSE, 0); if(mutex_buf[i] == 0) return 0; /* CreateMutex failed */ @@ -80,7 +81,7 @@ int Curl_mbedtlsthreadlock_thread_cleanup(void) #if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) if(pthread_mutex_destroy(&mutex_buf[i])) return 0; /* pthread_mutex_destroy failed */ -#elif defined(USE_THREADS_WIN32) +#elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H) if(!CloseHandle(mutex_buf[i])) return 0; /* CloseHandle failed */ #endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */ @@ -100,7 +101,7 @@ int Curl_mbedtlsthreadlock_lock_function(int n) "Error: mbedtlsthreadlock_lock_function failed\n")); return 0; /* pthread_mutex_lock failed */ } -#elif defined(USE_THREADS_WIN32) +#elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H) if(WaitForSingleObject(mutex_buf[n], INFINITE) == WAIT_FAILED) { DEBUGF(fprintf(stderr, "Error: mbedtlsthreadlock_lock_function failed\n")); @@ -120,7 +121,7 @@ int Curl_mbedtlsthreadlock_unlock_function(int n) "Error: mbedtlsthreadlock_unlock_function failed\n")); return 0; /* pthread_mutex_unlock failed */ } -#elif defined(USE_THREADS_WIN32) +#elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H) if(!ReleaseMutex(mutex_buf[n])) { DEBUGF(fprintf(stderr, "Error: mbedtlsthreadlock_unlock_function failed\n")); diff --git a/contrib/libs/curl/lib/vtls/nssg.h b/contrib/libs/curl/lib/vtls/nssg.h index ad7eef5801..454a38f1fb 100644 --- a/contrib/libs/curl/lib/vtls/nssg.h +++ b/contrib/libs/curl/lib/vtls/nssg.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/vtls/openssl.c b/contrib/libs/curl/lib/vtls/openssl.c index f27ba96952..fe55490309 100644 --- a/contrib/libs/curl/lib/vtls/openssl.c +++ b/contrib/libs/curl/lib/vtls/openssl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -55,7 +55,6 @@ #include "slist.h" #include "select.h" #include "vtls.h" -#include "vtls_int.h" #include "vauth/vauth.h" #include "keylog.h" #include "strcase.h" @@ -96,7 +95,6 @@ #include "curl_memory.h" #include "memdebug.h" - /* Uncomment the ALLOW_RENEG line to a real #define if you want to allow TLS renegotiations when built with BoringSSL. Renegotiating is non-compliant with HTTP/2 and "an extremely dangerous protocol feature". Beware. @@ -207,10 +205,8 @@ #if ((OPENSSL_VERSION_NUMBER >= 0x10101000L) && \ !defined(LIBRESSL_VERSION_NUMBER) && \ !defined(OPENSSL_IS_BORINGSSL)) - #define HAVE_SSL_CTX_SET_CIPHERSUITES - #if !defined(OPENSSL_IS_AWSLC) - #define HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH - #endif +#define HAVE_SSL_CTX_SET_CIPHERSUITES +#define HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH #endif /* @@ -229,8 +225,6 @@ #define OSSL_PACKAGE "LibreSSL" #elif defined(OPENSSL_IS_BORINGSSL) #define OSSL_PACKAGE "BoringSSL" -#elif defined(OPENSSL_IS_AWSLC) -#define OSSL_PACKAGE "AWS-LC" #else #define OSSL_PACKAGE "OpenSSL" #endif @@ -261,55 +255,23 @@ #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \ !(defined(LIBRESSL_VERSION_NUMBER) && \ LIBRESSL_VERSION_NUMBER < 0x2070100fL) && \ - !defined(OPENSSL_IS_BORINGSSL) && \ - !defined(OPENSSL_IS_AWSLC) + !defined(OPENSSL_IS_BORINGSSL) #define HAVE_OPENSSL_VERSION #endif -#ifdef OPENSSL_IS_BORINGSSL -typedef uint32_t sslerr_t; -#else -typedef unsigned long sslerr_t; -#endif - -/* - * Whether the OpenSSL version has the API needed to support sharing an - * X509_STORE between connections. The API is: - * * `X509_STORE_up_ref` -- Introduced: OpenSSL 1.1.0. - */ -#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* OpenSSL >= 1.1.0 */ -#define HAVE_SSL_X509_STORE_SHARE -#endif - -/* What API version do we use? */ -#if defined(LIBRESSL_VERSION_NUMBER) -#define USE_PRE_1_1_API (LIBRESSL_VERSION_NUMBER < 0x2070000f) -#else /* !LIBRESSL_VERSION_NUMBER */ -#define USE_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L) -#endif /* !LIBRESSL_VERSION_NUMBER */ - -struct ossl_ssl_backend_data { +struct ssl_backend_data { + struct Curl_easy *logger; /* transfer handle to pass trace logs to, only + using sockindex 0 */ /* these ones requires specific SSL-types */ SSL_CTX* ctx; SSL* handle; X509* server_cert; - BIO_METHOD *bio_method; - CURLcode io_result; /* result of last BIO cfilter operation */ #ifndef HAVE_KEYLOG_CALLBACK /* Set to true once a valid keylog entry has been created to avoid dupes. */ bool keylog_done; #endif - bool x509_store_setup; /* x509 store has been set up */ }; -#if defined(HAVE_SSL_X509_STORE_SHARE) -struct multi_ssl_backend_data { - char *CAfile; /* CAfile path used to generate X509 store */ - X509_STORE *store; /* cached X509 store or NULL if none */ - struct curltime time; /* when the cached store was created */ -}; -#endif /* HAVE_SSL_X509_STORE_SHARE */ - #define push_certinfo(_label, _num) \ do { \ long info_len = BIO_get_mem_data(mem, &ptr); \ @@ -365,8 +327,8 @@ static int asn1_object_dump(ASN1_OBJECT *a, char *buf, size_t len) } static void X509V3_ext(struct Curl_easy *data, - int certnum, - CONST_EXTS STACK_OF(X509_EXTENSION) *exts) + int certnum, + CONST_EXTS STACK_OF(X509_EXTENSION) *exts) { int i; @@ -398,7 +360,7 @@ static void X509V3_ext(struct Curl_easy *data, } } -#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) +#ifdef OPENSSL_IS_BORINGSSL typedef size_t numcert_t; #else typedef int numcert_t; @@ -622,7 +584,7 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl) FREE_PKEY_PARAM_BIGNUM(q); FREE_PKEY_PARAM_BIGNUM(g); FREE_PKEY_PARAM_BIGNUM(pub_key); - } + } break; } } @@ -648,175 +610,9 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl) #ifdef USE_OPENSSL -#if USE_PRE_1_1_API -#if !defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER < 0x2070000fL -#define BIO_set_init(x,v) ((x)->init=(v)) -#define BIO_get_data(x) ((x)->ptr) -#define BIO_set_data(x,v) ((x)->ptr=(v)) -#endif -#define BIO_get_shutdown(x) ((x)->shutdown) -#define BIO_set_shutdown(x,v) ((x)->shutdown=(v)) -#endif /* USE_PRE_1_1_API */ - -static int bio_cf_create(BIO *bio) -{ - BIO_set_shutdown(bio, 1); - BIO_set_init(bio, 1); -#if USE_PRE_1_1_API - bio->num = -1; -#endif - BIO_set_data(bio, NULL); - return 1; -} - -static int bio_cf_destroy(BIO *bio) -{ - if(!bio) - return 0; - return 1; -} - -static long bio_cf_ctrl(BIO *bio, int cmd, long num, void *ptr) -{ - struct Curl_cfilter *cf = BIO_get_data(bio); - long ret = 1; - - (void)cf; - (void)ptr; - switch(cmd) { - case BIO_CTRL_GET_CLOSE: - ret = (long)BIO_get_shutdown(bio); - break; - case BIO_CTRL_SET_CLOSE: - BIO_set_shutdown(bio, (int)num); - break; - case BIO_CTRL_FLUSH: - /* we do no delayed writes, but if we ever would, this - * needs to trigger it. */ - ret = 1; - break; - case BIO_CTRL_DUP: - ret = 1; - break; -#ifdef BIO_CTRL_EOF - case BIO_CTRL_EOF: - /* EOF has been reached on input? */ - return (!cf->next || !cf->next->connected); -#endif - default: - ret = 0; - break; - } - return ret; -} - -static int bio_cf_out_write(BIO *bio, const char *buf, int blen) -{ - struct Curl_cfilter *cf = BIO_get_data(bio); - struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nwritten; - CURLcode result = CURLE_SEND_ERROR; - - DEBUGASSERT(data); - nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result); - DEBUGF(LOG_CF(data, cf, "bio_cf_out_write(len=%d) -> %d, err=%d", - blen, (int)nwritten, result)); - BIO_clear_retry_flags(bio); - backend->io_result = result; - if(nwritten < 0) { - if(CURLE_AGAIN == result) - BIO_set_retry_write(bio); - } - return (int)nwritten; -} - -static int bio_cf_in_read(BIO *bio, char *buf, int blen) -{ - struct Curl_cfilter *cf = BIO_get_data(bio); - struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nread; - CURLcode result = CURLE_RECV_ERROR; - - DEBUGASSERT(data); - /* OpenSSL catches this case, so should we. */ - if(!buf) - return 0; - - nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result); - DEBUGF(LOG_CF(data, cf, "bio_cf_in_read(len=%d) -> %d, err=%d", - blen, (int)nread, result)); - BIO_clear_retry_flags(bio); - backend->io_result = result; - if(nread < 0) { - if(CURLE_AGAIN == result) - BIO_set_retry_read(bio); - } - - /* Before returning server replies to the SSL instance, we need - * to have setup the x509 store or verification will fail. */ - if(!backend->x509_store_setup) { - result = Curl_ssl_setup_x509_store(cf, data, backend->ctx); - if(result) { - backend->io_result = result; - return -1; - } - backend->x509_store_setup = TRUE; - } - - return (int)nread; -} - -#if USE_PRE_1_1_API - -static BIO_METHOD bio_cf_meth_1_0 = { - BIO_TYPE_MEM, - "OpenSSL CF BIO", - bio_cf_out_write, - bio_cf_in_read, - NULL, /* puts is never called */ - NULL, /* gets is never called */ - bio_cf_ctrl, - bio_cf_create, - bio_cf_destroy, - NULL -}; - -static BIO_METHOD *bio_cf_method_create(void) -{ - return &bio_cf_meth_1_0; -} - -#define bio_cf_method_free(m) Curl_nop_stmt - -#else - -static BIO_METHOD *bio_cf_method_create(void) -{ - BIO_METHOD *m = BIO_meth_new(BIO_TYPE_MEM, "OpenSSL CF BIO"); - if(m) { - BIO_meth_set_write(m, &bio_cf_out_write); - BIO_meth_set_read(m, &bio_cf_in_read); - BIO_meth_set_ctrl(m, &bio_cf_ctrl); - BIO_meth_set_create(m, &bio_cf_create); - BIO_meth_set_destroy(m, &bio_cf_destroy); - } - return m; -} - -static void bio_cf_method_free(BIO_METHOD *m) -{ - if(m) - BIO_meth_free(m); -} - -#endif - +static bool ossl_associate_connection(struct Curl_easy *data, + struct connectdata *conn, + int sockindex); /* * Number of bytes to read from the random number seed file. This must be @@ -849,9 +645,9 @@ ossl_log_tls12_secret(const SSL *ssl, bool *keylog_done) if(!session || *keylog_done) return; -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ - !(defined(LIBRESSL_VERSION_NUMBER) && \ - LIBRESSL_VERSION_NUMBER < 0x20700000L) +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !(defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) /* ssl->s3 is not checked in openssl 1.1.0-pre6, but let's assume that * we have a valid SSL context if we have a non-NULL session. */ SSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE); @@ -915,33 +711,20 @@ static const char *SSL_ERROR_to_str(int err) } } -static size_t ossl_version(char *buffer, size_t size); - /* Return error string for last OpenSSL error */ static char *ossl_strerror(unsigned long error, char *buf, size_t size) { - size_t len; - DEBUGASSERT(size); - *buf = '\0'; - - len = ossl_version(buf, size); - DEBUGASSERT(len < (size - 2)); - if(len < (size - 2)) { - buf += len; - size -= (len + 2); - *buf++ = ':'; - *buf++ = ' '; + if(size) *buf = '\0'; - } -#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) +#ifdef OPENSSL_IS_BORINGSSL ERR_error_string_n((uint32_t)error, buf, size); #else ERR_error_string_n(error, buf, size); #endif - if(!*buf) { + if(size > 1 && !*buf) { strncpy(buf, (error ? "Unknown error" : "No error"), size); buf[size - 1] = '\0'; } @@ -949,6 +732,54 @@ static char *ossl_strerror(unsigned long error, char *buf, size_t size) return buf; } +/* Return an extra data index for the transfer data. + * This index can be used with SSL_get_ex_data() and SSL_set_ex_data(). + */ +static int ossl_get_ssl_data_index(void) +{ + static int ssl_ex_data_data_index = -1; + if(ssl_ex_data_data_index < 0) { + ssl_ex_data_data_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); + } + return ssl_ex_data_data_index; +} + +/* Return an extra data index for the connection data. + * This index can be used with SSL_get_ex_data() and SSL_set_ex_data(). + */ +static int ossl_get_ssl_conn_index(void) +{ + static int ssl_ex_data_conn_index = -1; + if(ssl_ex_data_conn_index < 0) { + ssl_ex_data_conn_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); + } + return ssl_ex_data_conn_index; +} + +/* Return an extra data index for the sockindex. + * This index can be used with SSL_get_ex_data() and SSL_set_ex_data(). + */ +static int ossl_get_ssl_sockindex_index(void) +{ + static int sockindex_index = -1; + if(sockindex_index < 0) { + sockindex_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); + } + return sockindex_index; +} + +/* Return an extra data index for proxy boolean. + * This index can be used with SSL_get_ex_data() and SSL_set_ex_data(). + */ +static int ossl_get_proxy_index(void) +{ + static int proxy_index = -1; + if(proxy_index < 0) { + proxy_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); + } + return proxy_index; +} + static int passwd_callback(char *buf, int num, int encrypting, void *global_passwd) { @@ -1157,7 +988,7 @@ SSL_CTX_use_certificate_blob(SSL_CTX *ctx, const struct curl_blob *blob, } ret = SSL_CTX_use_certificate(ctx, x); -end: + end: X509_free(x); BIO_free(in); return ret; @@ -1165,7 +996,7 @@ end: static int SSL_CTX_use_PrivateKey_blob(SSL_CTX *ctx, const struct curl_blob *blob, - int type, const char *key_passwd) + int type, const char *key_passwd) { int ret = 0; EVP_PKEY *pkey = NULL; @@ -1188,7 +1019,7 @@ SSL_CTX_use_PrivateKey_blob(SSL_CTX *ctx, const struct curl_blob *blob, } ret = SSL_CTX_use_PrivateKey(ctx, pkey); EVP_PKEY_free(pkey); -end: + end: BIO_free(in); return ret; } @@ -1199,8 +1030,8 @@ SSL_CTX_use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob, { /* SSL_CTX_add1_chain_cert introduced in OpenSSL 1.0.2 */ #if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) && /* OpenSSL 1.0.2 or later */ \ - !(defined(LIBRESSL_VERSION_NUMBER) && \ - (LIBRESSL_VERSION_NUMBER < 0x2090100fL)) /* LibreSSL 2.9.1 or later */ + !(defined(LIBRESSL_VERSION_NUMBER) && \ + (LIBRESSL_VERSION_NUMBER < 0x2090100fL)) /* LibreSSL 2.9.1 or later */ int ret = 0; X509 *x = NULL; void *passwd_callback_userdata = (void *)key_passwd; @@ -1225,7 +1056,7 @@ SSL_CTX_use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob, if(ret) { X509 *ca; - sslerr_t err; + unsigned long err; if(!SSL_CTX_clear_chain_certs(ctx)) { ret = 0; @@ -1251,7 +1082,7 @@ SSL_CTX_use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob, ret = 0; } -end: + end: X509_free(x); BIO_free(in); return ret; @@ -1319,7 +1150,7 @@ int cert_stuff(struct Curl_easy *data, cert_use_result = cert_blob ? SSL_CTX_use_certificate_blob(ctx, cert_blob, file_type, key_passwd) : - SSL_CTX_use_certificate_file(ctx, cert_file, file_type); + SSL_CTX_use_certificate_file(ctx, cert_file, file_type); if(cert_use_result != 1) { failf(data, "could not load ASN1 client certificate from %s, " OSSL_PACKAGE @@ -1333,67 +1164,67 @@ int cert_stuff(struct Curl_easy *data, break; case SSL_FILETYPE_ENGINE: #if defined(USE_OPENSSL_ENGINE) && defined(ENGINE_CTRL_GET_CMD_FROM_NAME) - { - /* Implicitly use pkcs11 engine if none was provided and the - * cert_file is a PKCS#11 URI */ - if(!data->state.engine) { - if(is_pkcs11_uri(cert_file)) { - if(ossl_set_engine(data, "pkcs11") != CURLE_OK) { - return 0; + { + /* Implicitly use pkcs11 engine if none was provided and the + * cert_file is a PKCS#11 URI */ + if(!data->state.engine) { + if(is_pkcs11_uri(cert_file)) { + if(ossl_set_engine(data, "pkcs11") != CURLE_OK) { + return 0; + } } } - } - if(data->state.engine) { - const char *cmd_name = "LOAD_CERT_CTRL"; - struct { - const char *cert_id; - X509 *cert; - } params; + if(data->state.engine) { + const char *cmd_name = "LOAD_CERT_CTRL"; + struct { + const char *cert_id; + X509 *cert; + } params; - params.cert_id = cert_file; - params.cert = NULL; + params.cert_id = cert_file; + params.cert = NULL; - /* Does the engine supports LOAD_CERT_CTRL ? */ - if(!ENGINE_ctrl(data->state.engine, ENGINE_CTRL_GET_CMD_FROM_NAME, - 0, (void *)cmd_name, NULL)) { - failf(data, "ssl engine does not support loading certificates"); - return 0; - } + /* Does the engine supports LOAD_CERT_CTRL ? */ + if(!ENGINE_ctrl(data->state.engine, ENGINE_CTRL_GET_CMD_FROM_NAME, + 0, (void *)cmd_name, NULL)) { + failf(data, "ssl engine does not support loading certificates"); + return 0; + } - /* Load the certificate from the engine */ - if(!ENGINE_ctrl_cmd(data->state.engine, cmd_name, - 0, ¶ms, NULL, 1)) { - failf(data, "ssl engine cannot load client cert with id" - " '%s' [%s]", cert_file, - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer))); - return 0; - } + /* Load the certificate from the engine */ + if(!ENGINE_ctrl_cmd(data->state.engine, cmd_name, + 0, ¶ms, NULL, 1)) { + failf(data, "ssl engine cannot load client cert with id" + " '%s' [%s]", cert_file, + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + return 0; + } - if(!params.cert) { - failf(data, "ssl engine didn't initialized the certificate " - "properly."); - return 0; - } + if(!params.cert) { + failf(data, "ssl engine didn't initialized the certificate " + "properly."); + return 0; + } - if(SSL_CTX_use_certificate(ctx, params.cert) != 1) { - failf(data, "unable to set client certificate [%s]", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer))); + if(SSL_CTX_use_certificate(ctx, params.cert) != 1) { + failf(data, "unable to set client certificate [%s]", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + return 0; + } + X509_free(params.cert); /* we don't need the handle any more... */ + } + else { + failf(data, "crypto engine not set, can't load certificate"); return 0; } - X509_free(params.cert); /* we don't need the handle any more... */ - } - else { - failf(data, "crypto engine not set, can't load certificate"); - return 0; } - } - break; + break; #else - failf(data, "file type ENG for certificate not implemented"); - return 0; + failf(data, "file type ENG for certificate not implemented"); + return 0; #endif case SSL_FILETYPE_PKCS12: @@ -1500,7 +1331,7 @@ int cert_stuff(struct Curl_easy *data, } cert_done = 1; -fail: + fail: EVP_PKEY_free(pri); X509_free(x509); sk_X509_pop_free(ca, X509_free); @@ -1528,7 +1359,7 @@ fail: case SSL_FILETYPE_ASN1: cert_use_result = key_blob ? SSL_CTX_use_PrivateKey_blob(ctx, key_blob, file_type, key_passwd) : - SSL_CTX_use_PrivateKey_file(ctx, key_file, file_type); + SSL_CTX_use_PrivateKey_file(ctx, key_file, file_type); if(cert_use_result != 1) { failf(data, "unable to set private key file: '%s' type %s", key_file?key_file:"(memory blob)", key_type?key_type:"PEM"); @@ -1537,57 +1368,57 @@ fail: break; case SSL_FILETYPE_ENGINE: #ifdef USE_OPENSSL_ENGINE - { - EVP_PKEY *priv_key = NULL; - - /* Implicitly use pkcs11 engine if none was provided and the - * key_file is a PKCS#11 URI */ - if(!data->state.engine) { - if(is_pkcs11_uri(key_file)) { - if(ossl_set_engine(data, "pkcs11") != CURLE_OK) { - return 0; + { /* XXXX still needs some work */ + EVP_PKEY *priv_key = NULL; + + /* Implicitly use pkcs11 engine if none was provided and the + * key_file is a PKCS#11 URI */ + if(!data->state.engine) { + if(is_pkcs11_uri(key_file)) { + if(ossl_set_engine(data, "pkcs11") != CURLE_OK) { + return 0; + } } } - } - if(data->state.engine) { - UI_METHOD *ui_method = - UI_create_method((char *)"curl user interface"); - if(!ui_method) { - failf(data, "unable do create " OSSL_PACKAGE - " user-interface method"); - return 0; - } - UI_method_set_opener(ui_method, UI_method_get_opener(UI_OpenSSL())); - UI_method_set_closer(ui_method, UI_method_get_closer(UI_OpenSSL())); - UI_method_set_reader(ui_method, ssl_ui_reader); - UI_method_set_writer(ui_method, ssl_ui_writer); - /* the typecast below was added to please mingw32 */ - priv_key = (EVP_PKEY *) - ENGINE_load_private_key(data->state.engine, key_file, - ui_method, - key_passwd); - UI_destroy_method(ui_method); - if(!priv_key) { - failf(data, "failed to load private key from crypto engine"); - return 0; + if(data->state.engine) { + UI_METHOD *ui_method = + UI_create_method((char *)"curl user interface"); + if(!ui_method) { + failf(data, "unable do create " OSSL_PACKAGE + " user-interface method"); + return 0; + } + UI_method_set_opener(ui_method, UI_method_get_opener(UI_OpenSSL())); + UI_method_set_closer(ui_method, UI_method_get_closer(UI_OpenSSL())); + UI_method_set_reader(ui_method, ssl_ui_reader); + UI_method_set_writer(ui_method, ssl_ui_writer); + /* the typecast below was added to please mingw32 */ + priv_key = (EVP_PKEY *) + ENGINE_load_private_key(data->state.engine, key_file, + ui_method, + key_passwd); + UI_destroy_method(ui_method); + if(!priv_key) { + failf(data, "failed to load private key from crypto engine"); + return 0; + } + if(SSL_CTX_use_PrivateKey(ctx, priv_key) != 1) { + failf(data, "unable to set private key"); + EVP_PKEY_free(priv_key); + return 0; + } + EVP_PKEY_free(priv_key); /* we don't need the handle any more... */ } - if(SSL_CTX_use_PrivateKey(ctx, priv_key) != 1) { - failf(data, "unable to set private key"); - EVP_PKEY_free(priv_key); + else { + failf(data, "crypto engine not set, can't load private key"); return 0; } - EVP_PKEY_free(priv_key); /* we don't need the handle any more... */ } - else { - failf(data, "crypto engine not set, can't load private key"); - return 0; - } - } - break; + break; #else - failf(data, "file type ENG for private key not supported"); - return 0; + failf(data, "file type ENG for private key not supported"); + return 0; #endif case SSL_FILETYPE_PKCS12: if(!cert_done) { @@ -1616,8 +1447,8 @@ fail: EVP_PKEY_free(pktmp); } -#if !defined(OPENSSL_NO_RSA) && !defined(OPENSSL_IS_BORINGSSL) && \ - !defined(OPENSSL_NO_DEPRECATED_3_0) +#if !defined(OPENSSL_NO_RSA) && !defined(OPENSSL_IS_BORINGSSL) && \ + !defined(OPENSSL_NO_DEPRECATED_3_0) { /* If RSA is used, don't check the private key if its flags indicate * it doesn't support it. */ @@ -1749,14 +1580,19 @@ static int ossl_init(void) Curl_tls_keylog_open(); + /* Initialize the extra data indexes */ + if(ossl_get_ssl_data_index() < 0 || ossl_get_ssl_conn_index() < 0 || + ossl_get_ssl_sockindex_index() < 0 || ossl_get_proxy_index() < 0) + return 0; + return 1; } /* Global cleanup */ static void ossl_cleanup(void) { -#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \ - !defined(LIBRESSL_VERSION_NUMBER) +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \ + !defined(LIBRESSL_VERSION_NUMBER) /* OpenSSL 1.1 deprecates all these cleanup functions and turns them into no-ops in OpenSSL 1.0 compatibility mode */ #else @@ -1789,6 +1625,58 @@ static void ossl_cleanup(void) Curl_tls_keylog_close(); } +/* + * This function is used to determine connection status. + * + * Return codes: + * 1 means the connection is still in place + * 0 means the connection has been closed + * -1 means the connection status is unknown + */ +static int ossl_check_cxn(struct connectdata *conn) +{ + /* SSL_peek takes data out of the raw recv buffer without peeking so we use + recv MSG_PEEK instead. Bug #795 */ +#ifdef MSG_PEEK + char buf; + ssize_t nread; + nread = recv((RECV_TYPE_ARG1)conn->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf, + (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK); + if(nread == 0) + return 0; /* connection has been closed */ + if(nread == 1) + return 1; /* connection still in place */ + else if(nread == -1) { + int err = SOCKERRNO; + if(err == EINPROGRESS || +#if defined(EAGAIN) && (EAGAIN != EWOULDBLOCK) + err == EAGAIN || +#endif + err == EWOULDBLOCK) + return 1; /* connection still in place */ + if(err == ECONNRESET || +#ifdef ECONNABORTED + err == ECONNABORTED || +#endif +#ifdef ENETDOWN + err == ENETDOWN || +#endif +#ifdef ENETRESET + err == ENETRESET || +#endif +#ifdef ESHUTDOWN + err == ESHUTDOWN || +#endif +#ifdef ETIMEDOUT + err == ETIMEDOUT || +#endif + err == ENOTCONN) + return 0; /* connection has been closed */ + } +#endif + return -1; /* connection status unknown */ +} + /* Selects an OpenSSL crypto engine */ static CURLcode ossl_set_engine(struct Curl_easy *data, const char *engine) @@ -1878,25 +1766,33 @@ static struct curl_slist *ossl_engines_list(struct Curl_easy *data) return list; } -static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data) +#define set_logger(conn, data) \ + conn->ssl[0].backend->logger = data + +static void ossl_closeone(struct Curl_easy *data, + struct connectdata *conn, + struct ssl_connect_data *connssl) { - struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ssl_backend_data *backend = connssl->backend; - (void)data; DEBUGASSERT(backend); if(backend->handle) { - if(cf->next && cf->next->connected) { - char buf[32]; - /* Maybe the server has already sent a close notify alert. - Read it to avoid an RST on the TCP connection. */ - (void)SSL_read(backend->handle, buf, (int)sizeof(buf)); + char buf[32]; + set_logger(conn, data); + /* + * The conn->sock[0] socket is passed to openssl with SSL_set_fd(). Make + * sure the socket is not closed before calling OpenSSL functions that + * will use it. + */ + DEBUGASSERT(conn->sock[FIRSTSOCKET] != CURL_SOCKET_BAD); - (void)SSL_shutdown(backend->handle); - SSL_set_connect_state(backend->handle); - } + /* Maybe the server has already sent a close notify alert. + Read it to avoid an RST on the TCP connection. */ + (void)SSL_read(backend->handle, buf, (int)sizeof(buf)); + + (void)SSL_shutdown(backend->handle); + SSL_set_connect_state(backend->handle); SSL_free(backend->handle); backend->handle = NULL; @@ -1904,32 +1800,38 @@ static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data) if(backend->ctx) { SSL_CTX_free(backend->ctx); backend->ctx = NULL; - backend->x509_store_setup = FALSE; - } - if(backend->bio_method) { - bio_cf_method_free(backend->bio_method); - backend->bio_method = NULL; } } /* + * This function is called when an SSL connection is closed. + */ +static void ossl_close(struct Curl_easy *data, struct connectdata *conn, + int sockindex) +{ + ossl_closeone(data, conn, &conn->ssl[sockindex]); +#ifndef CURL_DISABLE_PROXY + ossl_closeone(data, conn, &conn->proxy_ssl[sockindex]); +#endif +} + +/* * This function is called to shut down the SSL layer but keep the * socket open (CCC - Clear Command Channel) */ -static int ossl_shutdown(struct Curl_cfilter *cf, - struct Curl_easy *data) +static int ossl_shutdown(struct Curl_easy *data, + struct connectdata *conn, int sockindex) { int retval = 0; - struct ssl_connect_data *connssl = cf->ctx; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; char buf[256]; /* We will use this for the OpenSSL error buffer, so it has to be at least 256 bytes long. */ unsigned long sslerror; - int nread; + ssize_t nread; int buffsize; int err; bool done = FALSE; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ssl_backend_data *backend = connssl->backend; int loop = 10; DEBUGASSERT(backend); @@ -1941,21 +1843,21 @@ static int ossl_shutdown(struct Curl_cfilter *cf, we do not send one. Let's hope other servers do the same... */ if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE) - (void)SSL_shutdown(backend->handle); + (void)SSL_shutdown(backend->handle); #endif if(backend->handle) { buffsize = (int)sizeof(buf); while(!done && loop--) { - int what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), + int what = SOCKET_READABLE(conn->sock[sockindex], SSL_SHUTDOWN_TIMEOUT); if(what > 0) { ERR_clear_error(); /* Something to read, let's do it and hope that it is the close notify alert from the server */ - nread = SSL_read(backend->handle, buf, buffsize); - err = SSL_get_error(backend->handle, nread); + nread = (ssize_t)SSL_read(backend->handle, buf, buffsize); + err = SSL_get_error(backend->handle, (int)nread); switch(err) { case SSL_ERROR_NONE: /* this is not an error */ @@ -2042,7 +1944,7 @@ static void ossl_close_all(struct Curl_easy *data) #else (void)data; #endif -#if !defined(HAVE_ERR_REMOVE_THREAD_STATE_DEPRECATED) && \ +#if !defined(HAVE_ERR_REMOVE_THREAD_STATE_DEPRECATED) && \ defined(HAVE_ERR_REMOVE_THREAD_STATE) /* OpenSSL 1.0.1 and 1.0.2 build an error queue that is stored per-thread so we need to clean it here in case the thread will be killed. All OpenSSL @@ -2070,28 +1972,12 @@ static bool subj_alt_hostcheck(struct Curl_easy *data, #endif if(Curl_cert_hostcheck(match_pattern, matchlen, hostname, hostlen)) { infof(data, " subjectAltName: host \"%s\" matched cert's \"%s\"", - dispname, match_pattern); + dispname, match_pattern); return TRUE; } return FALSE; } -static CURLcode -ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, - X509 *server_cert, const char *hostname, - const char *dispname); - -CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, - X509 *server_cert) -{ - const char *hostname, *dispname; - int port; - - (void)conn; - Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &dispname, &port); - return ossl_verifyhost(data, conn, server_cert, hostname, dispname); -} - /* Quote from RFC2818 section 3.1 "Server Identity" If a subjectAltName extension of type dNSName is present, that MUST @@ -2114,10 +2000,8 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, This function is now used from ngtcp2 (QUIC) as well. */ -static CURLcode -ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, - X509 *server_cert, const char *hostname, - const char *dispname) +CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, + X509 *server_cert) { bool matched = FALSE; int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */ @@ -2131,15 +2015,9 @@ ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, CURLcode result = CURLE_OK; bool dNSName = FALSE; /* if a dNSName field exists in the cert */ bool iPAddress = FALSE; /* if a iPAddress field exists in the cert */ - size_t hostlen; - - (void)conn; - hostlen = strlen(hostname); - -#ifndef ENABLE_IPV6 - /* Silence compiler warnings for unused params */ - (void) conn; -#endif + const char * const hostname = SSL_HOST_NAME(); + const char * const dispname = SSL_HOST_DISPNAME(); + size_t hostlen = strlen(hostname); #ifdef ENABLE_IPV6 if(conn->bits.ipv6_ip && @@ -2158,7 +2036,7 @@ ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, altnames = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL); if(altnames) { -#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) +#ifdef OPENSSL_IS_BORINGSSL size_t numalts; size_t i; #else @@ -2314,11 +2192,10 @@ ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, } #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ - !defined(OPENSSL_NO_OCSP) -static CURLcode verifystatus(struct Curl_cfilter *cf, - struct Curl_easy *data) + !defined(OPENSSL_NO_OCSP) +static CURLcode verifystatus(struct Curl_easy *data, + struct ssl_connect_data *connssl) { - struct ssl_connect_data *connssl = cf->ctx; int i, ocsp_status; unsigned char *status; const unsigned char *p; @@ -2327,8 +2204,7 @@ static CURLcode verifystatus(struct Curl_cfilter *cf, OCSP_BASICRESP *br = NULL; X509_STORE *st = NULL; STACK_OF(X509) *ch = NULL; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ssl_backend_data *backend = connssl->backend; X509 *cert; OCSP_CERTID *id = NULL; int cert_status, crl_reason; @@ -2489,81 +2365,81 @@ static const char *ssl_msg_type(int ssl_ver, int msg) #ifdef SSL2_VERSION_MAJOR if(ssl_ver == SSL2_VERSION_MAJOR) { switch(msg) { - case SSL2_MT_ERROR: - return "Error"; - case SSL2_MT_CLIENT_HELLO: - return "Client hello"; - case SSL2_MT_CLIENT_MASTER_KEY: - return "Client key"; - case SSL2_MT_CLIENT_FINISHED: - return "Client finished"; - case SSL2_MT_SERVER_HELLO: - return "Server hello"; - case SSL2_MT_SERVER_VERIFY: - return "Server verify"; - case SSL2_MT_SERVER_FINISHED: - return "Server finished"; - case SSL2_MT_REQUEST_CERTIFICATE: - return "Request CERT"; - case SSL2_MT_CLIENT_CERTIFICATE: - return "Client CERT"; + case SSL2_MT_ERROR: + return "Error"; + case SSL2_MT_CLIENT_HELLO: + return "Client hello"; + case SSL2_MT_CLIENT_MASTER_KEY: + return "Client key"; + case SSL2_MT_CLIENT_FINISHED: + return "Client finished"; + case SSL2_MT_SERVER_HELLO: + return "Server hello"; + case SSL2_MT_SERVER_VERIFY: + return "Server verify"; + case SSL2_MT_SERVER_FINISHED: + return "Server finished"; + case SSL2_MT_REQUEST_CERTIFICATE: + return "Request CERT"; + case SSL2_MT_CLIENT_CERTIFICATE: + return "Client CERT"; } } else #endif if(ssl_ver == SSL3_VERSION_MAJOR) { switch(msg) { - case SSL3_MT_HELLO_REQUEST: - return "Hello request"; - case SSL3_MT_CLIENT_HELLO: - return "Client hello"; - case SSL3_MT_SERVER_HELLO: - return "Server hello"; + case SSL3_MT_HELLO_REQUEST: + return "Hello request"; + case SSL3_MT_CLIENT_HELLO: + return "Client hello"; + case SSL3_MT_SERVER_HELLO: + return "Server hello"; #ifdef SSL3_MT_NEWSESSION_TICKET - case SSL3_MT_NEWSESSION_TICKET: - return "Newsession Ticket"; -#endif - case SSL3_MT_CERTIFICATE: - return "Certificate"; - case SSL3_MT_SERVER_KEY_EXCHANGE: - return "Server key exchange"; - case SSL3_MT_CLIENT_KEY_EXCHANGE: - return "Client key exchange"; - case SSL3_MT_CERTIFICATE_REQUEST: - return "Request CERT"; - case SSL3_MT_SERVER_DONE: - return "Server finished"; - case SSL3_MT_CERTIFICATE_VERIFY: - return "CERT verify"; - case SSL3_MT_FINISHED: - return "Finished"; + case SSL3_MT_NEWSESSION_TICKET: + return "Newsession Ticket"; +#endif + case SSL3_MT_CERTIFICATE: + return "Certificate"; + case SSL3_MT_SERVER_KEY_EXCHANGE: + return "Server key exchange"; + case SSL3_MT_CLIENT_KEY_EXCHANGE: + return "Client key exchange"; + case SSL3_MT_CERTIFICATE_REQUEST: + return "Request CERT"; + case SSL3_MT_SERVER_DONE: + return "Server finished"; + case SSL3_MT_CERTIFICATE_VERIFY: + return "CERT verify"; + case SSL3_MT_FINISHED: + return "Finished"; #ifdef SSL3_MT_CERTIFICATE_STATUS - case SSL3_MT_CERTIFICATE_STATUS: - return "Certificate Status"; + case SSL3_MT_CERTIFICATE_STATUS: + return "Certificate Status"; #endif #ifdef SSL3_MT_ENCRYPTED_EXTENSIONS - case SSL3_MT_ENCRYPTED_EXTENSIONS: - return "Encrypted Extensions"; + case SSL3_MT_ENCRYPTED_EXTENSIONS: + return "Encrypted Extensions"; #endif #ifdef SSL3_MT_SUPPLEMENTAL_DATA - case SSL3_MT_SUPPLEMENTAL_DATA: - return "Supplemental data"; + case SSL3_MT_SUPPLEMENTAL_DATA: + return "Supplemental data"; #endif #ifdef SSL3_MT_END_OF_EARLY_DATA - case SSL3_MT_END_OF_EARLY_DATA: - return "End of early data"; + case SSL3_MT_END_OF_EARLY_DATA: + return "End of early data"; #endif #ifdef SSL3_MT_KEY_UPDATE - case SSL3_MT_KEY_UPDATE: - return "Key update"; + case SSL3_MT_KEY_UPDATE: + return "Key update"; #endif #ifdef SSL3_MT_NEXT_PROTO - case SSL3_MT_NEXT_PROTO: - return "Next protocol"; + case SSL3_MT_NEXT_PROTO: + return "Next protocol"; #endif #ifdef SSL3_MT_MESSAGE_HASH - case SSL3_MT_MESSAGE_HASH: - return "Message hash"; + case SSL3_MT_MESSAGE_HASH: + return "Message hash"; #endif } } @@ -2597,15 +2473,18 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, const void *buf, size_t len, SSL *ssl, void *userp) { - const char *verstr = "???"; - struct Curl_cfilter *cf = userp; - struct Curl_easy *data = NULL; char unknown[32]; + const char *verstr = NULL; + struct connectdata *conn = userp; + struct ssl_connect_data *connssl = &conn->ssl[0]; + struct ssl_backend_data *backend = connssl->backend; + struct Curl_easy *data = NULL; - if(!cf) - return; - data = CF_DATA_CURRENT(cf); - if(!data || !data->set.fdebug || (direction && direction != 1)) + DEBUGASSERT(backend); + data = backend->logger; + + if(!conn || !data || !data->set.fdebug || + (direction != 0 && direction != 1)) return; switch(ssl_ver) { @@ -2650,9 +2529,6 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, * For TLS 1.3, skip notification of the decrypted inner Content-Type. */ if(ssl_ver -#ifdef SSL3_RT_HEADER - && content_type != SSL3_RT_HEADER -#endif #ifdef SSL3_RT_INNER_CONTENT_TYPE && content_type != SSL3_RT_INNER_CONTENT_TYPE #endif @@ -2687,8 +2563,7 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, msg_name = ssl_msg_type(ssl_ver, msg_type); } - txt_len = msnprintf(ssl_buf, sizeof(ssl_buf), - "%s (%s), %s, %s (%d):\n", + txt_len = msnprintf(ssl_buf, sizeof(ssl_buf), "%s (%s), %s, %s (%d):\n", verstr, direction?"OUT":"IN", tls_rt_name, msg_name, msg_type); if(0 <= txt_len && (unsigned)txt_len < sizeof(ssl_buf)) { @@ -2713,24 +2588,21 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, /* Check for OpenSSL 1.0.2 which has ALPN support. */ #undef HAS_ALPN -#if OPENSSL_VERSION_NUMBER >= 0x10002000L \ - && !defined(OPENSSL_NO_TLSEXT) +#if OPENSSL_VERSION_NUMBER >= 0x10002000L \ + && !defined(OPENSSL_NO_TLSEXT) # define HAS_ALPN 1 #endif #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */ static CURLcode -ossl_set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx) +set_ssl_version_min_max(SSL_CTX *ctx, struct connectdata *conn) { - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); /* first, TLS min version... */ - long curl_ssl_version_min = conn_config->version; + long curl_ssl_version_min = SSL_CONN_CONFIG(version); long curl_ssl_version_max; /* convert curl min SSL version option to OpenSSL constant */ -#if (defined(OPENSSL_IS_BORINGSSL) || \ - defined(OPENSSL_IS_AWSLC) || \ - defined(LIBRESSL_VERSION_NUMBER)) +#if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER) uint16_t ossl_ssl_version_min = 0; uint16_t ossl_ssl_version_max = 0; #else @@ -2738,22 +2610,22 @@ ossl_set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx) long ossl_ssl_version_max = 0; #endif switch(curl_ssl_version_min) { - case CURL_SSLVERSION_TLSv1: /* TLS 1.x */ - case CURL_SSLVERSION_TLSv1_0: - ossl_ssl_version_min = TLS1_VERSION; - break; - case CURL_SSLVERSION_TLSv1_1: - ossl_ssl_version_min = TLS1_1_VERSION; - break; - case CURL_SSLVERSION_TLSv1_2: - ossl_ssl_version_min = TLS1_2_VERSION; - break; - case CURL_SSLVERSION_TLSv1_3: + case CURL_SSLVERSION_TLSv1: /* TLS 1.x */ + case CURL_SSLVERSION_TLSv1_0: + ossl_ssl_version_min = TLS1_VERSION; + break; + case CURL_SSLVERSION_TLSv1_1: + ossl_ssl_version_min = TLS1_1_VERSION; + break; + case CURL_SSLVERSION_TLSv1_2: + ossl_ssl_version_min = TLS1_2_VERSION; + break; + case CURL_SSLVERSION_TLSv1_3: #ifdef TLS1_3_VERSION - ossl_ssl_version_min = TLS1_3_VERSION; - break; + ossl_ssl_version_min = TLS1_3_VERSION; + break; #else - return CURLE_NOT_BUILT_IN; + return CURLE_NOT_BUILT_IN; #endif } @@ -2770,33 +2642,33 @@ ossl_set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx) } /* ... then, TLS max version */ - curl_ssl_version_max = conn_config->version_max; + curl_ssl_version_max = SSL_CONN_CONFIG(version_max); /* convert curl max SSL version option to OpenSSL constant */ switch(curl_ssl_version_max) { - case CURL_SSLVERSION_MAX_TLSv1_0: - ossl_ssl_version_max = TLS1_VERSION; - break; - case CURL_SSLVERSION_MAX_TLSv1_1: - ossl_ssl_version_max = TLS1_1_VERSION; - break; - case CURL_SSLVERSION_MAX_TLSv1_2: - ossl_ssl_version_max = TLS1_2_VERSION; - break; + case CURL_SSLVERSION_MAX_TLSv1_0: + ossl_ssl_version_max = TLS1_VERSION; + break; + case CURL_SSLVERSION_MAX_TLSv1_1: + ossl_ssl_version_max = TLS1_1_VERSION; + break; + case CURL_SSLVERSION_MAX_TLSv1_2: + ossl_ssl_version_max = TLS1_2_VERSION; + break; #ifdef TLS1_3_VERSION - case CURL_SSLVERSION_MAX_TLSv1_3: - ossl_ssl_version_max = TLS1_3_VERSION; - break; + case CURL_SSLVERSION_MAX_TLSv1_3: + ossl_ssl_version_max = TLS1_3_VERSION; + break; #endif - case CURL_SSLVERSION_MAX_NONE: /* none selected */ - case CURL_SSLVERSION_MAX_DEFAULT: /* max selected */ - default: - /* SSL_CTX_set_max_proto_version states that: - setting the maximum to 0 will enable - protocol versions up to the highest version - supported by the library */ - ossl_ssl_version_max = 0; - break; + case CURL_SSLVERSION_MAX_NONE: /* none selected */ + case CURL_SSLVERSION_MAX_DEFAULT: /* max selected */ + default: + /* SSL_CTX_set_max_proto_version states that: + setting the maximum to 0 will enable + protocol versions up to the highest version + supported by the library */ + ossl_ssl_version_max = 0; + break; } if(!SSL_CTX_set_max_proto_version(ctx, ossl_ssl_version_max)) { @@ -2807,7 +2679,7 @@ ossl_set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx) } #endif -#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) +#ifdef OPENSSL_IS_BORINGSSL typedef uint32_t ctx_option_t; #elif OPENSSL_VERSION_NUMBER >= 0x30000000L typedef uint64_t ctx_option_t; @@ -2817,76 +2689,75 @@ typedef long ctx_option_t; #if (OPENSSL_VERSION_NUMBER < 0x10100000L) /* 1.1.0 */ static CURLcode -ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, - struct Curl_cfilter *cf, - struct Curl_easy *data) +set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, + struct Curl_easy *data, + struct connectdata *conn, int sockindex) { - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - long ssl_version = conn_config->version; - long ssl_version_max = conn_config->version_max; + long ssl_version = SSL_CONN_CONFIG(version); + long ssl_version_max = SSL_CONN_CONFIG(version_max); (void) data; /* In case it's unused. */ switch(ssl_version) { - case CURL_SSLVERSION_TLSv1_3: + case CURL_SSLVERSION_TLSv1_3: #ifdef TLS1_3_VERSION - { - struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; - DEBUGASSERT(backend); - SSL_CTX_set_max_proto_version(backend->ctx, TLS1_3_VERSION); - *ctx_options |= SSL_OP_NO_TLSv1_2; - } + { + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + SSL_CTX_set_max_proto_version(backend->ctx, TLS1_3_VERSION); + *ctx_options |= SSL_OP_NO_TLSv1_2; + } #else - (void)ctx_options; - failf(data, OSSL_PACKAGE " was built without TLS 1.3 support"); - return CURLE_NOT_BUILT_IN; + (void)sockindex; + (void)ctx_options; + failf(data, OSSL_PACKAGE " was built without TLS 1.3 support"); + return CURLE_NOT_BUILT_IN; #endif - /* FALLTHROUGH */ - case CURL_SSLVERSION_TLSv1_2: + /* FALLTHROUGH */ + case CURL_SSLVERSION_TLSv1_2: #if OPENSSL_VERSION_NUMBER >= 0x1000100FL - *ctx_options |= SSL_OP_NO_TLSv1_1; + *ctx_options |= SSL_OP_NO_TLSv1_1; #else - failf(data, OSSL_PACKAGE " was built without TLS 1.2 support"); - return CURLE_NOT_BUILT_IN; + failf(data, OSSL_PACKAGE " was built without TLS 1.2 support"); + return CURLE_NOT_BUILT_IN; #endif - /* FALLTHROUGH */ - case CURL_SSLVERSION_TLSv1_1: + /* FALLTHROUGH */ + case CURL_SSLVERSION_TLSv1_1: #if OPENSSL_VERSION_NUMBER >= 0x1000100FL - *ctx_options |= SSL_OP_NO_TLSv1; + *ctx_options |= SSL_OP_NO_TLSv1; #else - failf(data, OSSL_PACKAGE " was built without TLS 1.1 support"); - return CURLE_NOT_BUILT_IN; + failf(data, OSSL_PACKAGE " was built without TLS 1.1 support"); + return CURLE_NOT_BUILT_IN; #endif - /* FALLTHROUGH */ - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1: - break; + /* FALLTHROUGH */ + case CURL_SSLVERSION_TLSv1_0: + case CURL_SSLVERSION_TLSv1: + break; } switch(ssl_version_max) { - case CURL_SSLVERSION_MAX_TLSv1_0: + case CURL_SSLVERSION_MAX_TLSv1_0: #if OPENSSL_VERSION_NUMBER >= 0x1000100FL - *ctx_options |= SSL_OP_NO_TLSv1_1; + *ctx_options |= SSL_OP_NO_TLSv1_1; #endif - /* FALLTHROUGH */ - case CURL_SSLVERSION_MAX_TLSv1_1: + /* FALLTHROUGH */ + case CURL_SSLVERSION_MAX_TLSv1_1: #if OPENSSL_VERSION_NUMBER >= 0x1000100FL - *ctx_options |= SSL_OP_NO_TLSv1_2; + *ctx_options |= SSL_OP_NO_TLSv1_2; #endif - /* FALLTHROUGH */ - case CURL_SSLVERSION_MAX_TLSv1_2: + /* FALLTHROUGH */ + case CURL_SSLVERSION_MAX_TLSv1_2: #ifdef TLS1_3_VERSION - *ctx_options |= SSL_OP_NO_TLSv1_3; + *ctx_options |= SSL_OP_NO_TLSv1_3; #endif - break; - case CURL_SSLVERSION_MAX_TLSv1_3: + break; + case CURL_SSLVERSION_MAX_TLSv1_3: #ifdef TLS1_3_VERSION - break; + break; #else - failf(data, OSSL_PACKAGE " was built without TLS 1.3 support"); - return CURLE_NOT_BUILT_IN; + failf(data, OSSL_PACKAGE " was built without TLS 1.3 support"); + return CURLE_NOT_BUILT_IN; #endif } return CURLE_OK; @@ -2899,23 +2770,31 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) { int res = 0; + struct connectdata *conn; struct Curl_easy *data; - struct Curl_cfilter *cf; - const struct ssl_config_data *config; - struct ssl_connect_data *connssl; + int sockindex; + curl_socket_t *sockindex_ptr; + int data_idx = ossl_get_ssl_data_index(); + int connectdata_idx = ossl_get_ssl_conn_index(); + int sockindex_idx = ossl_get_ssl_sockindex_index(); + int proxy_idx = ossl_get_proxy_index(); bool isproxy; - cf = (struct Curl_cfilter*) SSL_get_app_data(ssl); - connssl = cf? cf->ctx : NULL; - data = connssl? CF_DATA_CURRENT(cf) : NULL; + if(data_idx < 0 || connectdata_idx < 0 || sockindex_idx < 0 || proxy_idx < 0) + return 0; + + conn = (struct connectdata*) SSL_get_ex_data(ssl, connectdata_idx); + data = (struct Curl_easy *) SSL_get_ex_data(ssl, data_idx); /* The sockindex has been stored as a pointer to an array element */ - if(!cf || !data) + sockindex_ptr = (curl_socket_t*) SSL_get_ex_data(ssl, sockindex_idx); + if(!conn || !data || !sockindex_ptr) return 0; - isproxy = Curl_ssl_cf_is_proxy(cf); + sockindex = (int)(sockindex_ptr - conn->sock); + + isproxy = SSL_get_ex_data(ssl, proxy_idx) ? TRUE : FALSE; - config = Curl_ssl_cf_get_config(cf, data); - if(config->primary.sessionid) { + if(SSL_SET_OPTION(primary.sessionid)) { bool incache; bool added = FALSE; void *old_ssl_sessionid = NULL; @@ -2924,7 +2803,8 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) if(isproxy) incache = FALSE; else - incache = !(Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL)); + incache = !(Curl_ssl_getsessionid(data, conn, isproxy, + &old_ssl_sessionid, NULL, sockindex)); if(incache) { if(old_ssl_sessionid != ssl_sessionid) { infof(data, "old SSL session ID is stale, removing"); @@ -2934,8 +2814,8 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) } if(!incache) { - if(!Curl_ssl_addsessionid(cf, data, ssl_sessionid, - 0 /* unknown size */, &added)) { + if(!Curl_ssl_addsessionid(data, conn, isproxy, ssl_sessionid, + 0 /* unknown size */, sockindex, &added)) { if(added) { /* the session has been put into the session cache */ res = 1; @@ -2950,7 +2830,7 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) return res; } -static CURLcode load_cacert_from_memory(X509_STORE *store, +static CURLcode load_cacert_from_memory(SSL_CTX *ctx, const struct curl_blob *ca_info_blob) { /* these need to be freed at the end */ @@ -2959,11 +2839,16 @@ static CURLcode load_cacert_from_memory(X509_STORE *store, /* everything else is just a reference */ int i, count = 0; + X509_STORE *cts = NULL; X509_INFO *itmp = NULL; if(ca_info_blob->len > (size_t)INT_MAX) return CURLE_SSL_CACERT_BADFILE; + cts = SSL_CTX_get_cert_store(ctx); + if(!cts) + return CURLE_OUT_OF_MEMORY; + cbio = BIO_new_mem_buf(ca_info_blob->data, (int)ca_info_blob->len); if(!cbio) return CURLE_OUT_OF_MEMORY; @@ -2978,7 +2863,7 @@ static CURLcode load_cacert_from_memory(X509_STORE *store, for(i = 0; i < (int)sk_X509_INFO_num(inf); ++i) { itmp = sk_X509_INFO_value(inf, i); if(itmp->x509) { - if(X509_STORE_add_cert(store, itmp->x509)) { + if(X509_STORE_add_cert(cts, itmp->x509)) { ++count; } else { @@ -2988,7 +2873,7 @@ static CURLcode load_cacert_from_memory(X509_STORE *store, } } if(itmp->crl) { - if(X509_STORE_add_crl(store, itmp->crl)) { + if(X509_STORE_add_crl(cts, itmp->crl)) { ++count; } else { @@ -3003,430 +2888,24 @@ static CURLcode load_cacert_from_memory(X509_STORE *store, BIO_free(cbio); /* if we didn't end up importing anything, treat that as an error */ - return (count > 0) ? CURLE_OK : CURLE_SSL_CACERT_BADFILE; + return (count > 0 ? CURLE_OK : CURLE_SSL_CACERT_BADFILE); } -static CURLcode populate_x509_store(struct Curl_cfilter *cf, - struct Curl_easy *data, - X509_STORE *store) -{ - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - CURLcode result = CURLE_OK; - X509_LOOKUP *lookup = NULL; - const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; - const char * const ssl_cafile = - /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ - (ca_info_blob ? NULL : conn_config->CAfile); - const char * const ssl_capath = conn_config->CApath; - const char * const ssl_crlfile = ssl_config->primary.CRLfile; - const bool verifypeer = conn_config->verifypeer; - bool imported_native_ca = false; - bool imported_ca_info_blob = false; - - if(!store) - return CURLE_OUT_OF_MEMORY; - - if(verifypeer) { -#if defined(USE_WIN32_CRYPTO) - /* Import certificates from the Windows root certificate store if - requested. - https://stackoverflow.com/questions/9507184/ - https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037 - https://datatracker.ietf.org/doc/html/rfc5280 */ - if(ssl_config->native_ca_store) { - HCERTSTORE hStore = CertOpenSystemStore(0, TEXT("ROOT")); - - if(hStore) { - PCCERT_CONTEXT pContext = NULL; - /* The array of enhanced key usage OIDs will vary per certificate and - is declared outside of the loop so that rather than malloc/free each - iteration we can grow it with realloc, when necessary. */ - CERT_ENHKEY_USAGE *enhkey_usage = NULL; - DWORD enhkey_usage_size = 0; - - /* This loop makes a best effort to import all valid certificates from - the MS root store. If a certificate cannot be imported it is - skipped. 'result' is used to store only hard-fail conditions (such - as out of memory) that cause an early break. */ - result = CURLE_OK; - for(;;) { - X509 *x509; - FILETIME now; - BYTE key_usage[2]; - DWORD req_size; - const unsigned char *encoded_cert; -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - char cert_name[256]; -#endif - - pContext = CertEnumCertificatesInStore(hStore, pContext); - if(!pContext) - break; - -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, - NULL, cert_name, sizeof(cert_name))) { - strcpy(cert_name, "Unknown"); - } - infof(data, "SSL: Checking cert \"%s\"", cert_name); -#endif - encoded_cert = (const unsigned char *)pContext->pbCertEncoded; - if(!encoded_cert) - continue; - - GetSystemTimeAsFileTime(&now); - if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 || - CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0) - continue; - - /* If key usage exists check for signing attribute */ - if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType, - pContext->pCertInfo, - key_usage, sizeof(key_usage))) { - if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE)) - continue; - } - else if(GetLastError()) - continue; - - /* If enhanced key usage exists check for server auth attribute. - * - * Note "In a Microsoft environment, a certificate might also have - * EKU extended properties that specify valid uses for the - * certificate." The call below checks both, and behavior varies - * depending on what is found. For more details see - * CertGetEnhancedKeyUsage doc. - */ - if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) { - if(req_size && req_size > enhkey_usage_size) { - void *tmp = realloc(enhkey_usage, req_size); - - if(!tmp) { - failf(data, "SSL: Out of memory allocating for OID list"); - result = CURLE_OUT_OF_MEMORY; - break; - } - - enhkey_usage = (CERT_ENHKEY_USAGE *)tmp; - enhkey_usage_size = req_size; - } - - if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) { - if(!enhkey_usage->cUsageIdentifier) { - /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate - is good for all uses. If it returns zero, the certificate - has no valid uses." */ - if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND) - continue; - } - else { - DWORD i; - bool found = false; - - for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) { - if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */, - enhkey_usage->rgpszUsageIdentifier[i])) { - found = true; - break; - } - } - - if(!found) - continue; - } - } - else - continue; - } - else - continue; - - x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded); - if(!x509) - continue; - - /* Try to import the certificate. This may fail for legitimate - reasons such as duplicate certificate, which is allowed by MS but - not OpenSSL. */ - if(X509_STORE_add_cert(store, x509) == 1) { -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - infof(data, "SSL: Imported cert \"%s\"", cert_name); -#endif - imported_native_ca = true; - } - X509_free(x509); - } - - free(enhkey_usage); - CertFreeCertificateContext(pContext); - CertCloseStore(hStore, 0); - - if(result) - return result; - } - if(imported_native_ca) - infof(data, "successfully imported Windows CA store"); - else - infof(data, "error importing Windows CA store, continuing anyway"); - } -#endif - if(ca_info_blob) { - result = load_cacert_from_memory(store, ca_info_blob); - if(result) { - failf(data, "error importing CA certificate blob"); - return result; - } - else { - imported_ca_info_blob = true; - infof(data, "successfully imported CA certificate blob"); - } - } - - if(ssl_cafile || ssl_capath) { -#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) - /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */ - if(ssl_cafile && !X509_STORE_load_file(store, ssl_cafile)) { - if(!imported_native_ca && !imported_ca_info_blob) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate file: %s", ssl_cafile); - return CURLE_SSL_CACERT_BADFILE; - } - else - infof(data, "error setting certificate file, continuing anyway"); - } - if(ssl_capath && !X509_STORE_load_path(store, ssl_capath)) { - if(!imported_native_ca && !imported_ca_info_blob) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate path: %s", ssl_capath); - return CURLE_SSL_CACERT_BADFILE; - } - else - infof(data, "error setting certificate path, continuing anyway"); - } -#else - /* tell OpenSSL where to find CA certificates that are used to verify the - server's certificate. */ - if(!X509_STORE_load_locations(store, ssl_cafile, ssl_capath)) { - if(!imported_native_ca && !imported_ca_info_blob) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate verify locations:" - " CAfile: %s CApath: %s", - ssl_cafile ? ssl_cafile : "none", - ssl_capath ? ssl_capath : "none"); - return CURLE_SSL_CACERT_BADFILE; - } - else { - infof(data, "error setting certificate verify locations," - " continuing anyway"); - } - } -#endif - infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); - infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); - } - -#ifdef CURL_CA_FALLBACK - if(!ssl_cafile && !ssl_capath && - !imported_native_ca && !imported_ca_info_blob) { - /* verifying the peer without any CA certificates won't - work so use openssl's built-in default as fallback */ - X509_STORE_set_default_paths(store); - } -#endif - } - - if(ssl_crlfile) { - /* tell OpenSSL where to find CRL file that is used to check certificate - * revocation */ - lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); - if(!lookup || - (!X509_load_crl_file(lookup, ssl_crlfile, X509_FILETYPE_PEM)) ) { - failf(data, "error loading CRL file: %s", ssl_crlfile); - return CURLE_SSL_CRL_BADFILE; - } - /* Everything is fine. */ - infof(data, "successfully loaded CRL file:"); - X509_STORE_set_flags(store, - X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); - - infof(data, " CRLfile: %s", ssl_crlfile); - } - - if(verifypeer) { - /* Try building a chain using issuers in the trusted store first to avoid - problems with server-sent legacy intermediates. Newer versions of - OpenSSL do alternate chain checking by default but we do not know how to - determine that in a reliable manner. - https://rt.openssl.org/Ticket/Display.html?id=3621&user=guest&pass=guest - */ -#if defined(X509_V_FLAG_TRUSTED_FIRST) - X509_STORE_set_flags(store, X509_V_FLAG_TRUSTED_FIRST); -#endif -#ifdef X509_V_FLAG_PARTIAL_CHAIN - if(!ssl_config->no_partialchain && !ssl_crlfile) { - /* Have intermediate certificates in the trust store be treated as - trust-anchors, in the same way as self-signed root CA certificates - are. This allows users to verify servers using the intermediate cert - only, instead of needing the whole chain. - - Due to OpenSSL bug https://github.com/openssl/openssl/issues/5081 we - cannot do partial chains with a CRL check. - */ - X509_STORE_set_flags(store, X509_V_FLAG_PARTIAL_CHAIN); - } -#endif - } - - return result; -} - -#if defined(HAVE_SSL_X509_STORE_SHARE) -static bool cached_x509_store_expired(const struct Curl_easy *data, - const struct multi_ssl_backend_data *mb) -{ - const struct ssl_general_config *cfg = &data->set.general_ssl; - struct curltime now = Curl_now(); - timediff_t elapsed_ms = Curl_timediff(now, mb->time); - timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000; - - if(timeout_ms < 0) - return false; - - return elapsed_ms >= timeout_ms; -} - -static bool cached_x509_store_different( - struct Curl_cfilter *cf, - const struct multi_ssl_backend_data *mb) -{ - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - if(!mb->CAfile || !conn_config->CAfile) - return mb->CAfile != conn_config->CAfile; - - return strcmp(mb->CAfile, conn_config->CAfile); -} - -static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf, - const struct Curl_easy *data) -{ - struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi; - X509_STORE *store = NULL; - - if(multi && - multi->ssl_backend_data && - multi->ssl_backend_data->store && - !cached_x509_store_expired(data, multi->ssl_backend_data) && - !cached_x509_store_different(cf, multi->ssl_backend_data)) { - store = multi->ssl_backend_data->store; - } - - return store; -} - -static void set_cached_x509_store(struct Curl_cfilter *cf, - const struct Curl_easy *data, - X509_STORE *store) -{ - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi; - struct multi_ssl_backend_data *mbackend; - - if(!multi) - return; - - if(!multi->ssl_backend_data) { - multi->ssl_backend_data = calloc(1, sizeof(struct multi_ssl_backend_data)); - if(!multi->ssl_backend_data) - return; - } - - mbackend = multi->ssl_backend_data; - - if(X509_STORE_up_ref(store)) { - char *CAfile = NULL; - - if(conn_config->CAfile) { - CAfile = strdup(conn_config->CAfile); - if(!CAfile) { - X509_STORE_free(store); - return; - } - } - - if(mbackend->store) { - X509_STORE_free(mbackend->store); - free(mbackend->CAfile); - } - - mbackend->time = Curl_now(); - mbackend->store = store; - mbackend->CAfile = CAfile; - } -} - -CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, - struct Curl_easy *data, - SSL_CTX *ssl_ctx) -{ - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - CURLcode result = CURLE_OK; - X509_STORE *cached_store; - bool cache_criteria_met; - - /* Consider the X509 store cacheable if it comes exclusively from a CAfile, - or no source is provided and we are falling back to openssl's built-in - default. */ - cache_criteria_met = (data->set.general_ssl.ca_cache_timeout != 0) && - conn_config->verifypeer && - !conn_config->CApath && - !conn_config->ca_info_blob && - !ssl_config->primary.CRLfile && - !ssl_config->native_ca_store; - - cached_store = get_cached_x509_store(cf, data); - if(cached_store && cache_criteria_met && X509_STORE_up_ref(cached_store)) { - SSL_CTX_set_cert_store(ssl_ctx, cached_store); - } - else { - X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx); - - result = populate_x509_store(cf, data, store); - if(result == CURLE_OK && cache_criteria_met) { - set_cached_x509_store(cf, data, store); - } - } - - return result; -} -#else /* HAVE_SSL_X509_STORE_SHARE */ -CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, - struct Curl_easy *data, - SSL_CTX *ssl_ctx) -{ - X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx); - - return populate_x509_store(cf, data, store); -} -#endif /* HAVE_SSL_X509_STORE_SHARE */ - -static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode ossl_connect_step1(struct Curl_easy *data, + struct connectdata *conn, int sockindex) { CURLcode result = CURLE_OK; char *ciphers; SSL_METHOD_QUAL SSL_METHOD *req_method = NULL; - struct ssl_connect_data *connssl = cf->ctx; + X509_LOOKUP *lookup = NULL; + curl_socket_t sockfd = conn->sock[sockindex]; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; ctx_option_t ctx_options = 0; void *ssl_sessionid = NULL; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - BIO *bio; #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME bool sni; - const char *hostname = connssl->hostname; + const char * const hostname = SSL_HOST_NAME(); #ifdef ENABLE_IPV6 struct in6_addr addr; @@ -3434,14 +2913,23 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, struct in_addr addr; #endif #endif - const long int ssl_version = conn_config->version; - char * const ssl_cert = ssl_config->primary.clientcert; - const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; - const char * const ssl_cert_type = ssl_config->cert_type; - const bool verifypeer = conn_config->verifypeer; + const long int ssl_version = SSL_CONN_CONFIG(version); +#ifdef USE_OPENSSL_SRP + const enum CURL_TLSAUTH ssl_authtype = SSL_SET_OPTION(primary.authtype); +#endif + char * const ssl_cert = SSL_SET_OPTION(primary.clientcert); + const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(primary.cert_blob); + const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob); + const char * const ssl_cert_type = SSL_SET_OPTION(cert_type); + const char * const ssl_cafile = + /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ + (ca_info_blob ? NULL : SSL_CONN_CONFIG(CAfile)); + const char * const ssl_capath = SSL_CONN_CONFIG(CApath); + const bool verifypeer = SSL_CONN_CONFIG(verifypeer); + const char * const ssl_crlfile = SSL_SET_OPTION(primary.CRLfile); char error_buffer[256]; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ssl_backend_data *backend = connssl->backend; + bool imported_native_ca = false; DEBUGASSERT(ssl_connect_1 == connssl->connecting_state); DEBUGASSERT(backend); @@ -3451,7 +2939,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, if(result) return result; - ssl_config->certverifyresult = !X509_V_OK; + SSL_SET_OPTION_LVALUE(certverifyresult) = !X509_V_OK; /* check to see if we've been told to use an explicit SSL/TLS version */ @@ -3481,12 +2969,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } - if(backend->ctx) { - /* This happens when an error was encountered before in this - * step and we are called to do it again. Get rid of any leftover - * from the previous call. */ - ossl_close(cf, data); - } + DEBUGASSERT(!backend->ctx); backend->ctx = SSL_CTX_new(req_method); if(!backend->ctx) { @@ -3503,7 +2986,8 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, if(data->set.fdebug && data->set.verbose) { /* the SSL trace callback is only used for verbose logging */ SSL_CTX_set_msg_callback(backend->ctx, ossl_trace); - SSL_CTX_set_msg_callback_arg(backend->ctx, cf); + SSL_CTX_set_msg_callback_arg(backend->ctx, conn); + set_logger(conn, data); } #endif @@ -3561,55 +3045,75 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS /* unless the user explicitly asks to allow the protocol vulnerability we use the work-around */ - if(!ssl_config->enable_beast) + if(!SSL_SET_OPTION(enable_beast)) ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; #endif switch(ssl_version) { - case CURL_SSLVERSION_SSLv2: - case CURL_SSLVERSION_SSLv3: - return CURLE_NOT_BUILT_IN; + case CURL_SSLVERSION_SSLv2: + case CURL_SSLVERSION_SSLv3: + return CURLE_NOT_BUILT_IN; /* "--tlsv<x.y>" options mean TLS >= version <x.y> */ - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: /* TLS >= version 1.0 */ - case CURL_SSLVERSION_TLSv1_0: /* TLS >= version 1.0 */ - case CURL_SSLVERSION_TLSv1_1: /* TLS >= version 1.1 */ - case CURL_SSLVERSION_TLSv1_2: /* TLS >= version 1.2 */ - case CURL_SSLVERSION_TLSv1_3: /* TLS >= version 1.3 */ - /* asking for any TLS version as the minimum, means no SSL versions - allowed */ - ctx_options |= SSL_OP_NO_SSLv2; - ctx_options |= SSL_OP_NO_SSLv3; + case CURL_SSLVERSION_DEFAULT: + case CURL_SSLVERSION_TLSv1: /* TLS >= version 1.0 */ + case CURL_SSLVERSION_TLSv1_0: /* TLS >= version 1.0 */ + case CURL_SSLVERSION_TLSv1_1: /* TLS >= version 1.1 */ + case CURL_SSLVERSION_TLSv1_2: /* TLS >= version 1.2 */ + case CURL_SSLVERSION_TLSv1_3: /* TLS >= version 1.3 */ + /* asking for any TLS version as the minimum, means no SSL versions + allowed */ + ctx_options |= SSL_OP_NO_SSLv2; + ctx_options |= SSL_OP_NO_SSLv3; #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */ - result = ossl_set_ssl_version_min_max(cf, backend->ctx); + result = set_ssl_version_min_max(backend->ctx, conn); #else - result = ossl_set_ssl_version_min_max_legacy(&ctx_options, cf, data); + result = set_ssl_version_min_max_legacy(&ctx_options, data, conn, + sockindex); #endif - if(result != CURLE_OK) - return result; - break; + if(result != CURLE_OK) + return result; + break; - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; + default: + failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); + return CURLE_SSL_CONNECT_ERROR; } SSL_CTX_set_options(backend->ctx, ctx_options); #ifdef HAS_ALPN - if(connssl->alpn) { - struct alpn_proto_buf proto; + if(conn->bits.tls_enable_alpn) { + int cur = 0; + unsigned char protocols[128]; + +#ifdef USE_HTTP2 + if(data->state.httpwant >= CURL_HTTP_VERSION_2 +#ifndef CURL_DISABLE_PROXY + && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy) +#endif + ) { + protocols[cur++] = ALPN_H2_LENGTH; + + memcpy(&protocols[cur], ALPN_H2, ALPN_H2_LENGTH); + cur += ALPN_H2_LENGTH; + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); + } +#endif + + protocols[cur++] = ALPN_HTTP_1_1_LENGTH; + memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH); + cur += ALPN_HTTP_1_1_LENGTH; + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); - result = Curl_alpn_to_proto_buf(&proto, connssl->alpn); - if(result || - SSL_CTX_set_alpn_protos(backend->ctx, proto.data, proto.len)) { + /* expects length prefixed preference ordered list of protocols in wire + * format + */ + if(SSL_CTX_set_alpn_protos(backend->ctx, protocols, cur)) { failf(data, "Error setting ALPN"); return CURLE_SSL_CONNECT_ERROR; } - Curl_alpn_to_proto_str(&proto, connssl->alpn); - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); } #endif @@ -3617,15 +3121,15 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, if(!result && !cert_stuff(data, backend->ctx, ssl_cert, ssl_cert_blob, ssl_cert_type, - ssl_config->key, ssl_config->key_blob, - ssl_config->key_type, ssl_config->key_passwd)) + SSL_SET_OPTION(key), SSL_SET_OPTION(key_blob), + SSL_SET_OPTION(key_type), SSL_SET_OPTION(key_passwd))) result = CURLE_SSL_CERTPROBLEM; if(result) /* failf() is already done in cert_stuff() */ return result; } - ciphers = conn_config->cipher_list; + ciphers = SSL_CONN_CONFIG(cipher_list); if(!ciphers) ciphers = (char *)DEFAULT_CIPHER_SELECTION; if(ciphers) { @@ -3638,7 +3142,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, #ifdef HAVE_SSL_CTX_SET_CIPHERSUITES { - char *ciphers13 = conn_config->cipher_list13; + 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", ciphers13); @@ -3656,7 +3160,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, #ifdef HAVE_SSL_CTX_SET_EC_CURVES { - char *curves = conn_config->curves; + char *curves = SSL_CONN_CONFIG(curves); if(curves) { if(!SSL_CTX_set1_curves_list(backend->ctx, curves)) { failf(data, "failed setting curves list: '%s'", curves); @@ -3667,9 +3171,10 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, #endif #ifdef USE_OPENSSL_SRP - if(ssl_config->primary.username && Curl_auth_allowed_to_host(data)) { - char * const ssl_username = ssl_config->primary.username; - char * const ssl_password = ssl_config->primary.password; + if((ssl_authtype == CURL_TLSAUTH_SRP) && + Curl_auth_allowed_to_host(data)) { + char * const ssl_username = SSL_SET_OPTION(primary.username); + char * const ssl_password = SSL_SET_OPTION(primary.password); infof(data, "Using TLS-SRP username: %s", ssl_username); if(!SSL_CTX_set_srp_username(backend->ctx, ssl_username)) { @@ -3680,7 +3185,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, failf(data, "failed setting SRP password"); return CURLE_BAD_FUNCTION_ARGUMENT; } - if(!conn_config->cipher_list) { + if(!SSL_CONN_CONFIG(cipher_list)) { infof(data, "Setting cipher list SRP"); if(!SSL_CTX_set_cipher_list(backend->ctx, "SRP")) { @@ -3691,6 +3196,250 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, } #endif + +#if defined(USE_WIN32_CRYPTO) + /* Import certificates from the Windows root certificate store if requested. + https://stackoverflow.com/questions/9507184/ + https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037 + https://datatracker.ietf.org/doc/html/rfc5280 */ + if((SSL_CONN_CONFIG(verifypeer) || SSL_CONN_CONFIG(verifyhost)) && + (SSL_SET_OPTION(native_ca_store))) { + X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx); + HCERTSTORE hStore = CertOpenSystemStore(0, TEXT("ROOT")); + + if(hStore) { + PCCERT_CONTEXT pContext = NULL; + /* The array of enhanced key usage OIDs will vary per certificate and is + declared outside of the loop so that rather than malloc/free each + iteration we can grow it with realloc, when necessary. */ + CERT_ENHKEY_USAGE *enhkey_usage = NULL; + DWORD enhkey_usage_size = 0; + + /* This loop makes a best effort to import all valid certificates from + the MS root store. If a certificate cannot be imported it is skipped. + 'result' is used to store only hard-fail conditions (such as out of + memory) that cause an early break. */ + result = CURLE_OK; + for(;;) { + X509 *x509; + FILETIME now; + BYTE key_usage[2]; + DWORD req_size; + const unsigned char *encoded_cert; +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + char cert_name[256]; +#endif + + pContext = CertEnumCertificatesInStore(hStore, pContext); + if(!pContext) + break; + +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, + NULL, cert_name, sizeof(cert_name))) { + strcpy(cert_name, "Unknown"); + } + infof(data, "SSL: Checking cert \"%s\"", cert_name); +#endif + + encoded_cert = (const unsigned char *)pContext->pbCertEncoded; + if(!encoded_cert) + continue; + + GetSystemTimeAsFileTime(&now); + if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 || + CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0) + continue; + + /* If key usage exists check for signing attribute */ + if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType, + pContext->pCertInfo, + key_usage, sizeof(key_usage))) { + if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE)) + continue; + } + else if(GetLastError()) + continue; + + /* If enhanced key usage exists check for server auth attribute. + * + * Note "In a Microsoft environment, a certificate might also have EKU + * extended properties that specify valid uses for the certificate." + * The call below checks both, and behavior varies depending on what is + * found. For more details see CertGetEnhancedKeyUsage doc. + */ + if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) { + if(req_size && req_size > enhkey_usage_size) { + void *tmp = realloc(enhkey_usage, req_size); + + if(!tmp) { + failf(data, "SSL: Out of memory allocating for OID list"); + result = CURLE_OUT_OF_MEMORY; + break; + } + + enhkey_usage = (CERT_ENHKEY_USAGE *)tmp; + enhkey_usage_size = req_size; + } + + if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) { + if(!enhkey_usage->cUsageIdentifier) { + /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate is + good for all uses. If it returns zero, the certificate has no + valid uses." */ + if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND) + continue; + } + else { + DWORD i; + bool found = false; + + for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) { + if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */, + enhkey_usage->rgpszUsageIdentifier[i])) { + found = true; + break; + } + } + + if(!found) + continue; + } + } + else + continue; + } + else + continue; + + x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded); + if(!x509) + continue; + + /* Try to import the certificate. This may fail for legitimate reasons + such as duplicate certificate, which is allowed by MS but not + OpenSSL. */ + if(X509_STORE_add_cert(store, x509) == 1) { +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + infof(data, "SSL: Imported cert \"%s\"", cert_name); +#endif + imported_native_ca = true; + } + X509_free(x509); + } + + free(enhkey_usage); + CertFreeCertificateContext(pContext); + CertCloseStore(hStore, 0); + + if(result) + return result; + } + if(imported_native_ca) + infof(data, "successfully imported Windows CA store"); + else + infof(data, "error importing Windows CA store, continuing anyway"); + } +#endif + + if(ca_info_blob) { + result = load_cacert_from_memory(backend->ctx, ca_info_blob); + if(result) { + if(result == CURLE_OUT_OF_MEMORY || + (verifypeer && !imported_native_ca)) { + failf(data, "error importing CA certificate blob"); + return result; + } + /* Only warn if no certificate verification is required. */ + infof(data, "error importing CA certificate blob, continuing anyway"); + } + } + + if(verifypeer && !imported_native_ca && (ssl_cafile || ssl_capath)) { +#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) + /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */ + if(ssl_cafile && + !SSL_CTX_load_verify_file(backend->ctx, ssl_cafile)) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate file: %s", ssl_cafile); + return CURLE_SSL_CACERT_BADFILE; + } + if(ssl_capath && + !SSL_CTX_load_verify_dir(backend->ctx, ssl_capath)) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate path: %s", ssl_capath); + return CURLE_SSL_CACERT_BADFILE; + } +#else + /* tell OpenSSL where to find CA certificates that are used to verify the + server's certificate. */ + if(!SSL_CTX_load_verify_locations(backend->ctx, ssl_cafile, ssl_capath)) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate verify locations:" + " CAfile: %s CApath: %s", + ssl_cafile ? ssl_cafile : "none", + ssl_capath ? ssl_capath : "none"); + return CURLE_SSL_CACERT_BADFILE; + } +#endif + infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); + infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); + } + +#ifdef CURL_CA_FALLBACK + if(verifypeer && + !ca_info_blob && !ssl_cafile && !ssl_capath && !imported_native_ca) { + /* verifying the peer without any CA certificates won't + work so use openssl's built-in default as fallback */ + SSL_CTX_set_default_verify_paths(backend->ctx); + } +#endif + + if(ssl_crlfile) { + /* tell OpenSSL where to find CRL file that is used to check certificate + * revocation */ + lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(backend->ctx), + X509_LOOKUP_file()); + if(!lookup || + (!X509_load_crl_file(lookup, ssl_crlfile, X509_FILETYPE_PEM)) ) { + failf(data, "error loading CRL file: %s", ssl_crlfile); + return CURLE_SSL_CRL_BADFILE; + } + /* Everything is fine. */ + infof(data, "successfully loaded CRL file:"); + X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx), + X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); + + infof(data, " CRLfile: %s", ssl_crlfile); + } + + if(verifypeer) { + /* Try building a chain using issuers in the trusted store first to avoid + problems with server-sent legacy intermediates. Newer versions of + OpenSSL do alternate chain checking by default but we do not know how to + determine that in a reliable manner. + https://rt.openssl.org/Ticket/Display.html?id=3621&user=guest&pass=guest + */ +#if defined(X509_V_FLAG_TRUSTED_FIRST) + X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx), + X509_V_FLAG_TRUSTED_FIRST); +#endif +#ifdef X509_V_FLAG_PARTIAL_CHAIN + if(!SSL_SET_OPTION(no_partialchain) && !ssl_crlfile) { + /* Have intermediate certificates in the trust store be treated as + trust-anchors, in the same way as self-signed root CA certificates + are. This allows users to verify servers using the intermediate cert + only, instead of needing the whole chain. + + Due to OpenSSL bug https://github.com/openssl/openssl/issues/5081 we + cannot do partial chains with a CRL check. + */ + X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx), + X509_V_FLAG_PARTIAL_CHAIN); + } +#endif + } + /* OpenSSL always tries to verify the peer, this only says whether it should * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with @@ -3710,8 +3459,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, * an internal session cache. */ SSL_CTX_set_session_cache_mode(backend->ctx, - SSL_SESS_CACHE_CLIENT | - SSL_SESS_CACHE_NO_INTERNAL); + SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL); SSL_CTX_sess_set_new_cb(backend->ctx, ossl_new_session_cb); /* give application a chance to interfere with SSL set up. */ @@ -3735,16 +3483,13 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, return CURLE_OUT_OF_MEMORY; } - SSL_set_app_data(backend->handle, cf); - #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ - !defined(OPENSSL_NO_OCSP) - if(conn_config->verifystatus) + !defined(OPENSSL_NO_OCSP) + if(SSL_CONN_CONFIG(verifystatus)) SSL_set_tlsext_status_type(backend->handle, TLSEXT_STATUSTYPE_ocsp); #endif -#if (defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)) && \ - defined(ALLOW_RENEG) +#if defined(OPENSSL_IS_BORINGSSL) && defined(ALLOW_RENEG) SSL_set_renegotiate_mode(backend->handle, ssl_renegotiate_freely); #endif @@ -3765,11 +3510,18 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, } #endif - SSL_set_app_data(backend->handle, cf); + if(!ossl_associate_connection(data, conn, sockindex)) { + /* Maybe the internal errors of SSL_get_ex_new_index or SSL_set_ex_data */ + failf(data, "SSL: ossl_associate_connection failed: %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + return CURLE_SSL_CONNECT_ERROR; + } - if(ssl_config->primary.sessionid) { + if(SSL_SET_OPTION(primary.sessionid)) { Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL)) { + if(!Curl_ssl_getsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE, + &ssl_sessionid, NULL, sockindex)) { /* we got a session id, use it! */ if(!SSL_set_session(backend->handle, ssl_sessionid)) { Curl_ssl_sessionid_unlock(data); @@ -3784,39 +3536,40 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, Curl_ssl_sessionid_unlock(data); } - backend->bio_method = bio_cf_method_create(); - if(!backend->bio_method) - return CURLE_OUT_OF_MEMORY; - bio = BIO_new(backend->bio_method); - if(!bio) - return CURLE_OUT_OF_MEMORY; - - BIO_set_data(bio, cf); -#ifdef HAVE_SSL_SET0_WBIO - /* with OpenSSL v1.1.1 we get an alternative to SSL_set_bio() that works - * without backward compat quirks. Every call takes one reference, so we - * up it and pass. SSL* then owns it and will free. - * We check on the function in configure, since libressl and friends - * each have their own versions to add support for this. */ - BIO_up_ref(bio); - SSL_set0_rbio(backend->handle, bio); - SSL_set0_wbio(backend->handle, bio); -#else - SSL_set_bio(backend->handle, bio, bio); +#ifndef CURL_DISABLE_PROXY + if(conn->proxy_ssl[sockindex].use) { + BIO *const bio = BIO_new(BIO_f_ssl()); + struct ssl_backend_data *proxy_backend; + SSL* handle = NULL; + proxy_backend = conn->proxy_ssl[sockindex].backend; + DEBUGASSERT(proxy_backend); + handle = proxy_backend->handle; + DEBUGASSERT(ssl_connection_complete == conn->proxy_ssl[sockindex].state); + DEBUGASSERT(handle != NULL); + DEBUGASSERT(bio != NULL); + BIO_set_ssl(bio, handle, FALSE); + SSL_set_bio(backend->handle, bio, bio); + } + else #endif + if(!SSL_set_fd(backend->handle, (int)sockfd)) { + /* pass the raw socket into the SSL layers */ + failf(data, "SSL: SSL_set_fd failed: %s", + ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer))); + return CURLE_SSL_CONNECT_ERROR; + } + connssl->connecting_state = ssl_connect_2; return CURLE_OK; } -static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode ossl_connect_step2(struct Curl_easy *data, + struct connectdata *conn, int sockindex) { int err; - struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; DEBUGASSERT(ssl_connect_2 == connssl->connecting_state || ssl_connect_2_reading == connssl->connecting_state || ssl_connect_2_writing == connssl->connecting_state); @@ -3825,16 +3578,6 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, ERR_clear_error(); err = SSL_connect(backend->handle); - - if(!backend->x509_store_setup) { - /* After having send off the ClientHello, we prepare the x509 - * store to verify the coming certificate from the server */ - CURLcode result = Curl_ssl_setup_x509_store(cf, data, backend->ctx); - if(result) - return result; - backend->x509_store_setup = TRUE; - } - #ifndef HAVE_KEYLOG_CALLBACK if(Curl_tls_keylog_enabled()) { /* If key logging is enabled, wait for the handshake to complete and then @@ -3864,12 +3607,9 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, return CURLE_OK; } #endif - else if(backend->io_result == CURLE_AGAIN) { - return CURLE_OK; - } else { /* untreated error */ - sslerr_t errdetail; + unsigned long errdetail; char error_buffer[256]=""; CURLcode result; long lerr; @@ -3894,7 +3634,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, lerr = SSL_get_verify_result(backend->handle); if(lerr != X509_V_OK) { - ssl_config->certverifyresult = lerr; + SSL_SET_OPTION_LVALUE(certverifyresult) = lerr; msnprintf(error_buffer, sizeof(error_buffer), "SSL certificate problem: %s", X509_verify_cert_error_string(lerr)); @@ -3904,19 +3644,17 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, error_buffer */ strcpy(error_buffer, "SSL certificate verification failed"); } -#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \ - !defined(LIBRESSL_VERSION_NUMBER) && \ - !defined(OPENSSL_IS_BORINGSSL) && \ - !defined(OPENSSL_IS_AWSLC)) - +#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \ + !defined(LIBRESSL_VERSION_NUMBER) && \ + !defined(OPENSSL_IS_BORINGSSL)) /* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on - OpenSSL version above v1.1.1, not LibreSSL, BoringSSL, or AWS-LC */ + OpenSSL version above v1.1.1, not LibreSSL nor BoringSSL */ else if((lib == ERR_LIB_SSL) && (reason == SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)) { - /* If client certificate is required, communicate the - error to client */ - result = CURLE_SSL_CLIENTCERT; - ossl_strerror(errdetail, error_buffer, sizeof(error_buffer)); + /* If client certificate is required, communicate the + error to client */ + result = CURLE_SSL_CLIENTCERT; + ossl_strerror(errdetail, error_buffer, sizeof(error_buffer)); } #endif else { @@ -3931,14 +3669,15 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, * the SO_ERROR is also lost. */ if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) { + const char * const hostname = SSL_HOST_NAME(); + const long int port = SSL_HOST_PORT(); char extramsg[80]=""; int sockerr = SOCKERRNO; - if(sockerr && detail == SSL_ERROR_SYSCALL) Curl_strerror(sockerr, extramsg, sizeof(extramsg)); - failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%d ", + failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%ld ", extramsg[0] ? extramsg : SSL_ERROR_to_str(detail), - connssl->hostname, connssl->port); + hostname, port); return result; } @@ -3961,12 +3700,30 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, /* Sets data and len to negotiated protocol, len is 0 if no protocol was * negotiated */ - if(connssl->alpn) { + if(conn->bits.tls_enable_alpn) { const unsigned char *neg_protocol; unsigned int len; SSL_get0_alpn_selected(backend->handle, &neg_protocol, &len); + if(len) { + infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, len, neg_protocol); - return Curl_alpn_set_negotiated(cf, data, neg_protocol, len); +#ifdef USE_HTTP2 + if(len == ALPN_H2_LENGTH && + !memcmp(ALPN_H2, neg_protocol, len)) { + conn->alpn = CURL_HTTP_VERSION_2; + } + else +#endif + if(len == ALPN_HTTP_1_1_LENGTH && + !memcmp(ALPN_HTTP_1_1, neg_protocol, ALPN_HTTP_1_1_LENGTH)) { + conn->alpn = CURL_HTTP_VERSION_1_1; + } + } + else + infof(data, VTLS_INFOF_NO_ALPN); + + Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ? + BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); } #endif @@ -3978,8 +3735,8 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, * Heavily modified from: * https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#OpenSSL */ -static CURLcode ossl_pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert, - const char *pinnedpubkey) +static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert, + const char *pinnedpubkey) { /* Scratch */ int len1 = 0, len2 = 0; @@ -4000,7 +3757,7 @@ static CURLcode ossl_pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert, /* Thanks to Viktor Dukhovni on the OpenSSL mailing list */ /* https://groups.google.com/group/mailing.openssl.users/browse_thread - /thread/d61858dae102c6c7 */ + /thread/d61858dae102c6c7 */ len1 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), NULL); if(len1 < 1) break; /* failed */ @@ -4040,14 +3797,11 @@ static CURLcode ossl_pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert, * We check certificates to authenticate the server; otherwise we risk * man-in-the-middle attack. */ -static CURLcode servercert(struct Curl_cfilter *cf, - struct Curl_easy *data, +static CURLcode servercert(struct Curl_easy *data, + struct connectdata *conn, + struct ssl_connect_data *connssl, bool strict) { - struct connectdata *conn = cf->conn; - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); CURLcode result = CURLE_OK; int rc; long lerr; @@ -4057,8 +3811,7 @@ static CURLcode servercert(struct Curl_cfilter *cf, char buffer[2048]; const char *ptr; BIO *mem = BIO_new(BIO_s_mem()); - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ssl_backend_data *backend = connssl->backend; DEBUGASSERT(backend); @@ -4073,7 +3826,7 @@ static CURLcode servercert(struct Curl_cfilter *cf, if(data->set.ssl.certinfo) /* asked to gather certificate info */ - (void)Curl_ossl_certchain(data, backend->handle); + (void)Curl_ossl_certchain(data, connssl->backend->handle); backend->server_cert = SSL_get1_peer_certificate(backend->handle); if(!backend->server_cert) { @@ -4085,8 +3838,7 @@ static CURLcode servercert(struct Curl_cfilter *cf, return CURLE_PEER_FAILED_VERIFICATION; } - infof(data, "%s certificate:", - Curl_ssl_cf_is_proxy(cf)? "Proxy" : "Server"); + infof(data, "%s certificate:", SSL_IS_PROXY() ? "Proxy" : "Server"); rc = x509_name_oneline(X509_get_subject_name(backend->server_cert), buffer, sizeof(buffer)); @@ -4109,9 +3861,8 @@ static CURLcode servercert(struct Curl_cfilter *cf, BIO_free(mem); - if(conn_config->verifyhost) { - result = ossl_verifyhost(data, conn, backend->server_cert, - connssl->hostname, connssl->dispname); + if(SSL_CONN_CONFIG(verifyhost)) { + result = Curl_ossl_verifyhost(data, conn, backend->server_cert); if(result) { X509_free(backend->server_cert); backend->server_cert = NULL; @@ -4133,10 +3884,10 @@ static CURLcode servercert(struct Curl_cfilter *cf, deallocating the certificate. */ /* e.g. match issuer name with provided issuer certificate */ - if(conn_config->issuercert || conn_config->issuercert_blob) { - if(conn_config->issuercert_blob) { - fp = BIO_new_mem_buf(conn_config->issuercert_blob->data, - (int)conn_config->issuercert_blob->len); + if(SSL_CONN_CONFIG(issuercert) || SSL_CONN_CONFIG(issuercert_blob)) { + if(SSL_CONN_CONFIG(issuercert_blob)) { + fp = BIO_new_mem_buf(SSL_CONN_CONFIG(issuercert_blob)->data, + (int)SSL_CONN_CONFIG(issuercert_blob)->len); if(!fp) { failf(data, "BIO_new_mem_buf NULL, " OSSL_PACKAGE @@ -4161,10 +3912,10 @@ static CURLcode servercert(struct Curl_cfilter *cf, return CURLE_OUT_OF_MEMORY; } - if(BIO_read_filename(fp, conn_config->issuercert) <= 0) { + if(BIO_read_filename(fp, SSL_CONN_CONFIG(issuercert)) <= 0) { if(strict) failf(data, "SSL: Unable to open issuer cert (%s)", - conn_config->issuercert); + SSL_CONN_CONFIG(issuercert)); BIO_free(fp); X509_free(backend->server_cert); backend->server_cert = NULL; @@ -4176,7 +3927,7 @@ static CURLcode servercert(struct Curl_cfilter *cf, if(!issuer) { if(strict) failf(data, "SSL: Unable to read issuer cert (%s)", - conn_config->issuercert); + SSL_CONN_CONFIG(issuercert)); BIO_free(fp); X509_free(issuer); X509_free(backend->server_cert); @@ -4187,7 +3938,7 @@ static CURLcode servercert(struct Curl_cfilter *cf, if(X509_check_issued(issuer, backend->server_cert) != X509_V_OK) { if(strict) failf(data, "SSL: Certificate issuer check failed (%s)", - conn_config->issuercert); + SSL_CONN_CONFIG(issuercert)); BIO_free(fp); X509_free(issuer); X509_free(backend->server_cert); @@ -4196,15 +3947,15 @@ static CURLcode servercert(struct Curl_cfilter *cf, } infof(data, " SSL certificate issuer check ok (%s)", - conn_config->issuercert); + SSL_CONN_CONFIG(issuercert)); BIO_free(fp); X509_free(issuer); } lerr = SSL_get_verify_result(backend->handle); - ssl_config->certverifyresult = lerr; + SSL_SET_OPTION_LVALUE(certverifyresult) = lerr; if(lerr != X509_V_OK) { - if(conn_config->verifypeer) { + if(SSL_CONN_CONFIG(verifypeer)) { /* We probably never reach this, because SSL_connect() will fail and we return earlier if verifypeer is set? */ if(strict) @@ -4222,9 +3973,9 @@ static CURLcode servercert(struct Curl_cfilter *cf, } #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ - !defined(OPENSSL_NO_OCSP) - if(conn_config->verifystatus) { - result = verifystatus(cf, data); + !defined(OPENSSL_NO_OCSP) + if(SSL_CONN_CONFIG(verifystatus)) { + result = verifystatus(data, connssl); if(result) { X509_free(backend->server_cert); backend->server_cert = NULL; @@ -4237,11 +3988,9 @@ static CURLcode servercert(struct Curl_cfilter *cf, /* when not strict, we don't bother about the verify cert problems */ result = CURLE_OK; - ptr = Curl_ssl_cf_is_proxy(cf)? - data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: - data->set.str[STRING_SSL_PINNEDPUBLICKEY]; + ptr = SSL_PINNED_PUB_KEY(); if(!result && ptr) { - result = ossl_pkp_pin_peer_pubkey(data, backend->server_cert, ptr); + result = pkp_pin_peer_pubkey(data, backend->server_cert, ptr); if(result) failf(data, "SSL: public key does not match pinned public key"); } @@ -4253,12 +4002,11 @@ static CURLcode servercert(struct Curl_cfilter *cf, return result; } -static CURLcode ossl_connect_step3(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode ossl_connect_step3(struct Curl_easy *data, + struct connectdata *conn, int sockindex) { CURLcode result = CURLE_OK; - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); @@ -4269,8 +4017,8 @@ static CURLcode ossl_connect_step3(struct Curl_cfilter *cf, * operations. */ - result = servercert(cf, data, conn_config->verifypeer || - conn_config->verifyhost); + result = servercert(data, conn, connssl, (SSL_CONN_CONFIG(verifypeer) || + SSL_CONN_CONFIG(verifyhost))); if(!result) connssl->connecting_state = ssl_connect_done; @@ -4278,14 +4026,18 @@ static CURLcode ossl_connect_step3(struct Curl_cfilter *cf, return result; } -static CURLcode ossl_connect_common(struct Curl_cfilter *cf, - struct Curl_easy *data, +static Curl_recv ossl_recv; +static Curl_send ossl_send; + +static CURLcode ossl_connect_common(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, bool nonblocking, bool *done) { - CURLcode result = CURLE_OK; - struct ssl_connect_data *connssl = cf->ctx; - curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); + CURLcode result; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + curl_socket_t sockfd = conn->sock[sockindex]; int what; /* check if the connection has already been established */ @@ -4304,9 +4056,9 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, return CURLE_OPERATION_TIMEDOUT; } - result = ossl_connect_step1(cf, data); + result = ossl_connect_step1(data, conn, sockindex); if(result) - goto out; + return result; } while(ssl_connect_2 == connssl->connecting_state || @@ -4319,14 +4071,12 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, if(timeout_ms < 0) { /* no need to continue if time already is up */ failf(data, "SSL connection timeout"); - result = CURLE_OPERATION_TIMEDOUT; - goto out; + return CURLE_OPERATION_TIMEDOUT; } /* if ssl is expecting something, check if it's available. */ - if(!nonblocking && - (connssl->connecting_state == ssl_connect_2_reading || - connssl->connecting_state == ssl_connect_2_writing)) { + 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; @@ -4334,18 +4084,20 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, connssl->connecting_state?sockfd:CURL_SOCKET_BAD; what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, - timeout_ms); + nonblocking?0:timeout_ms); if(what < 0) { /* fatal error */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - result = CURLE_SSL_CONNECT_ERROR; - goto out; + return CURLE_SSL_CONNECT_ERROR; } if(0 == what) { + if(nonblocking) { + *done = FALSE; + return CURLE_OK; + } /* timeout */ failf(data, "SSL connection timeout"); - result = CURLE_OPERATION_TIMEDOUT; - goto out; + return CURLE_OPERATION_TIMEDOUT; } /* socket is readable or writable */ } @@ -4356,23 +4108,25 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, * before step2 has completed while ensuring that a client using select() * or epoll() will always have a valid fdset to wait on. */ - result = ossl_connect_step2(cf, data); + result = ossl_connect_step2(data, 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))) - goto out; + return result; } /* repeat step2 until all transactions are done. */ if(ssl_connect_3 == connssl->connecting_state) { - result = ossl_connect_step3(cf, data); + result = ossl_connect_step3(data, conn, sockindex); if(result) - goto out; + return result; } if(ssl_connect_done == connssl->connecting_state) { connssl->state = ssl_connection_complete; + conn->recv[sockindex] = ossl_recv; + conn->send[sockindex] = ossl_send; *done = TRUE; } else @@ -4381,24 +4135,24 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, /* Reset our connect state machine */ connssl->connecting_state = ssl_connect_1; -out: - return result; + return CURLE_OK; } -static CURLcode ossl_connect_nonblocking(struct Curl_cfilter *cf, - struct Curl_easy *data, +static CURLcode ossl_connect_nonblocking(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, bool *done) { - return ossl_connect_common(cf, data, TRUE, done); + return ossl_connect_common(data, conn, sockindex, TRUE, done); } -static CURLcode ossl_connect(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode ossl_connect(struct Curl_easy *data, struct connectdata *conn, + int sockindex) { CURLcode result; bool done = FALSE; - result = ossl_connect_common(cf, data, FALSE, &done); + result = ossl_connect_common(data, conn, sockindex, FALSE, &done); if(result) return result; @@ -4407,22 +4161,28 @@ static CURLcode ossl_connect(struct Curl_cfilter *cf, return CURLE_OK; } -static bool ossl_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data) +static bool ossl_data_pending(const struct connectdata *conn, + int connindex) { - struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; - - (void)data; - DEBUGASSERT(connssl && backend); - if(backend->handle && SSL_pending(backend->handle)) + const struct ssl_connect_data *connssl = &conn->ssl[connindex]; + DEBUGASSERT(connssl->backend); + if(connssl->backend->handle && SSL_pending(connssl->backend->handle)) return TRUE; +#ifndef CURL_DISABLE_PROXY + { + const struct ssl_connect_data *proxyssl = &conn->proxy_ssl[connindex]; + DEBUGASSERT(proxyssl->backend); + if(proxyssl->backend->handle && SSL_pending(proxyssl->backend->handle)) + return TRUE; + } +#endif return FALSE; } -static ssize_t ossl_send(struct Curl_cfilter *cf, - struct Curl_easy *data, +static size_t ossl_version(char *buffer, size_t size); + +static ssize_t ossl_send(struct Curl_easy *data, + int sockindex, const void *mem, size_t len, CURLcode *curlcode) @@ -4431,19 +4191,19 @@ static ssize_t ossl_send(struct Curl_cfilter *cf, 'size_t' */ int err; char error_buffer[256]; - sslerr_t sslerror; + unsigned long sslerror; int memlen; int rc; - struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct connectdata *conn = data->conn; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; - (void)data; DEBUGASSERT(backend); ERR_clear_error(); memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; + set_logger(conn, data); rc = SSL_write(backend->handle, mem, memlen); if(rc <= 0) { @@ -4456,43 +4216,34 @@ static ssize_t ossl_send(struct Curl_cfilter *cf, should be called again later. This is basically an EWOULDBLOCK equivalent. */ *curlcode = CURLE_AGAIN; - rc = -1; - goto out; + return -1; case SSL_ERROR_SYSCALL: - { - int sockerr = SOCKERRNO; - - if(backend->io_result == CURLE_AGAIN) { - *curlcode = CURLE_AGAIN; - rc = -1; - goto out; - } - sslerror = ERR_get_error(); - if(sslerror) - ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)); - else if(sockerr) - Curl_strerror(sockerr, error_buffer, sizeof(error_buffer)); - else { - strncpy(error_buffer, SSL_ERROR_to_str(err), sizeof(error_buffer)); - error_buffer[sizeof(error_buffer) - 1] = '\0'; + { + int sockerr = SOCKERRNO; + sslerror = ERR_get_error(); + if(sslerror) + ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)); + else if(sockerr) + Curl_strerror(sockerr, error_buffer, sizeof(error_buffer)); + else { + strncpy(error_buffer, SSL_ERROR_to_str(err), sizeof(error_buffer)); + error_buffer[sizeof(error_buffer) - 1] = '\0'; + } + failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d", + error_buffer, sockerr); + *curlcode = CURLE_SEND_ERROR; + return -1; } - failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d", - error_buffer, sockerr); - *curlcode = CURLE_SEND_ERROR; - rc = -1; - goto out; - } - case SSL_ERROR_SSL: { + case SSL_ERROR_SSL: /* A failure in the SSL library occurred, usually a protocol error. The OpenSSL error queue contains more information on the error. */ - struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next); - struct ssl_connect_data *connssl_next = cf_ssl_next? - cf_ssl_next->ctx : NULL; sslerror = ERR_get_error(); if(ERR_GET_LIB(sslerror) == ERR_LIB_SSL && ERR_GET_REASON(sslerror) == SSL_R_BIO_NOT_SET && - connssl->state == ssl_connection_complete && - (connssl_next && connssl_next->state == ssl_connection_complete) + conn->ssl[sockindex].state == ssl_connection_complete +#ifndef CURL_DISABLE_PROXY + && conn->proxy_ssl[sockindex].state == ssl_connection_complete +#endif ) { char ver[120]; (void)ossl_version(ver, sizeof(ver)); @@ -4502,26 +4253,20 @@ static ssize_t ossl_send(struct Curl_cfilter *cf, failf(data, "SSL_write() error: %s", ossl_strerror(sslerror, error_buffer, sizeof(error_buffer))); *curlcode = CURLE_SEND_ERROR; - rc = -1; - goto out; - } - default: - /* a true error */ - failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d", - SSL_ERROR_to_str(err), SOCKERRNO); - *curlcode = CURLE_SEND_ERROR; - rc = -1; - goto out; + return -1; } + /* a true error */ + failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d", + SSL_ERROR_to_str(err), SOCKERRNO); + *curlcode = CURLE_SEND_ERROR; + return -1; } *curlcode = CURLE_OK; - -out: return (ssize_t)rc; /* number of bytes */ } -static ssize_t ossl_recv(struct Curl_cfilter *cf, - struct Curl_easy *data, /* transfer */ +static ssize_t ossl_recv(struct Curl_easy *data, /* transfer */ + int num, /* socketindex */ char *buf, /* store read data here */ size_t buffersize, /* max amount to read */ CURLcode *curlcode) @@ -4530,19 +4275,17 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf, unsigned long sslerror; ssize_t nread; int buffsize; - struct connectdata *conn = cf->conn; - struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct connectdata *conn = data->conn; + struct ssl_connect_data *connssl = &conn->ssl[num]; + struct ssl_backend_data *backend = connssl->backend; - (void)data; DEBUGASSERT(backend); ERR_clear_error(); buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; + set_logger(conn, data); nread = (ssize_t)SSL_read(backend->handle, buf, buffsize); - if(nread <= 0) { /* failed SSL_read */ int err = SSL_get_error(backend->handle, (int)nread); @@ -4552,7 +4295,7 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf, break; case SSL_ERROR_ZERO_RETURN: /* no more data */ /* close_notify alert */ - if(cf->sockindex == FIRSTSOCKET) + if(num == FIRSTSOCKET) /* mark the connection for close if it is indeed the control connection */ connclose(conn, "TLS close_notify"); @@ -4561,17 +4304,11 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf, case SSL_ERROR_WANT_WRITE: /* there's data pending, re-invoke SSL_read() */ *curlcode = CURLE_AGAIN; - nread = -1; - goto out; + return -1; default: /* openssl/ssl.h for SSL_ERROR_SYSCALL says "look at error stack/return value/errno" */ /* https://www.openssl.org/docs/crypto/ERR_get_error.html */ - if(backend->io_result == CURLE_AGAIN) { - *curlcode = CURLE_AGAIN; - nread = -1; - goto out; - } sslerror = ERR_get_error(); if((nread < 0) || sslerror) { /* If the return code was negative or there actually is an error in the @@ -4588,8 +4325,7 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf, failf(data, OSSL_PACKAGE " SSL_read: %s, errno %d", error_buffer, sockerr); *curlcode = CURLE_RECV_ERROR; - nread = -1; - goto out; + return -1; } /* For debug builds be a little stricter and error on any SSL_ERROR_SYSCALL. For example a server may have closed the connection @@ -4612,14 +4348,11 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf, " (Fatal because this is a curl debug build)", error_buffer, sockerr); *curlcode = CURLE_RECV_ERROR; - nread = -1; - goto out; + return -1; } #endif } } - -out: return nread; } @@ -4631,7 +4364,7 @@ static size_t ossl_version(char *buffer, size_t size) int count; const char *ver = OpenSSL_version(OPENSSL_VERSION); const char expected[] = OSSL_PACKAGE " "; /* ie "LibreSSL " */ - if(strncasecompare(ver, expected, sizeof(expected) - 1)) { + if(Curl_strncasecompare(ver, expected, sizeof(expected) - 1)) { ver += sizeof(expected) - 1; } count = msnprintf(buffer, size, "%s/%s", OSSL_PACKAGE, ver); @@ -4655,10 +4388,6 @@ static size_t ossl_version(char *buffer, size_t size) #else return msnprintf(buffer, size, OSSL_PACKAGE); #endif -#elif defined(OPENSSL_IS_AWSLC) - return msnprintf(buffer, size, "%s/%s", - OSSL_PACKAGE, - AWSLC_VERSION_NUMBER_STRING); #elif defined(HAVE_OPENSSL_VERSION) && defined(OPENSSL_VERSION_STRING) return msnprintf(buffer, size, "%s/%s", OSSL_PACKAGE, OpenSSL_version(OPENSSL_VERSION_STRING)); @@ -4745,7 +4474,7 @@ static CURLcode ossl_sha256sum(const unsigned char *tmp, /* input */ static bool ossl_cert_status_request(void) { #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ - !defined(OPENSSL_NO_OCSP) + !defined(OPENSSL_NO_OCSP) return TRUE; #else return FALSE; @@ -4756,25 +4485,90 @@ static void *ossl_get_internals(struct ssl_connect_data *connssl, CURLINFO info) { /* Legacy: CURLINFO_TLS_SESSION must return an SSL_CTX pointer. */ - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ssl_backend_data *backend = connssl->backend; DEBUGASSERT(backend); return info == CURLINFO_TLS_SESSION ? - (void *)backend->ctx : (void *)backend->handle; + (void *)backend->ctx : (void *)backend->handle; +} + +static bool ossl_associate_connection(struct Curl_easy *data, + struct connectdata *conn, + int sockindex) +{ + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + + /* If we don't have SSL context, do nothing. */ + if(!backend->handle) + return FALSE; + + if(SSL_SET_OPTION(primary.sessionid)) { + int data_idx = ossl_get_ssl_data_index(); + int connectdata_idx = ossl_get_ssl_conn_index(); + int sockindex_idx = ossl_get_ssl_sockindex_index(); + int proxy_idx = ossl_get_proxy_index(); + + if(data_idx >= 0 && connectdata_idx >= 0 && sockindex_idx >= 0 && + proxy_idx >= 0) { + int data_status, conn_status, sockindex_status, proxy_status; + + /* Store the data needed for the "new session" callback. + * The sockindex is stored as a pointer to an array element. */ + data_status = SSL_set_ex_data(backend->handle, data_idx, data); + conn_status = SSL_set_ex_data(backend->handle, connectdata_idx, conn); + sockindex_status = SSL_set_ex_data(backend->handle, sockindex_idx, + conn->sock + sockindex); +#ifndef CURL_DISABLE_PROXY + proxy_status = SSL_set_ex_data(backend->handle, proxy_idx, + SSL_IS_PROXY() ? (void *) 1 : NULL); +#else + proxy_status = SSL_set_ex_data(backend->handle, proxy_idx, NULL); +#endif + if(data_status && conn_status && sockindex_status && proxy_status) + return TRUE; + } + return FALSE; + } + return TRUE; } -static void ossl_free_multi_ssl_backend_data( - struct multi_ssl_backend_data *mbackend) +/* + * Starting with TLS 1.3, the ossl_new_session_cb callback gets called after + * the handshake. If the transfer that sets up the callback gets killed before + * this callback arrives, we must make sure to properly clear the data to + * avoid UAF problems. A future optimization could be to instead store another + * transfer that might still be using the same connection. + */ + +static void ossl_disassociate_connection(struct Curl_easy *data, + int sockindex) { -#if defined(HAVE_SSL_X509_STORE_SHARE) - if(mbackend->store) { - X509_STORE_free(mbackend->store); + struct connectdata *conn = data->conn; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + + /* If we don't have SSL context, do nothing. */ + if(!backend->handle) + return; + + if(SSL_SET_OPTION(primary.sessionid)) { + int data_idx = ossl_get_ssl_data_index(); + int connectdata_idx = ossl_get_ssl_conn_index(); + int sockindex_idx = ossl_get_ssl_sockindex_index(); + int proxy_idx = ossl_get_proxy_index(); + + if(data_idx >= 0 && connectdata_idx >= 0 && sockindex_idx >= 0 && + proxy_idx >= 0) { + /* Disable references to data in "new session" callback to avoid + * accessing a stale pointer. */ + SSL_set_ex_data(backend->handle, data_idx, NULL); + SSL_set_ex_data(backend->handle, connectdata_idx, NULL); + SSL_set_ex_data(backend->handle, sockindex_idx, NULL); + SSL_set_ex_data(backend->handle, proxy_idx, NULL); + } } - free(mbackend->CAfile); - free(mbackend); -#else /* HAVE_SSL_X509_STORE_SHARE */ - (void)mbackend; -#endif /* HAVE_SSL_X509_STORE_SHARE */ } const struct Curl_ssl Curl_ssl_openssl = { @@ -4790,19 +4584,19 @@ const struct Curl_ssl Curl_ssl_openssl = { #endif SSLSUPP_HTTPS_PROXY, - sizeof(struct ossl_ssl_backend_data), + sizeof(struct ssl_backend_data), ossl_init, /* init */ ossl_cleanup, /* cleanup */ ossl_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ + ossl_check_cxn, /* check_cxn */ ossl_shutdown, /* shutdown */ ossl_data_pending, /* data_pending */ ossl_random, /* random */ ossl_cert_status_request, /* cert_status_request */ ossl_connect, /* connect */ ossl_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks,/* getsock */ + Curl_ssl_getsock, /* getsock */ ossl_get_internals, /* get_internals */ ossl_close, /* close_one */ ossl_close_all, /* close_all */ @@ -4816,11 +4610,8 @@ const struct Curl_ssl Curl_ssl_openssl = { #else NULL, /* sha256sum */ #endif - NULL, /* use of data in this connection */ - NULL, /* remote of data from this connection */ - ossl_free_multi_ssl_backend_data, /* free_multi_ssl_backend_data */ - ossl_recv, /* recv decrypted data */ - ossl_send, /* send data to encrypt */ + ossl_associate_connection, /* associate_connection */ + ossl_disassociate_connection /* disassociate_connection */ }; #endif /* USE_OPENSSL */ diff --git a/contrib/libs/curl/lib/vtls/openssl.h b/contrib/libs/curl/lib/vtls/openssl.h index 950faab889..9df4ecddba 100644 --- a/contrib/libs/curl/lib/vtls/openssl.h +++ b/contrib/libs/curl/lib/vtls/openssl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -56,14 +56,5 @@ CURLcode Curl_ossl_set_client_cert(struct Curl_easy *data, CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl); -/** - * Setup the OpenSSL X509_STORE in `ssl_ctx` for the cfilter `cf` and - * easy handle `data`. Will allow reuse of a shared cache if suitable - * and configured. - */ -CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, - struct Curl_easy *data, - SSL_CTX *ssl_ctx); - #endif /* USE_OPENSSL */ #endif /* HEADER_CURL_SSLUSE_H */ diff --git a/contrib/libs/curl/lib/vtls/rustls.c b/contrib/libs/curl/lib/vtls/rustls.c index 76d3e24d23..77a49f1ab4 100644 --- a/contrib/libs/curl/lib/vtls/rustls.c +++ b/contrib/libs/curl/lib/vtls/rustls.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Jacob Hoffman-Andrews, + * Copyright (C) 2020 - 2022, Jacob Hoffman-Andrews, * <github@hoffman-andrews.com> * * This software is licensed as described in the file COPYING, which @@ -35,12 +35,11 @@ #include "urldata.h" #include "sendf.h" #include "vtls.h" -#include "vtls_int.h" #include "select.h" #include "strerror.h" #include "multiif.h" -struct rustls_ssl_backend_data +struct ssl_backend_data { const struct rustls_client_config *config; struct rustls_connection *conn; @@ -64,117 +63,43 @@ static CURLcode map_error(rustls_result r) } static bool -cr_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) +cr_data_pending(const struct connectdata *conn, int sockindex) { - struct ssl_connect_data *ctx = cf->ctx; - struct rustls_ssl_backend_data *backend; - - (void)data; - DEBUGASSERT(ctx && ctx->backend); - backend = (struct rustls_ssl_backend_data *)ctx->backend; + const struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); return backend->data_pending; } static CURLcode -cr_connect(struct Curl_cfilter *cf UNUSED_PARAM, - struct Curl_easy *data UNUSED_PARAM) +cr_connect(struct Curl_easy *data UNUSED_PARAM, + struct connectdata *conn UNUSED_PARAM, + int sockindex UNUSED_PARAM) { infof(data, "rustls_connect: unimplemented"); return CURLE_SSL_CONNECT_ERROR; } -struct io_ctx { - struct Curl_cfilter *cf; - struct Curl_easy *data; -}; - static int read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n) { - struct io_ctx *io_ctx = userdata; - CURLcode result; - int ret = 0; - ssize_t nread = Curl_conn_cf_recv(io_ctx->cf->next, io_ctx->data, - (char *)buf, len, &result); - if(nread < 0) { - nread = 0; - if(CURLE_AGAIN == result) - ret = EAGAIN; - else - ret = EINVAL; + ssize_t n = sread(*(int *)userdata, buf, len); + if(n < 0) { + return SOCKERRNO; } - *out_n = (int)nread; - /* - DEBUGF(LOG_CF(io_ctx->data, io_ctx->cf, "cf->next recv(len=%zu) -> %zd, %d", - len, nread, result)); - */ - return ret; + *out_n = n; + return 0; } static int write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n) { - struct io_ctx *io_ctx = userdata; - CURLcode result; - int ret = 0; - ssize_t nwritten = Curl_conn_cf_send(io_ctx->cf->next, io_ctx->data, - (const char *)buf, len, &result); - if(nwritten < 0) { - nwritten = 0; - if(CURLE_AGAIN == result) - ret = EAGAIN; - else - ret = EINVAL; + ssize_t n = swrite(*(int *)userdata, buf, len); + if(n < 0) { + return SOCKERRNO; } - *out_n = (int)nwritten; - /* - DEBUGF(LOG_CF(io_ctx->data, io_ctx->cf, "cf->next send(len=%zu) -> %zd, %d", - len, nwritten, result)); - */ - return ret; -} - -static ssize_t tls_recv_more(struct Curl_cfilter *cf, - struct Curl_easy *data, CURLcode *err) -{ - struct ssl_connect_data *const connssl = cf->ctx; - struct rustls_ssl_backend_data *const backend = - (struct rustls_ssl_backend_data *)connssl->backend; - struct io_ctx io_ctx; - size_t tls_bytes_read = 0; - rustls_io_result io_error; - rustls_result rresult = 0; - - io_ctx.cf = cf; - io_ctx.data = data; - io_error = rustls_connection_read_tls(backend->conn, read_cb, &io_ctx, - &tls_bytes_read); - if(io_error == EAGAIN || io_error == EWOULDBLOCK) { - *err = CURLE_AGAIN; - return -1; - } - else if(io_error) { - char buffer[STRERROR_LEN]; - failf(data, "reading from socket: %s", - Curl_strerror(io_error, buffer, sizeof(buffer))); - *err = CURLE_READ_ERROR; - return -1; - } - - rresult = rustls_connection_process_new_packets(backend->conn); - if(rresult != RUSTLS_RESULT_OK) { - char errorbuf[255]; - size_t errorlen; - rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen); - failf(data, "rustls_connection_process_new_packets: %.*s", - errorlen, errorbuf); - *err = map_error(rresult); - return -1; - } - - backend->data_pending = TRUE; - *err = CURLE_OK; - return (ssize_t)tls_bytes_read; + *out_n = n; + return 0; } /* @@ -190,86 +115,94 @@ static ssize_t tls_recv_more(struct Curl_cfilter *cf, * output buffer. */ static ssize_t -cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, +cr_recv(struct Curl_easy *data, int sockindex, char *plainbuf, size_t plainlen, CURLcode *err) { - struct ssl_connect_data *const connssl = cf->ctx; - struct rustls_ssl_backend_data *const backend = - (struct rustls_ssl_backend_data *)connssl->backend; + struct connectdata *conn = data->conn; + struct ssl_connect_data *const connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *const backend = connssl->backend; struct rustls_connection *rconn = NULL; + size_t n = 0; + size_t tls_bytes_read = 0; size_t plain_bytes_copied = 0; rustls_result rresult = 0; - ssize_t nread; - bool eof = FALSE; + char errorbuf[255]; + rustls_io_result io_error; DEBUGASSERT(backend); rconn = backend->conn; - while(plain_bytes_copied < plainlen) { - if(!backend->data_pending) { - if(tls_recv_more(cf, data, err) < 0) { - if(*err != CURLE_AGAIN) { - nread = -1; - goto out; - } - break; - } - } + io_error = rustls_connection_read_tls(rconn, read_cb, + &conn->sock[sockindex], &tls_bytes_read); + if(io_error == EAGAIN || io_error == EWOULDBLOCK) { + infof(data, "sread: EAGAIN or EWOULDBLOCK"); + } + else if(io_error) { + char buffer[STRERROR_LEN]; + failf(data, "reading from socket: %s", + Curl_strerror(io_error, buffer, sizeof(buffer))); + *err = CURLE_READ_ERROR; + return -1; + } + + infof(data, "cr_recv read %ld bytes from the network", tls_bytes_read); + + rresult = rustls_connection_process_new_packets(rconn); + if(rresult != RUSTLS_RESULT_OK) { + rustls_error(rresult, errorbuf, sizeof(errorbuf), &n); + failf(data, "%.*s", n, errorbuf); + *err = map_error(rresult); + return -1; + } + backend->data_pending = TRUE; + + while(plain_bytes_copied < plainlen) { rresult = rustls_connection_read(rconn, (uint8_t *)plainbuf + plain_bytes_copied, plainlen - plain_bytes_copied, &n); if(rresult == RUSTLS_RESULT_PLAINTEXT_EMPTY) { + infof(data, "cr_recv got PLAINTEXT_EMPTY. will try again later."); backend->data_pending = FALSE; - } - else if(rresult == RUSTLS_RESULT_UNEXPECTED_EOF) { - failf(data, "rustls: peer closed TCP connection " - "without first closing TLS connection"); - *err = CURLE_READ_ERROR; - nread = -1; - goto out; + break; } else if(rresult != RUSTLS_RESULT_OK) { /* n always equals 0 in this case, don't need to check it */ - char errorbuf[255]; - size_t errorlen; - rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen); - failf(data, "rustls_connection_read: %.*s", errorlen, errorbuf); + failf(data, "error in rustls_connection_read: %d", rresult); *err = CURLE_READ_ERROR; - nread = -1; - goto out; + return -1; } else if(n == 0) { /* n == 0 indicates clean EOF, but we may have read some other plaintext bytes before we reached this. Break out of the loop so we can figure out whether to return success or EOF. */ - eof = TRUE; break; } else { + infof(data, "cr_recv copied out %ld bytes of plaintext", n); plain_bytes_copied += n; } } if(plain_bytes_copied) { *err = CURLE_OK; - nread = (ssize_t)plain_bytes_copied; + return plain_bytes_copied; } - else if(eof) { - *err = CURLE_OK; - nread = 0; - } - else { + + /* If we wrote out 0 plaintext bytes, that means either we hit a clean EOF, + OR we got a RUSTLS_RESULT_PLAINTEXT_EMPTY. + If the latter, return CURLE_AGAIN so curl doesn't treat this as EOF. */ + if(!backend->data_pending) { *err = CURLE_AGAIN; - nread = -1; + return -1; } -out: - DEBUGF(LOG_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", - plainlen, nread, *err)); - return nread; + /* Zero bytes read, and no RUSTLS_RESULT_PLAINTEXT_EMPTY, means the TCP + connection was cleanly closed (with a close_notify alert). */ + *err = CURLE_OK; + return 0; } /* @@ -283,52 +216,44 @@ out: * It will only drain rustls' plaintext output buffer into the socket. */ static ssize_t -cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, +cr_send(struct Curl_easy *data, int sockindex, const void *plainbuf, size_t plainlen, CURLcode *err) { - struct ssl_connect_data *const connssl = cf->ctx; - struct rustls_ssl_backend_data *const backend = - (struct rustls_ssl_backend_data *)connssl->backend; + struct connectdata *conn = data->conn; + struct ssl_connect_data *const connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *const backend = connssl->backend; struct rustls_connection *rconn = NULL; - struct io_ctx io_ctx; size_t plainwritten = 0; size_t tlswritten = 0; size_t tlswritten_total = 0; rustls_result rresult; rustls_io_result io_error; - char errorbuf[256]; - size_t errorlen; DEBUGASSERT(backend); rconn = backend->conn; - DEBUGF(LOG_CF(data, cf, "cf_send: %ld plain bytes", plainlen)); - - io_ctx.cf = cf; - io_ctx.data = data; + infof(data, "cr_send %ld bytes of plaintext", plainlen); if(plainlen > 0) { rresult = rustls_connection_write(rconn, plainbuf, plainlen, &plainwritten); if(rresult != RUSTLS_RESULT_OK) { - rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen); - failf(data, "rustls_connection_write: %.*s", errorlen, errorbuf); + failf(data, "error in rustls_connection_write"); *err = CURLE_WRITE_ERROR; return -1; } else if(plainwritten == 0) { - failf(data, "rustls_connection_write: EOF"); + failf(data, "EOF in rustls_connection_write"); *err = CURLE_WRITE_ERROR; return -1; } } while(rustls_connection_wants_write(rconn)) { - io_error = rustls_connection_write_tls(rconn, write_cb, &io_ctx, - &tlswritten); + io_error = rustls_connection_write_tls(rconn, write_cb, + &conn->sock[sockindex], &tlswritten); if(io_error == EAGAIN || io_error == EWOULDBLOCK) { - DEBUGF(LOG_CF(data, cf, "cf_send: EAGAIN after %zu bytes", - tlswritten_total)); + infof(data, "swrite: EAGAIN after %ld bytes", tlswritten_total); *err = CURLE_AGAIN; return -1; } @@ -344,7 +269,7 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, *err = CURLE_WRITE_ERROR; return -1; } - DEBUGF(LOG_CF(data, cf, "cf_send: wrote %zu TLS bytes", tlswritten)); + infof(data, "cr_send wrote %ld bytes to network", tlswritten); tlswritten_total += tlswritten; } @@ -377,42 +302,37 @@ cr_hostname_is_ip(const char *hostname) } static CURLcode -cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, - struct rustls_ssl_backend_data *const backend) +cr_init_backend(struct Curl_easy *data, struct connectdata *conn, + struct ssl_backend_data *const backend) { - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct rustls_connection *rconn = NULL; struct rustls_client_config_builder *config_builder = NULL; struct rustls_root_cert_store *roots = NULL; - const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; + const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob); const char * const ssl_cafile = /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ - (ca_info_blob ? NULL : conn_config->CAfile); - const bool verifypeer = conn_config->verifypeer; - const char *hostname = connssl->hostname; + (ca_info_blob ? NULL : SSL_CONN_CONFIG(CAfile)); + const bool verifypeer = SSL_CONN_CONFIG(verifypeer); + const char *hostname = conn->host.name; char errorbuf[256]; size_t errorlen; int result; + rustls_slice_bytes alpn[2] = { + { (const uint8_t *)ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH }, + { (const uint8_t *)ALPN_H2, ALPN_H2_LENGTH }, + }; DEBUGASSERT(backend); rconn = backend->conn; config_builder = rustls_client_config_builder_new(); - if(connssl->alpn) { - struct alpn_proto_buf proto; - rustls_slice_bytes alpn[ALPN_ENTRIES_MAX]; - size_t i; - - for(i = 0; i < connssl->alpn->count; ++i) { - alpn[i].data = (const uint8_t *)connssl->alpn->entries[i]; - alpn[i].len = strlen(connssl->alpn->entries[i]); - } - rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, - connssl->alpn->count); - Curl_alpn_to_proto_str(&proto, connssl->alpn); - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); - } +#ifdef USE_HTTP2 + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); + rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, 2); +#else + rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, 1); +#endif + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); if(!verifypeer) { rustls_client_config_builder_dangerous_set_certificate_verifier( config_builder, cr_verify_none); @@ -432,7 +352,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, result = rustls_root_cert_store_add_pem(roots, ca_info_blob->data, ca_info_blob->len, verifypeer); if(result != RUSTLS_RESULT_OK) { - failf(data, "rustls: failed to parse trusted certificates from blob"); + failf(data, "failed to parse trusted certificates from blob"); rustls_root_cert_store_free(roots); rustls_client_config_free( rustls_client_config_builder_build(config_builder)); @@ -442,7 +362,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, result = rustls_client_config_builder_use_roots(config_builder, roots); rustls_root_cert_store_free(roots); if(result != RUSTLS_RESULT_OK) { - failf(data, "rustls: failed to load trusted certificates"); + failf(data, "failed to load trusted certificates"); rustls_client_config_free( rustls_client_config_builder_build(config_builder)); return CURLE_SSL_CACERT_BADFILE; @@ -452,7 +372,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, result = rustls_client_config_builder_load_roots_from_file( config_builder, ssl_cafile); if(result != RUSTLS_RESULT_OK) { - failf(data, "rustls: failed to load trusted certificates"); + failf(data, "failed to load trusted certificates"); rustls_client_config_free( rustls_client_config_builder_build(config_builder)); return CURLE_SSL_CACERT_BADFILE; @@ -464,7 +384,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, { char *snihost = Curl_ssl_snihost(data, hostname, NULL); if(!snihost) { - failf(data, "rustls: failed to get SNI"); + failf(data, "Failed to set SNI"); return CURLE_SSL_CONNECT_ERROR; } result = rustls_client_connection_new(backend->config, snihost, &rconn); @@ -480,24 +400,45 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, } static void -cr_set_negotiated_alpn(struct Curl_cfilter *cf, struct Curl_easy *data, +cr_set_negotiated_alpn(struct Curl_easy *data, struct connectdata *conn, const struct rustls_connection *rconn) { const uint8_t *protocol = NULL; size_t len = 0; rustls_connection_get_alpn_protocol(rconn, &protocol, &len); - Curl_alpn_set_negotiated(cf, data, protocol, len); + if(!protocol) { + infof(data, VTLS_INFOF_NO_ALPN); + return; + } + +#ifdef USE_HTTP2 + if(len == ALPN_H2_LENGTH && 0 == memcmp(ALPN_H2, protocol, len)) { + infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, ALPN_H2); + conn->alpn = CURL_HTTP_VERSION_2; + } + else +#endif + if(len == ALPN_HTTP_1_1_LENGTH && + 0 == memcmp(ALPN_HTTP_1_1, protocol, len)) { + infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, ALPN_HTTP_1_1); + conn->alpn = CURL_HTTP_VERSION_1_1; + } + else { + infof(data, "ALPN, negotiated an unrecognized protocol"); + } + + Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ? + BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); } static CURLcode -cr_connect_nonblocking(struct Curl_cfilter *cf, - struct Curl_easy *data, bool *done) +cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn, + int sockindex, bool *done) { - struct ssl_connect_data *const connssl = cf->ctx; - curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); - struct rustls_ssl_backend_data *const backend = - (struct rustls_ssl_backend_data *)connssl->backend; + struct ssl_connect_data *const connssl = &conn->ssl[sockindex]; + curl_socket_t sockfd = conn->sock[sockindex]; + struct ssl_backend_data *const backend = connssl->backend; struct rustls_connection *rconn = NULL; CURLcode tmperr = CURLE_OK; int result; @@ -510,8 +451,7 @@ cr_connect_nonblocking(struct Curl_cfilter *cf, DEBUGASSERT(backend); if(ssl_connection_none == connssl->state) { - result = cr_init_backend(cf, data, - (struct rustls_ssl_backend_data *)connssl->backend); + result = cr_init_backend(data, conn, connssl->backend); if(result != CURLE_OK) { return result; } @@ -531,8 +471,10 @@ cr_connect_nonblocking(struct Curl_cfilter *cf, /* Done with the handshake. Set up callbacks to send/receive data. */ connssl->state = ssl_connection_complete; - cr_set_negotiated_alpn(cf, data, rconn); + cr_set_negotiated_alpn(data, conn, rconn); + conn->recv[sockindex] = cr_recv; + conn->send[sockindex] = cr_send; *done = TRUE; return CURLE_OK; } @@ -560,7 +502,7 @@ cr_connect_nonblocking(struct Curl_cfilter *cf, if(wants_write) { infof(data, "rustls_connection wants us to write_tls."); - cr_send(cf, data, NULL, 0, &tmperr); + cr_send(data, sockindex, NULL, 0, &tmperr); if(tmperr == CURLE_AGAIN) { infof(data, "writing would block"); /* fall through */ @@ -573,12 +515,13 @@ cr_connect_nonblocking(struct Curl_cfilter *cf, if(wants_read) { infof(data, "rustls_connection wants us to read_tls."); - if(tls_recv_more(cf, data, &tmperr) < 0) { - if(tmperr == CURLE_AGAIN) { - infof(data, "reading would block"); - /* fall through */ - } - else if(tmperr == CURLE_READ_ERROR) { + cr_recv(data, sockindex, NULL, 0, &tmperr); + if(tmperr == CURLE_AGAIN) { + infof(data, "reading would block"); + /* fall through */ + } + else if(tmperr != CURLE_OK) { + if(tmperr == CURLE_READ_ERROR) { return CURLE_SSL_CONNECT_ERROR; } else { @@ -596,16 +539,13 @@ cr_connect_nonblocking(struct Curl_cfilter *cf, /* returns a bitmap of flags for this connection's first socket indicating whether we want to read or write */ static int -cr_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) +cr_getsock(struct connectdata *conn, curl_socket_t *socks) { - struct ssl_connect_data *const connssl = cf->ctx; - curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); - struct rustls_ssl_backend_data *const backend = - (struct rustls_ssl_backend_data *)connssl->backend; + struct ssl_connect_data *const connssl = &conn->ssl[FIRSTSOCKET]; + curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; + struct ssl_backend_data *const backend = connssl->backend; struct rustls_connection *rconn = NULL; - (void)data; DEBUGASSERT(backend); rconn = backend->conn; @@ -625,18 +565,17 @@ static void * cr_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { - struct rustls_ssl_backend_data *backend = - (struct rustls_ssl_backend_data *)connssl->backend; + struct ssl_backend_data *backend = connssl->backend; DEBUGASSERT(backend); return &backend->conn; } static void -cr_close(struct Curl_cfilter *cf, struct Curl_easy *data) +cr_close(struct Curl_easy *data, struct connectdata *conn, + int sockindex) { - struct ssl_connect_data *connssl = cf->ctx; - struct rustls_ssl_backend_data *backend = - (struct rustls_ssl_backend_data *)connssl->backend; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; CURLcode tmperr = CURLE_OK; ssize_t n = 0; @@ -644,9 +583,9 @@ cr_close(struct Curl_cfilter *cf, struct Curl_easy *data) if(backend->conn) { rustls_connection_send_close_notify(backend->conn); - n = cr_send(cf, data, NULL, 0, &tmperr); + n = cr_send(data, sockindex, NULL, 0, &tmperr); if(n < 0) { - failf(data, "rustls: error sending close_notify: %d", tmperr); + failf(data, "error sending close notify: %d", tmperr); } rustls_connection_free(backend->conn); @@ -667,9 +606,8 @@ static size_t cr_version(char *buffer, size_t size) const struct Curl_ssl Curl_ssl_rustls = { { CURLSSLBACKEND_RUSTLS, "rustls" }, SSLSUPP_CAINFO_BLOB | /* supports */ - SSLSUPP_TLS13_CIPHERSUITES | - SSLSUPP_HTTPS_PROXY, - sizeof(struct rustls_ssl_backend_data), + SSLSUPP_TLS13_CIPHERSUITES, + sizeof(struct ssl_backend_data), Curl_none_init, /* init */ Curl_none_cleanup, /* cleanup */ @@ -681,7 +619,7 @@ const struct Curl_ssl Curl_ssl_rustls = { Curl_none_cert_status_request, /* cert_status_request */ cr_connect, /* connect */ cr_connect_nonblocking, /* connect_nonblocking */ - cr_get_select_socks, /* get_select_socks */ + cr_getsock, /* cr_getsock */ cr_get_internals, /* get_internals */ cr_close, /* close_one */ Curl_none_close_all, /* close_all */ @@ -692,10 +630,7 @@ const struct Curl_ssl Curl_ssl_rustls = { Curl_none_false_start, /* false_start */ NULL, /* sha256sum */ NULL, /* associate_connection */ - NULL, /* disassociate_connection */ - NULL, /* free_multi_ssl_backend_data */ - cr_recv, /* recv decrypted data */ - cr_send, /* send data to encrypt */ + NULL /* disassociate_connection */ }; #endif /* USE_RUSTLS */ diff --git a/contrib/libs/curl/lib/vtls/rustls.h b/contrib/libs/curl/lib/vtls/rustls.h index bfbe23de3e..6b393dd639 100644 --- a/contrib/libs/curl/lib/vtls/rustls.h +++ b/contrib/libs/curl/lib/vtls/rustls.h @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Jacob Hoffman-Andrews, + * Copyright (C) 2020 - 2022, Jacob Hoffman-Andrews, * <github@hoffman-andrews.com> * * This software is licensed as described in the file COPYING, which diff --git a/contrib/libs/curl/lib/vtls/schannel.h b/contrib/libs/curl/lib/vtls/schannel.h index 43309fc66d..2acf4dc8ce 100644 --- a/contrib/libs/curl/lib/vtls/schannel.h +++ b/contrib/libs/curl/lib/vtls/schannel.h @@ -7,8 +7,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Marc Hoersken, <info@marc-hoersken.de>, et al. - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012, Marc Hoersken, <info@marc-hoersken.de>, et al. + * Copyright (C) 2012 - 2022, 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 @@ -28,6 +28,8 @@ #ifdef USE_SCHANNEL +#define SCHANNEL_USE_BLACKLISTS 1 + #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable: 4201) @@ -52,7 +54,6 @@ #include <schannel.h> #include "curl_sspi.h" -#include "cfilters.h" #include "urldata.h" /* <wincrypt.h> has been included via the above <schnlsp.h>. @@ -76,8 +77,124 @@ extern const struct Curl_ssl Curl_ssl_schannel; -CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, - struct Curl_easy *data); +CURLcode Curl_verify_certificate(struct Curl_easy *data, + struct connectdata *conn, int sockindex); + +/* structs to expose only in schannel.c and schannel_verify.c */ +#ifdef EXPOSE_SCHANNEL_INTERNAL_STRUCTS + +#include <wincrypt.h> + +#ifdef __MINGW32__ +#ifdef __MINGW64_VERSION_MAJOR +#define HAS_MANUAL_VERIFY_API +#endif +#else +#ifdef CERT_CHAIN_REVOCATION_CHECK_CHAIN +#define HAS_MANUAL_VERIFY_API +#endif +#endif + +#if defined(CryptStringToBinary) && defined(CRYPT_STRING_HEX) \ + && !defined(DISABLE_SCHANNEL_CLIENT_CERT) +#define HAS_CLIENT_CERT_PATH +#endif + +#ifndef SCH_CREDENTIALS_VERSION + +#define SCH_CREDENTIALS_VERSION 0x00000005 + +typedef enum _eTlsAlgorithmUsage +{ + TlsParametersCngAlgUsageKeyExchange, + TlsParametersCngAlgUsageSignature, + TlsParametersCngAlgUsageCipher, + TlsParametersCngAlgUsageDigest, + TlsParametersCngAlgUsageCertSig +} eTlsAlgorithmUsage; + +typedef struct _CRYPTO_SETTINGS +{ + eTlsAlgorithmUsage eAlgorithmUsage; + UNICODE_STRING strCngAlgId; + DWORD cChainingModes; + PUNICODE_STRING rgstrChainingModes; + DWORD dwMinBitLength; + DWORD dwMaxBitLength; +} CRYPTO_SETTINGS, * PCRYPTO_SETTINGS; + +typedef struct _TLS_PARAMETERS +{ + DWORD cAlpnIds; + PUNICODE_STRING rgstrAlpnIds; + DWORD grbitDisabledProtocols; + DWORD cDisabledCrypto; + PCRYPTO_SETTINGS pDisabledCrypto; + DWORD dwFlags; +} TLS_PARAMETERS, * PTLS_PARAMETERS; + +typedef struct _SCH_CREDENTIALS +{ + DWORD dwVersion; + DWORD dwCredFormat; + DWORD cCreds; + PCCERT_CONTEXT* paCred; + HCERTSTORE hRootStore; + + DWORD cMappers; + struct _HMAPPER **aphMappers; + + DWORD dwSessionLifespan; + DWORD dwFlags; + DWORD cTlsParameters; + PTLS_PARAMETERS pTlsParameters; +} SCH_CREDENTIALS, * PSCH_CREDENTIALS; + +#define SCH_CRED_MAX_SUPPORTED_PARAMETERS 16 +#define SCH_CRED_MAX_SUPPORTED_ALPN_IDS 16 +#define SCH_CRED_MAX_SUPPORTED_CRYPTO_SETTINGS 16 +#define SCH_CRED_MAX_SUPPORTED_CHAINING_MODES 16 + +#endif + +struct Curl_schannel_cred { + CredHandle cred_handle; + TimeStamp time_stamp; + TCHAR *sni_hostname; +#ifdef HAS_CLIENT_CERT_PATH + HCERTSTORE client_cert_store; +#endif + int refcount; +}; + +struct Curl_schannel_ctxt { + CtxtHandle ctxt_handle; + TimeStamp time_stamp; +}; + +struct ssl_backend_data { + struct Curl_schannel_cred *cred; + struct Curl_schannel_ctxt *ctxt; + SecPkgContext_StreamSizes stream_sizes; + size_t encdata_length, decdata_length; + size_t encdata_offset, decdata_offset; + unsigned char *encdata_buffer, *decdata_buffer; + /* encdata_is_incomplete: if encdata contains only a partial record that + can't be decrypted without another Curl_read_plain (that is, status is + SEC_E_INCOMPLETE_MESSAGE) then set this true. after Curl_read_plain writes + more bytes into encdata then set this back to false. */ + bool encdata_is_incomplete; + unsigned long req_flags, ret_flags; + CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */ + bool recv_sspi_close_notify; /* true if connection closed by close_notify */ + bool recv_connection_closed; /* true if connection closed, regardless how */ + bool recv_renegotiating; /* true if recv is doing renegotiation */ + bool use_alpn; /* true if ALPN is used for this connection */ +#ifdef HAS_MANUAL_VERIFY_API + bool use_manual_cred_validation; /* true if manual cred validation is used */ +#endif +}; +#endif /* EXPOSE_SCHANNEL_INTERNAL_STRUCTS */ #endif /* USE_SCHANNEL */ #endif /* HEADER_CURL_SCHANNEL_H */ diff --git a/contrib/libs/curl/lib/vtls/sectransp.c b/contrib/libs/curl/lib/vtls/sectransp.c index 32bb3a5a79..c764e3631b 100644 --- a/contrib/libs/curl/lib/vtls/sectransp.c +++ b/contrib/libs/curl/lib/vtls/sectransp.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) Nick Zitzmann, <nickzman@gmail.com>. + * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2017, Nick Zitzmann, <nickzman@gmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -45,11 +45,6 @@ #pragma clang diagnostic ignored "-Wtautological-pointer-compare" #endif /* __clang__ */ -#ifdef __GNUC__ -#pragma GCC diagnostic ignored "-Waddress" -#pragma GCC diagnostic ignored "-Wundef" -#endif - #include <limits.h> #include <Security/Security.h> @@ -127,12 +122,12 @@ #include <sys/sysctl.h> #endif /* CURL_BUILD_MAC */ +#include "urldata.h" #include "sendf.h" #include "inet_pton.h" #include "connect.h" #include "select.h" #include "vtls.h" -#include "vtls_int.h" #include "sectransp.h" #include "curl_printf.h" #include "strdup.h" @@ -141,13 +136,13 @@ /* The last #include file should be: */ #include "memdebug.h" - /* From MacTypes.h (which we can't include because it isn't present in iOS: */ #define ioErr -36 #define paramErr -50 -struct st_ssl_backend_data { +struct ssl_backend_data { SSLContextRef ssl_ctx; + curl_socket_t ssl_sockfd; bool ssl_direction; /* true if writing, false if reading */ size_t ssl_write_buffered_length; }; @@ -239,7 +234,7 @@ struct st_cipher { insert in between existing items to appropriate place based on cipher suite IANA number */ -static const struct st_cipher ciphertable[] = { +const static struct st_cipher ciphertable[] = { /* SSL version 3.0 and initial TLS 1.0 cipher suites. Defined since SDK 10.2.8 */ CIPHER_DEF_SSLTLS(NULL_WITH_NULL_NULL, /* 0x0000 */ @@ -830,78 +825,116 @@ static const unsigned char ecDsaSecp384r1SpkiHeader[] = { #endif /* SECTRANSP_PINNEDPUBKEY_V1 */ #endif /* SECTRANSP_PINNEDPUBKEY */ -static OSStatus bio_cf_in_read(SSLConnectionRef connection, - void *buf, - size_t *dataLength) /* IN/OUT */ +/* The following two functions were ripped from Apple sample code, + * with some modifications: */ +static OSStatus SocketRead(SSLConnectionRef connection, + void *data, /* owned by + * caller, data + * RETURNED */ + size_t *dataLength) /* IN/OUT */ { - struct Curl_cfilter *cf = (struct Curl_cfilter *)connection; - struct ssl_connect_data *connssl = cf->ctx; - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nread; - CURLcode result; + size_t bytesToGo = *dataLength; + size_t initLen = bytesToGo; + UInt8 *currData = (UInt8 *)data; + /*int sock = *(int *)connection;*/ + struct ssl_connect_data *connssl = (struct ssl_connect_data *)connection; + struct ssl_backend_data *backend = connssl->backend; + int sock; OSStatus rtn = noErr; + size_t bytesRead; + ssize_t rrtn; + int theErr; - DEBUGASSERT(data); - nread = Curl_conn_cf_recv(cf->next, data, buf, *dataLength, &result); - DEBUGF(LOG_CF(data, cf, "bio_read(len=%zu) -> %zd, result=%d", - *dataLength, nread, result)); - if(nread < 0) { - switch(result) { - case CURLE_OK: - case CURLE_AGAIN: - rtn = errSSLWouldBlock; - backend->ssl_direction = false; - break; - default: - rtn = ioErr; - break; + DEBUGASSERT(backend); + sock = backend->ssl_sockfd; + *dataLength = 0; + + for(;;) { + bytesRead = 0; + rrtn = read(sock, currData, bytesToGo); + if(rrtn <= 0) { + /* this is guesswork... */ + theErr = errno; + if(rrtn == 0) { /* EOF = server hung up */ + /* the framework will turn this into errSSLClosedNoNotify */ + rtn = errSSLClosedGraceful; + } + else /* do the switch */ + switch(theErr) { + case ENOENT: + /* connection closed */ + rtn = errSSLClosedGraceful; + break; + case ECONNRESET: + rtn = errSSLClosedAbort; + break; + case EAGAIN: + rtn = errSSLWouldBlock; + backend->ssl_direction = false; + break; + default: + rtn = ioErr; + break; + } + break; + } + else { + bytesRead = rrtn; + } + bytesToGo -= bytesRead; + currData += bytesRead; + + if(bytesToGo == 0) { + /* filled buffer with incoming data, done */ + break; } - nread = 0; - } - else if(nread == 0) { - rtn = errSSLClosedGraceful; - } - else if((size_t)nread < *dataLength) { - rtn = errSSLWouldBlock; } - *dataLength = nread; + *dataLength = initLen - bytesToGo; + return rtn; } -static OSStatus bio_cf_out_write(SSLConnectionRef connection, - const void *buf, - size_t *dataLength) /* IN/OUT */ +static OSStatus SocketWrite(SSLConnectionRef connection, + const void *data, + size_t *dataLength) /* IN/OUT */ { - struct Curl_cfilter *cf = (struct Curl_cfilter *)connection; - struct ssl_connect_data *connssl = cf->ctx; - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nwritten; - CURLcode result; - OSStatus rtn = noErr; + size_t bytesSent = 0; + /*int sock = *(int *)connection;*/ + struct ssl_connect_data *connssl = (struct ssl_connect_data *)connection; + struct ssl_backend_data *backend = connssl->backend; + int sock; + ssize_t length; + size_t dataLen = *dataLength; + const UInt8 *dataPtr = (UInt8 *)data; + OSStatus ortn; + int theErr; - DEBUGASSERT(data); - nwritten = Curl_conn_cf_send(cf->next, data, buf, *dataLength, &result); - DEBUGF(LOG_CF(data, cf, "bio_send(len=%zu) -> %zd, result=%d", - *dataLength, nwritten, result)); - if(nwritten <= 0) { - if(result == CURLE_AGAIN) { - rtn = errSSLWouldBlock; + DEBUGASSERT(backend); + sock = backend->ssl_sockfd; + *dataLength = 0; + + do { + length = write(sock, + (char *)dataPtr + bytesSent, + dataLen - bytesSent); + } while((length > 0) && + ( (bytesSent += length) < dataLen) ); + + if(length <= 0) { + theErr = errno; + if(theErr == EAGAIN) { + ortn = errSSLWouldBlock; backend->ssl_direction = true; } else { - rtn = ioErr; + ortn = ioErr; } - nwritten = 0; } - else if((size_t)nwritten < *dataLength) { - rtn = errSSLWouldBlock; + else { + ortn = noErr; } - *dataLength = nwritten; - return rtn; + *dataLength = bytesSent; + return ortn; } #ifndef CURL_DISABLE_VERBOSE_STRINGS @@ -910,12 +943,12 @@ CF_INLINE const char *TLSCipherNameForNumber(SSLCipherSuite cipher) /* The first ciphers in the ciphertable are continuous. Here we do small optimization and instead of loop directly get SSL name by cipher number. */ - size_t i; if(cipher <= SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA) { return ciphertable[cipher].name; } /* Iterate through the rest of the ciphers */ - for(i = SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA + 1; i < NUM_OF_CIPHERS; + for(size_t i = SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA + 1; + i < NUM_OF_CIPHERS; ++i) { if(ciphertable[i].num == cipher) { return ciphertable[i].name; @@ -1339,15 +1372,14 @@ static CURLcode sectransp_version_from_curl(SSLProtocol *darwinver, } #endif -static CURLcode set_ssl_version_min_max(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode +set_ssl_version_min_max(struct Curl_easy *data, struct connectdata *conn, + int sockindex) { - struct ssl_connect_data *connssl = cf->ctx; - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - long ssl_version = conn_config->version; - long ssl_version_max = conn_config->version_max; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; + long ssl_version = SSL_CONN_CONFIG(version); + long ssl_version_max = SSL_CONN_CONFIG(version_max); long max_supported_version_by_os; DEBUGASSERT(backend); @@ -1440,8 +1472,7 @@ static CURLcode set_ssl_version_min_max(struct Curl_cfilter *cf, static bool is_cipher_suite_strong(SSLCipherSuite suite_num) { - size_t i; - for(i = 0; i < NUM_OF_CIPHERS; ++i) { + for(size_t i = 0; i < NUM_OF_CIPHERS; ++i) { if(ciphertable[i].num == suite_num) { return !ciphertable[i].weak; } @@ -1557,17 +1588,16 @@ static CURLcode sectransp_set_selected_ciphers(struct Curl_easy *data, size_t cipher_len = 0; const char *cipher_end = NULL; bool tls_name = FALSE; - size_t i; /* Skip separators */ while(is_separator(*cipher_start)) - cipher_start++; + cipher_start++; if(*cipher_start == '\0') { break; } /* Find last position of a cipher in the ciphers string */ cipher_end = cipher_start; - while(*cipher_end != '\0' && !is_separator(*cipher_end)) { + while (*cipher_end != '\0' && !is_separator(*cipher_end)) { ++cipher_end; } @@ -1581,7 +1611,7 @@ static CURLcode sectransp_set_selected_ciphers(struct Curl_easy *data, /* Iterate through the cipher table and look for the cipher, starting the cipher number 0x01 because the 0x00 is not the real cipher */ cipher_len = cipher_end - cipher_start; - for(i = 1; i < NUM_OF_CIPHERS; ++i) { + for(size_t i = 1; i < NUM_OF_CIPHERS; ++i) { const char *table_cipher_name = NULL; if(tls_name) { table_cipher_name = ciphertable[i].name; @@ -1635,21 +1665,23 @@ static CURLcode sectransp_set_selected_ciphers(struct Curl_easy *data, return CURLE_OK; } -static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode sectransp_connect_step1(struct Curl_easy *data, + struct connectdata *conn, + int sockindex) { - struct ssl_connect_data *connssl = cf->ctx; - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - const struct curl_blob *ssl_cablob = conn_config->ca_info_blob; + curl_socket_t sockfd = conn->sock[sockindex]; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; + const struct curl_blob *ssl_cablob = SSL_CONN_CONFIG(ca_info_blob); const char * const ssl_cafile = /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ - (ssl_cablob ? NULL : conn_config->CAfile); - const bool verifypeer = conn_config->verifypeer; - char * const ssl_cert = ssl_config->primary.clientcert; - const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; + (ssl_cablob ? NULL : SSL_CONN_CONFIG(CAfile)); + const bool verifypeer = SSL_CONN_CONFIG(verifypeer); + char * const ssl_cert = SSL_SET_OPTION(primary.clientcert); + const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(primary.cert_blob); + bool isproxy = SSL_IS_PROXY(); + const char * const hostname = SSL_HOST_NAME(); + const long int port = SSL_HOST_PORT(); #ifdef ENABLE_IPV6 struct in6_addr addr; #else @@ -1662,7 +1694,6 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, DEBUGASSERT(backend); - DEBUGF(LOG_CF(data, cf, "connect_step1")); GetDarwinVersionNumber(&darwinver_maj, &darwinver_min); #endif /* CURL_BUILD_MAC */ @@ -1702,7 +1733,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, /* check to see if we've been told to use an explicit SSL/TLS version */ #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS if(SSLSetProtocolVersionMax) { - switch(conn_config->version) { + switch(conn->ssl_config.version) { case CURL_SSLVERSION_TLSv1: (void)SSLSetProtocolVersionMin(backend->ssl_ctx, kTLSProtocol1); #if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1 @@ -1723,7 +1754,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, case CURL_SSLVERSION_TLSv1_2: case CURL_SSLVERSION_TLSv1_3: { - CURLcode result = set_ssl_version_min_max(cf, data); + CURLcode result = set_ssl_version_min_max(data, conn, sockindex); if(result != CURLE_OK) return result; break; @@ -1742,7 +1773,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, kSSLProtocolAll, false); - switch(conn_config->version) { + switch(conn->ssl_config.version) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, @@ -1760,7 +1791,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, case CURL_SSLVERSION_TLSv1_2: case CURL_SSLVERSION_TLSv1_3: { - CURLcode result = set_ssl_version_min_max(cf, data); + CURLcode result = set_ssl_version_min_max(data, conn, sockindex); if(result != CURLE_OK) return result; break; @@ -1776,13 +1807,13 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, #endif /* CURL_SUPPORT_MAC_10_8 */ } #else - if(conn_config->version_max != CURL_SSLVERSION_MAX_NONE) { + if(conn->ssl_config.version_max != CURL_SSLVERSION_MAX_NONE) { failf(data, "Your version of the OS does not support to set maximum" " SSL/TLS version"); return CURLE_SSL_CONNECT_ERROR; } (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, kSSLProtocolAll, false); - switch(conn_config->version) { + switch(conn->ssl_config.version) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: case CURL_SSLVERSION_TLSv1_0: @@ -1810,33 +1841,38 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, #endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ #if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1 - if(connssl->alpn) { + if(conn->bits.tls_enable_alpn) { if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) { - struct alpn_proto_buf proto; - size_t i; - CFStringRef cstr; CFMutableArrayRef alpnArr = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - for(i = 0; i < connssl->alpn->count; ++i) { - cstr = CFStringCreateWithCString(NULL, connssl->alpn->entries[i], - kCFStringEncodingUTF8); - if(!cstr) - return CURLE_OUT_OF_MEMORY; - CFArrayAppendValue(alpnArr, cstr); - CFRelease(cstr); + +#ifdef USE_HTTP2 + if(data->state.httpwant >= CURL_HTTP_VERSION_2 +#ifndef CURL_DISABLE_PROXY + && (!isproxy || !conn->bits.tunnel_proxy) +#endif + ) { + CFArrayAppendValue(alpnArr, CFSTR(ALPN_H2)); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); } +#endif + + CFArrayAppendValue(alpnArr, CFSTR(ALPN_HTTP_1_1)); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); + + /* expects length prefixed preference ordered list of protocols in wire + * format + */ err = SSLSetALPNProtocols(backend->ssl_ctx, alpnArr); if(err != noErr) infof(data, "WARNING: failed to set ALPN protocols; OSStatus %d", err); CFRelease(alpnArr); - Curl_alpn_to_proto_str(&proto, connssl->alpn); - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); } } #endif - if(ssl_config->key) { + if(SSL_SET_OPTION(key)) { infof(data, "WARNING: SSL: CURLOPT_SSLKEY is ignored by Secure " "Transport. The private key must be in the Keychain."); } @@ -1855,17 +1891,17 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, else err = !noErr; if((err != noErr) && (is_cert_file || is_cert_data)) { - if(!ssl_config->cert_type) + if(!SSL_SET_OPTION(cert_type)) infof(data, "SSL: Certificate type not set, assuming " "PKCS#12 format."); - else if(!strcasecompare(ssl_config->cert_type, "P12")) { + else if(!strcasecompare(SSL_SET_OPTION(cert_type), "P12")) { failf(data, "SSL: The Security framework only supports " "loading identities that are in PKCS#12 format."); return CURLE_SSL_CERTPROBLEM; } err = CopyIdentityFromPKCS12File(ssl_cert, ssl_cert_blob, - ssl_config->key_passwd, + SSL_SET_OPTION(key_passwd), &cert_and_key); } @@ -1958,7 +1994,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, #else if(SSLSetSessionOption) { #endif /* CURL_BUILD_MAC */ - bool break_on_auth = !conn_config->verifypeer || + bool break_on_auth = !conn->ssl_config.verifypeer || ssl_cafile || ssl_cablob; err = SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionBreakOnServerAuth, @@ -1971,7 +2007,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, else { #if CURL_SUPPORT_MAC_10_8 err = SSLSetEnableCertVerify(backend->ssl_ctx, - conn_config->verifypeer?true:false); + conn->ssl_config.verifypeer?true:false); if(err != noErr) { failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err); return CURLE_SSL_CONNECT_ERROR; @@ -1980,7 +2016,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, } #else err = SSLSetEnableCertVerify(backend->ssl_ctx, - conn_config->verifypeer?true:false); + conn->ssl_config.verifypeer?true:false); if(err != noErr) { failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err); return CURLE_SSL_CONNECT_ERROR; @@ -2001,9 +2037,9 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, /* Configure hostname check. SNI is used if available. * Both hostname check and SNI require SSLSetPeerDomainName(). * Also: the verifyhost setting influences SNI usage */ - if(conn_config->verifyhost) { + if(conn->ssl_config.verifyhost) { size_t snilen; - char *snihost = Curl_ssl_snihost(data, connssl->hostname, &snilen); + char *snihost = Curl_ssl_snihost(data, hostname, &snilen); if(!snihost) { failf(data, "Failed to set SNI"); return CURLE_SSL_CONNECT_ERROR; @@ -2016,9 +2052,9 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } - if((Curl_inet_pton(AF_INET, connssl->hostname, &addr)) + if((Curl_inet_pton(AF_INET, hostname, &addr)) #ifdef ENABLE_IPV6 - || (Curl_inet_pton(AF_INET6, connssl->hostname, &addr)) + || (Curl_inet_pton(AF_INET6, hostname, &addr)) #endif ) { infof(data, "WARNING: using IP address, SNI is being disabled by " @@ -2029,7 +2065,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, infof(data, "WARNING: disabling hostname validation also disables SNI."); } - ciphers = conn_config->cipher_list; + ciphers = SSL_CONN_CONFIG(cipher_list); if(ciphers) { err = sectransp_set_selected_ciphers(data, backend->ssl_ctx, ciphers); } @@ -2047,20 +2083,20 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, specifically doesn't want us doing that: */ if(SSLSetSessionOption) { SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionSendOneByteRecord, - !ssl_config->enable_beast); + !SSL_SET_OPTION(enable_beast)); SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionFalseStart, - ssl_config->falsestart); /* false start support */ + data->set.ssl.falsestart); /* false start support */ } #endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */ /* Check if there's a cached ID we can/should use here! */ - if(ssl_config->primary.sessionid) { + if(SSL_SET_OPTION(primary.sessionid)) { char *ssl_sessionid; size_t ssl_sessionid_len; Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, (void **)&ssl_sessionid, - &ssl_sessionid_len)) { + if(!Curl_ssl_getsessionid(data, conn, isproxy, (void **)&ssl_sessionid, + &ssl_sessionid_len, sockindex)) { /* we got a session id, use it! */ err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len); Curl_ssl_sessionid_unlock(data); @@ -2076,10 +2112,9 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, else { CURLcode result; ssl_sessionid = - aprintf("%s:%d:%d:%s:%d", + aprintf("%s:%d:%d:%s:%ld", ssl_cafile ? ssl_cafile : "(blob memory)", - verifypeer, conn_config->verifyhost, connssl->hostname, - connssl->port); + verifypeer, SSL_CONN_CONFIG(verifyhost), hostname, port); ssl_sessionid_len = strlen(ssl_sessionid); err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len); @@ -2089,8 +2124,8 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } - result = Curl_ssl_addsessionid(cf, data, ssl_sessionid, - ssl_sessionid_len, NULL); + result = Curl_ssl_addsessionid(data, conn, isproxy, ssl_sessionid, + ssl_sessionid_len, sockindex, NULL); Curl_ssl_sessionid_unlock(data); if(result) { failf(data, "failed to store ssl session"); @@ -2099,13 +2134,18 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, } } - err = SSLSetIOFuncs(backend->ssl_ctx, bio_cf_in_read, bio_cf_out_write); + err = SSLSetIOFuncs(backend->ssl_ctx, SocketRead, SocketWrite); if(err != noErr) { failf(data, "SSL: SSLSetIOFuncs() failed: OSStatus %d", err); return CURLE_SSL_CONNECT_ERROR; } - err = SSLSetConnection(backend->ssl_ctx, cf); + /* pass the raw socket into the SSL layers */ + /* We need to store the FD in a constant memory address, because + * SSLSetConnection() will not copy that address. I've found that + * conn->sock[sockindex] may change on its own. */ + backend->ssl_sockfd = sockfd; + err = SSLSetConnection(backend->ssl_ctx, connssl); if(err != noErr) { failf(data, "SSL: SSLSetConnection() failed: %d", err); return CURLE_SSL_CONNECT_ERROR; @@ -2164,39 +2204,50 @@ static long pem_to_der(const char *in, unsigned char **out, size_t *outlen) return sep_end - in; } -#define MAX_CERTS_SIZE (50*1024*1024) /* arbitrary - to catch mistakes */ - static int read_cert(const char *file, unsigned char **out, size_t *outlen) { int fd; - ssize_t n; - unsigned char buf[512]; - struct dynbuf certs; - - Curl_dyn_init(&certs, MAX_CERTS_SIZE); + ssize_t n, len = 0, cap = 512; + unsigned char buf[512], *data; fd = open(file, 0); if(fd < 0) return -1; + data = malloc(cap); + if(!data) { + close(fd); + return -1; + } + for(;;) { n = read(fd, buf, sizeof(buf)); - if(!n) - break; if(n < 0) { close(fd); - Curl_dyn_free(&certs); + free(data); return -1; } - if(Curl_dyn_addn(&certs, buf, n)) { + else if(n == 0) { close(fd); - return -1; + break; + } + + if(len + n >= cap) { + cap *= 2; + data = Curl_saferealloc(data, cap); + if(!data) { + close(fd); + return -1; + } } + + memcpy(data + len, buf, n); + len += n; } - close(fd); + data[len] = '\0'; - *out = Curl_dyn_uptr(&certs); - *outlen = Curl_dyn_len(&certs); + *out = data; + *outlen = len; return 0; } @@ -2205,18 +2256,16 @@ static int append_cert_to_array(struct Curl_easy *data, const unsigned char *buf, size_t buflen, CFMutableArrayRef array) { + CFDataRef certdata = CFDataCreate(kCFAllocatorDefault, buf, buflen); char *certp; CURLcode result; - SecCertificateRef cacert; - CFDataRef certdata; - - certdata = CFDataCreate(kCFAllocatorDefault, buf, buflen); if(!certdata) { failf(data, "SSL: failed to allocate array for CA certificate"); return CURLE_OUT_OF_MEMORY; } - cacert = SecCertificateCreateWithData(kCFAllocatorDefault, certdata); + SecCertificateRef cacert = + SecCertificateCreateWithData(kCFAllocatorDefault, certdata); CFRelease(certdata); if(!cacert) { failf(data, "SSL: failed to create SecCertificate from CA certificate"); @@ -2242,8 +2291,7 @@ static int append_cert_to_array(struct Curl_easy *data, return CURLE_OK; } -static CURLcode verify_cert_buf(struct Curl_cfilter *cf, - struct Curl_easy *data, +static CURLcode verify_cert_buf(struct Curl_easy *data, const unsigned char *certbuf, size_t buflen, SSLContextRef ctx) { @@ -2251,12 +2299,7 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf, long res; unsigned char *der; size_t derlen, offset = 0; - OSStatus ret; - SecTrustResultType trust_eval; - CFMutableArrayRef array = NULL; - SecTrustRef trust = NULL; - CURLcode result = CURLE_PEER_FAILED_VERIFICATION; - (void)cf; + /* * Certbuf now contains the contents of the certificate file, which can be * - a single DER certificate, @@ -2266,11 +2309,11 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf, * Go through certbuf, and convert any PEM certificate in it into DER * format. */ - array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeArrayCallBacks); if(!array) { failf(data, "SSL: out of memory creating CA certificate array"); - result = CURLE_OUT_OF_MEMORY; - goto out; + return CURLE_OUT_OF_MEMORY; } while(offset < buflen) { @@ -2282,10 +2325,10 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf, */ res = pem_to_der((const char *)certbuf + offset, &der, &derlen); if(res < 0) { + CFRelease(array); failf(data, "SSL: invalid CA certificate #%d (offset %zu) in bundle", n, offset); - result = CURLE_SSL_CACERT_BADFILE; - goto out; + return CURLE_SSL_CACERT_BADFILE; } offset += res; @@ -2293,9 +2336,8 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf, /* This is not a PEM file, probably a certificate in DER format. */ rc = append_cert_to_array(data, certbuf, buflen, array); if(rc != CURLE_OK) { - DEBUGF(LOG_CF(data, cf, "append_cert for CA failed")); - result = rc; - goto out; + CFRelease(array); + return rc; } break; } @@ -2307,73 +2349,63 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf, rc = append_cert_to_array(data, der, derlen, array); free(der); if(rc != CURLE_OK) { - DEBUGF(LOG_CF(data, cf, "append_cert for CA failed")); - result = rc; - goto out; + CFRelease(array); + return rc; } } - ret = SSLCopyPeerTrust(ctx, &trust); + SecTrustRef trust; + OSStatus ret = SSLCopyPeerTrust(ctx, &trust); if(!trust) { failf(data, "SSL: error getting certificate chain"); - goto out; + CFRelease(array); + return CURLE_PEER_FAILED_VERIFICATION; } else if(ret != noErr) { + CFRelease(array); failf(data, "SSLCopyPeerTrust() returned error %d", ret); - goto out; + return CURLE_PEER_FAILED_VERIFICATION; } - DEBUGF(LOG_CF(data, cf, "setting %d trust anchors", n)); ret = SecTrustSetAnchorCertificates(trust, array); if(ret != noErr) { + CFRelease(array); + CFRelease(trust); failf(data, "SecTrustSetAnchorCertificates() returned error %d", ret); - goto out; + return CURLE_PEER_FAILED_VERIFICATION; } ret = SecTrustSetAnchorCertificatesOnly(trust, true); if(ret != noErr) { + CFRelease(array); + CFRelease(trust); failf(data, "SecTrustSetAnchorCertificatesOnly() returned error %d", ret); - goto out; + return CURLE_PEER_FAILED_VERIFICATION; } - trust_eval = 0; + SecTrustResultType trust_eval = 0; ret = SecTrustEvaluate(trust, &trust_eval); + CFRelease(array); + CFRelease(trust); if(ret != noErr) { failf(data, "SecTrustEvaluate() returned error %d", ret); - goto out; + return CURLE_PEER_FAILED_VERIFICATION; } switch(trust_eval) { case kSecTrustResultUnspecified: - /* what does this really mean? */ - DEBUGF(LOG_CF(data, cf, "trust result: Unspecified")); - result = CURLE_OK; - goto out; case kSecTrustResultProceed: - DEBUGF(LOG_CF(data, cf, "trust result: Proceed")); - result = CURLE_OK; - goto out; + return CURLE_OK; case kSecTrustResultRecoverableTrustFailure: - failf(data, "SSL: peer not verified: RecoverableTrustFailure"); - goto out; case kSecTrustResultDeny: - failf(data, "SSL: peer not verified: Deny"); - goto out; default: - failf(data, "SSL: perr not verified: result=%d", trust_eval); - goto out; + failf(data, "SSL: certificate verification failed (result: %d)", + trust_eval); + return CURLE_PEER_FAILED_VERIFICATION; } - -out: - if(trust) - CFRelease(trust); - if(array) - CFRelease(array); - return result; } -static CURLcode verify_cert(struct Curl_cfilter *cf, - struct Curl_easy *data, const char *cafile, +static CURLcode verify_cert(struct Curl_easy *data, const char *cafile, const struct curl_blob *ca_info_blob, SSLContextRef ctx) { @@ -2382,7 +2414,6 @@ static CURLcode verify_cert(struct Curl_cfilter *cf, size_t buflen; if(ca_info_blob) { - DEBUGF(LOG_CF(data, cf, "verify_peer, CA from config blob")); certbuf = (unsigned char *)malloc(ca_info_blob->len + 1); if(!certbuf) { return CURLE_OUT_OF_MEMORY; @@ -2392,7 +2423,6 @@ static CURLcode verify_cert(struct Curl_cfilter *cf, certbuf[ca_info_blob->len]='\0'; } else if(cafile) { - DEBUGF(LOG_CF(data, cf, "verify_peer, CA from file '%s'", cafile)); if(read_cert(cafile, &certbuf, &buflen) < 0) { failf(data, "SSL: failed to read or invalid CA certificate"); return CURLE_SSL_CACERT_BADFILE; @@ -2401,7 +2431,7 @@ static CURLcode verify_cert(struct Curl_cfilter *cf, else return CURLE_SSL_CACERT_BADFILE; - result = verify_cert_buf(cf, data, certbuf, buflen, ctx); + result = verify_cert_buf(data, certbuf, buflen, ctx); free(certbuf); return result; } @@ -2430,15 +2460,11 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, do { SecTrustRef trust; - OSStatus ret; - SecKeyRef keyRef; - OSStatus success; - - ret = SSLCopyPeerTrust(ctx, &trust); + OSStatus ret = SSLCopyPeerTrust(ctx, &trust); if(ret != noErr || !trust) break; - keyRef = SecTrustCopyPublicKey(trust); + SecKeyRef keyRef = SecTrustCopyPublicKey(trust); CFRelease(trust); if(!keyRef) break; @@ -2452,8 +2478,8 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, #elif SECTRANSP_PINNEDPUBKEY_V2 - success = SecItemExport(keyRef, kSecFormatOpenSSL, 0, NULL, - &publicKeyBits); + OSStatus success = SecItemExport(keyRef, kSecFormatOpenSSL, 0, NULL, + &publicKeyBits); CFRelease(keyRef); if(success != errSecSuccess || !publicKeyBits) break; @@ -2518,25 +2544,23 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, } #endif /* SECTRANSP_PINNEDPUBKEY */ -static CURLcode sectransp_connect_step2(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode +sectransp_connect_step2(struct Curl_easy *data, struct connectdata *conn, + int sockindex) { - struct ssl_connect_data *connssl = cf->ctx; - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; OSStatus err; SSLCipherSuite cipher; SSLProtocol protocol = 0; + const char * const hostname = SSL_HOST_NAME(); DEBUGASSERT(ssl_connect_2 == connssl->connecting_state || ssl_connect_2_reading == connssl->connecting_state || ssl_connect_2_writing == connssl->connecting_state); DEBUGASSERT(backend); - DEBUGF(LOG_CF(data, cf, "connect_step2")); /* Here goes nothing: */ -check_handshake: err = SSLHandshake(backend->ssl_ctx); if(err != noErr) { @@ -2549,16 +2573,16 @@ check_handshake: /* The below is errSSLServerAuthCompleted; it's not defined in Leopard's headers */ case -9841: - if((conn_config->CAfile || conn_config->ca_info_blob) && - conn_config->verifypeer) { - CURLcode result = verify_cert(cf, data, conn_config->CAfile, - conn_config->ca_info_blob, + if((SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(ca_info_blob)) && + SSL_CONN_CONFIG(verifypeer)) { + CURLcode result = verify_cert(data, SSL_CONN_CONFIG(CAfile), + SSL_CONN_CONFIG(ca_info_blob), backend->ssl_ctx); if(result) return result; } /* the documentation says we need to call SSLHandshake() again */ - goto check_handshake; + return sectransp_connect_step2(data, conn, sockindex); /* Problem with encrypt / decrypt */ case errSSLPeerDecodeError: @@ -2660,7 +2684,7 @@ check_handshake: host name: */ case errSSLHostNameMismatch: failf(data, "SSL certificate peer verification failed, the " - "certificate did not match \"%s\"\n", connssl->dispname); + "certificate did not match \"%s\"\n", conn->host.dispname); return CURLE_PEER_FAILED_VERIFICATION; /* Problem with SSL / TLS negotiation */ @@ -2727,7 +2751,7 @@ check_handshake: failf(data, "Peer rejected unexpected message"); break; #if CURL_BUILD_MAC_10_11 || CURL_BUILD_IOS_9 - /* Treating non-fatal error as fatal like before */ + /* Treaing non-fatal error as fatal like before */ case errSSLClientHelloReceived: failf(data, "A non-fatal result for providing a server name " "indication"); @@ -2752,7 +2776,7 @@ check_handshake: default: /* May also return codes listed in Security Framework Result Codes */ failf(data, "Unknown SSL protocol error in connection to %s:%d", - connssl->hostname, err); + hostname, err); break; } return CURLE_SSL_CONNECT_ERROR; @@ -2811,7 +2835,7 @@ check_handshake: } #if(CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1 - if(connssl->alpn) { + if(conn->bits.tls_enable_alpn) { if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) { CFArrayRef alpnArr = NULL; CFStringRef chosenProtocol = NULL; @@ -2823,18 +2847,18 @@ check_handshake: #ifdef USE_HTTP2 if(chosenProtocol && !CFStringCompare(chosenProtocol, CFSTR(ALPN_H2), 0)) { - cf->conn->alpn = CURL_HTTP_VERSION_2; + conn->alpn = CURL_HTTP_VERSION_2; } else #endif if(chosenProtocol && !CFStringCompare(chosenProtocol, CFSTR(ALPN_HTTP_1_1), 0)) { - cf->conn->alpn = CURL_HTTP_VERSION_1_1; + conn->alpn = CURL_HTTP_VERSION_1_1; } else infof(data, VTLS_INFOF_NO_ALPN); - Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ? + Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ? BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); /* chosenProtocol is a reference to the string within alpnArr @@ -2870,12 +2894,11 @@ add_cert_to_certinfo(struct Curl_easy *data, } static CURLcode -collect_server_cert_single(struct Curl_cfilter *cf, struct Curl_easy *data, +collect_server_cert_single(struct Curl_easy *data, SecCertificateRef server_cert, CFIndex idx) { CURLcode result = CURLE_OK; - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); #ifndef CURL_DISABLE_VERBOSE_STRINGS if(data->set.verbose) { char *certp; @@ -2886,26 +2909,26 @@ collect_server_cert_single(struct Curl_cfilter *cf, struct Curl_easy *data, } } #endif - if(ssl_config->certinfo) + if(data->set.ssl.certinfo) result = add_cert_to_certinfo(data, server_cert, (int)idx); return result; } /* This should be called during step3 of the connection at the earliest */ -static CURLcode collect_server_cert(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode +collect_server_cert(struct Curl_easy *data, + struct connectdata *conn, + int sockindex) { #ifndef CURL_DISABLE_VERBOSE_STRINGS const bool show_verbose_server_cert = data->set.verbose; #else const bool show_verbose_server_cert = false; #endif - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - CURLcode result = ssl_config->certinfo ? + CURLcode result = data->set.ssl.certinfo ? CURLE_PEER_FAILED_VERIFICATION : CURLE_OK; - struct ssl_connect_data *connssl = cf->ctx; - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; CFArrayRef server_certs = NULL; SecCertificateRef server_cert; OSStatus err; @@ -2914,7 +2937,7 @@ static CURLcode collect_server_cert(struct Curl_cfilter *cf, DEBUGASSERT(backend); - if(!show_verbose_server_cert && !ssl_config->certinfo) + if(!show_verbose_server_cert && !data->set.ssl.certinfo) return CURLE_OK; if(!backend->ssl_ctx) @@ -2928,11 +2951,11 @@ static CURLcode collect_server_cert(struct Curl_cfilter *cf, a null trust, so be on guard for that: */ if(err == noErr && trust) { count = SecTrustGetCertificateCount(trust); - if(ssl_config->certinfo) + if(data->set.ssl.certinfo) result = Curl_ssl_init_certinfo(data, (int)count); for(i = 0L ; !result && (i < count) ; i++) { server_cert = SecTrustGetCertificateAtIndex(trust, i); - result = collect_server_cert_single(cf, data, server_cert, i); + result = collect_server_cert_single(data, server_cert, i); } CFRelease(trust); } @@ -2950,11 +2973,11 @@ static CURLcode collect_server_cert(struct Curl_cfilter *cf, a null trust, so be on guard for that: */ if(err == noErr && trust) { count = SecTrustGetCertificateCount(trust); - if(ssl_config->certinfo) + if(data->set.ssl.certinfo) result = Curl_ssl_init_certinfo(data, (int)count); for(i = 0L ; !result && (i < count) ; i++) { server_cert = SecTrustGetCertificateAtIndex(trust, i); - result = collect_server_cert_single(cf, data, server_cert, i); + result = collect_server_cert_single(data, server_cert, i); } CFRelease(trust); } @@ -2965,12 +2988,12 @@ static CURLcode collect_server_cert(struct Curl_cfilter *cf, /* Just in case SSLCopyPeerCertificates() returns null too... */ if(err == noErr && server_certs) { count = CFArrayGetCount(server_certs); - if(ssl_config->certinfo) + if(data->set.ssl.certinfo) result = Curl_ssl_init_certinfo(data, (int)count); for(i = 0L ; !result && (i < count) ; i++) { server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs, i); - result = collect_server_cert_single(cf, data, server_cert, i); + result = collect_server_cert_single(data, server_cert, i); } CFRelease(server_certs); } @@ -2982,11 +3005,11 @@ static CURLcode collect_server_cert(struct Curl_cfilter *cf, err = SSLCopyPeerCertificates(backend->ssl_ctx, &server_certs); if(err == noErr) { count = CFArrayGetCount(server_certs); - if(ssl_config->certinfo) + if(data->set.ssl.certinfo) result = Curl_ssl_init_certinfo(data, (int)count); for(i = 0L ; !result && (i < count) ; i++) { server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs, i); - result = collect_server_cert_single(cf, data, server_cert, i); + result = collect_server_cert_single(data, server_cert, i); } CFRelease(server_certs); } @@ -2994,17 +3017,16 @@ static CURLcode collect_server_cert(struct Curl_cfilter *cf, return result; } -static CURLcode sectransp_connect_step3(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode +sectransp_connect_step3(struct Curl_easy *data, struct connectdata *conn, + int sockindex) { - struct ssl_connect_data *connssl = cf->ctx; - CURLcode result; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - DEBUGF(LOG_CF(data, cf, "connect_step3")); /* There is no step 3! * Well, okay, let's collect server certificates, and if verbose mode is on, * let's print the details of the server certificates. */ - result = collect_server_cert(cf, data); + const CURLcode result = collect_server_cert(data, conn, sockindex); if(result) return result; @@ -3012,14 +3034,19 @@ static CURLcode sectransp_connect_step3(struct Curl_cfilter *cf, return CURLE_OK; } +static Curl_recv sectransp_recv; +static Curl_send sectransp_send; + static CURLcode -sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, +sectransp_connect_common(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, bool nonblocking, bool *done) { CURLcode result; - struct ssl_connect_data *connssl = cf->ctx; - curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + curl_socket_t sockfd = conn->sock[sockindex]; int what; /* check if the connection has already been established */ @@ -3038,7 +3065,7 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, return CURLE_OPERATION_TIMEDOUT; } - result = sectransp_connect_step1(cf, data); + result = sectransp_connect_step1(data, conn, sockindex); if(result) return result; } @@ -3092,7 +3119,7 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, * before step2 has completed while ensuring that a client using select() * or epoll() will always have a valid fdset to wait on. */ - result = sectransp_connect_step2(cf, data); + result = sectransp_connect_step2(data, conn, sockindex); if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state || ssl_connect_2_reading == connssl->connecting_state || @@ -3103,14 +3130,15 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, if(ssl_connect_3 == connssl->connecting_state) { - result = sectransp_connect_step3(cf, data); + result = sectransp_connect_step3(data, conn, sockindex); if(result) return result; } if(ssl_connect_done == connssl->connecting_state) { - DEBUGF(LOG_CF(data, cf, "connected")); connssl->state = ssl_connection_complete; + conn->recv[sockindex] = sectransp_recv; + conn->send[sockindex] = sectransp_send; *done = TRUE; } else @@ -3122,20 +3150,20 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, return CURLE_OK; } -static CURLcode sectransp_connect_nonblocking(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) +static CURLcode sectransp_connect_nonblocking(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, bool *done) { - return sectransp_connect_common(cf, data, TRUE, done); + return sectransp_connect_common(data, conn, sockindex, TRUE, done); } -static CURLcode sectransp_connect(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode sectransp_connect(struct Curl_easy *data, + struct connectdata *conn, int sockindex) { CURLcode result; bool done = FALSE; - result = sectransp_connect_common(cf, data, FALSE, &done); + result = sectransp_connect_common(data, conn, sockindex, FALSE, &done); if(result) return result; @@ -3145,18 +3173,17 @@ static CURLcode sectransp_connect(struct Curl_cfilter *cf, return CURLE_OK; } -static void sectransp_close(struct Curl_cfilter *cf, struct Curl_easy *data) +static void sectransp_close(struct Curl_easy *data, struct connectdata *conn, + int sockindex) { - struct ssl_connect_data *connssl = cf->ctx; - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; (void) data; DEBUGASSERT(backend); if(backend->ssl_ctx) { - DEBUGF(LOG_CF(data, cf, "close")); (void)SSLClose(backend->ssl_ctx); #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS if(SSLCreateContext) @@ -3170,20 +3197,19 @@ static void sectransp_close(struct Curl_cfilter *cf, struct Curl_easy *data) #endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ backend->ssl_ctx = NULL; } + backend->ssl_sockfd = 0; } -static int sectransp_shutdown(struct Curl_cfilter *cf, - struct Curl_easy *data) +static int sectransp_shutdown(struct Curl_easy *data, + struct connectdata *conn, int sockindex) { - struct ssl_connect_data *connssl = cf->ctx; - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; ssize_t nread; int what; int rc; char buf[120]; int loop = 10; /* avoid getting stuck */ - CURLcode result; DEBUGASSERT(backend); @@ -3195,14 +3221,12 @@ static int sectransp_shutdown(struct Curl_cfilter *cf, return 0; #endif - sectransp_close(cf, data); + sectransp_close(data, conn, sockindex); rc = 0; - what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), - SSL_SHUTDOWN_TIMEOUT); + what = SOCKET_READABLE(conn->sock[sockindex], SSL_SHUTDOWN_TIMEOUT); - DEBUGF(LOG_CF(data, cf, "shutdown")); while(loop--) { if(what < 0) { /* anything that gets here is fatally bad */ @@ -3219,17 +3243,19 @@ static int sectransp_shutdown(struct Curl_cfilter *cf, /* Something to read, let's do it and hope that it is the close notify alert from the server. No way to SSL_Read now, so use read(). */ - nread = Curl_conn_cf_recv(cf->next, data, buf, sizeof(buf), &result); + nread = read(conn->sock[sockindex], buf, sizeof(buf)); if(nread < 0) { - failf(data, "read: %s", curl_easy_strerror(result)); + char buffer[STRERROR_LEN]; + failf(data, "read: %s", + Curl_strerror(errno, buffer, sizeof(buffer))); rc = -1; } if(nread <= 0) break; - what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), 0); + what = SOCKET_READABLE(conn->sock[sockindex], 0); } return rc; @@ -3251,20 +3277,43 @@ static size_t sectransp_version(char *buffer, size_t size) return msnprintf(buffer, size, "SecureTransport"); } -static bool sectransp_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data) +/* + * This function uses SSLGetSessionState to determine connection status. + * + * Return codes: + * 1 means the connection is still in place + * 0 means the connection has been closed + * -1 means the connection status is unknown + */ +static int sectransp_check_cxn(struct connectdata *conn) +{ + struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET]; + struct ssl_backend_data *backend = connssl->backend; + OSStatus err; + SSLSessionState state; + + DEBUGASSERT(backend); + + if(backend->ssl_ctx) { + err = SSLGetSessionState(backend->ssl_ctx, &state); + if(err == noErr) + return state == kSSLConnected || state == kSSLHandshake; + return -1; + } + return 0; +} + +static bool sectransp_data_pending(const struct connectdata *conn, + int connindex) { - const struct ssl_connect_data *connssl = cf->ctx; - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; + const struct ssl_connect_data *connssl = &conn->ssl[connindex]; + struct ssl_backend_data *backend = connssl->backend; OSStatus err; size_t buffer; - (void)data; DEBUGASSERT(backend); if(backend->ssl_ctx) { /* SSL is in use */ - DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data_pending")); err = SSLGetBufferedReadSize(backend->ssl_ctx, &buffer); if(err == noErr) return buffer > 0UL; @@ -3313,15 +3362,15 @@ static bool sectransp_false_start(void) return FALSE; } -static ssize_t sectransp_send(struct Curl_cfilter *cf, - struct Curl_easy *data, +static ssize_t sectransp_send(struct Curl_easy *data, + int sockindex, const void *mem, size_t len, CURLcode *curlcode) { - struct ssl_connect_data *connssl = cf->ctx; - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; + struct connectdata *conn = data->conn; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; size_t processed = 0UL; OSStatus err; @@ -3382,31 +3431,28 @@ static ssize_t sectransp_send(struct Curl_cfilter *cf, return (ssize_t)processed; } -static ssize_t sectransp_recv(struct Curl_cfilter *cf, - struct Curl_easy *data, +static ssize_t sectransp_recv(struct Curl_easy *data, + int num, char *buf, size_t buffersize, CURLcode *curlcode) { - struct ssl_connect_data *connssl = cf->ctx; - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct connectdata *conn = data->conn; + struct ssl_connect_data *connssl = &conn->ssl[num]; + struct ssl_backend_data *backend = connssl->backend; size_t processed = 0UL; OSStatus err; DEBUGASSERT(backend); -again: - *curlcode = CURLE_OK; + again: err = SSLRead(backend->ssl_ctx, buf, buffersize, &processed); if(err != noErr) { switch(err) { case errSSLWouldBlock: /* return how much we read (if anything) */ - if(processed) { + if(processed) return (ssize_t)processed; - } *curlcode = CURLE_AGAIN; return -1L; break; @@ -3418,21 +3464,19 @@ again: case errSSLClosedGraceful: case errSSLClosedNoNotify: *curlcode = CURLE_OK; - return 0; + return -1L; break; /* The below is errSSLPeerAuthCompleted; it's not defined in Leopard's headers */ case -9841: - if((conn_config->CAfile || conn_config->ca_info_blob) && - conn_config->verifypeer) { - CURLcode result = verify_cert(cf, data, conn_config->CAfile, - conn_config->ca_info_blob, + if((SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(ca_info_blob)) && + SSL_CONN_CONFIG(verifypeer)) { + CURLcode result = verify_cert(data, SSL_CONN_CONFIG(CAfile), + SSL_CONN_CONFIG(ca_info_blob), backend->ssl_ctx); - if(result) { - *curlcode = result; - return -1; - } + if(result) + return result; } goto again; default: @@ -3448,8 +3492,7 @@ again: static void *sectransp_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; + struct ssl_backend_data *backend = connssl->backend; (void)info; DEBUGASSERT(backend); return backend->ssl_ctx; @@ -3461,23 +3504,24 @@ const struct Curl_ssl Curl_ssl_sectransp = { SSLSUPP_CAINFO_BLOB | SSLSUPP_CERTINFO | #ifdef SECTRANSP_PINNEDPUBKEY - SSLSUPP_PINNEDPUBKEY | + SSLSUPP_PINNEDPUBKEY, +#else + 0, #endif /* SECTRANSP_PINNEDPUBKEY */ - SSLSUPP_HTTPS_PROXY, - sizeof(struct st_ssl_backend_data), + sizeof(struct ssl_backend_data), Curl_none_init, /* init */ Curl_none_cleanup, /* cleanup */ sectransp_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ + sectransp_check_cxn, /* check_cxn */ sectransp_shutdown, /* shutdown */ sectransp_data_pending, /* data_pending */ sectransp_random, /* random */ Curl_none_cert_status_request, /* cert_status_request */ sectransp_connect, /* connect */ sectransp_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks, /* getsock */ + Curl_ssl_getsock, /* getsock */ sectransp_get_internals, /* get_internals */ sectransp_close, /* close_one */ Curl_none_close_all, /* close_all */ @@ -3488,10 +3532,7 @@ const struct Curl_ssl Curl_ssl_sectransp = { sectransp_false_start, /* false_start */ sectransp_sha256sum, /* sha256sum */ NULL, /* associate_connection */ - NULL, /* disassociate_connection */ - NULL, /* free_multi_ssl_backend_data */ - sectransp_recv, /* recv decrypted data */ - sectransp_send, /* send data to encrypt */ + NULL /* disassociate_connection */ }; #ifdef __clang__ diff --git a/contrib/libs/curl/lib/vtls/sectransp.h b/contrib/libs/curl/lib/vtls/sectransp.h index 0f1085ad91..2d53b7c480 100644 --- a/contrib/libs/curl/lib/vtls/sectransp.h +++ b/contrib/libs/curl/lib/vtls/sectransp.h @@ -7,8 +7,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Nick Zitzmann, <nickzman@gmail.com>. - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2014, Nick Zitzmann, <nickzman@gmail.com>. + * Copyright (C) 2012 - 2022, 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 diff --git a/contrib/libs/curl/lib/vtls/vtls.c b/contrib/libs/curl/lib/vtls/vtls.c index 510bcfea20..9dee5aa3b3 100644 --- a/contrib/libs/curl/lib/vtls/vtls.c +++ b/contrib/libs/curl/lib/vtls/vtls.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -51,10 +51,8 @@ #endif #include "urldata.h" -#include "cfilters.h" #include "vtls.h" /* generic SSL protos etc */ -#include "vtls_int.h" #include "slist.h" #include "sendf.h" #include "strcase.h" @@ -73,7 +71,6 @@ #include "curl_memory.h" #include "memdebug.h" - /* convenience macro to check if this handle is using a shared SSL session */ #define SSLSESSION_SHARED(data) (data->share && \ (data->share->specifier & \ @@ -130,33 +127,6 @@ static bool blobcmp(struct curl_blob *first, struct curl_blob *second) return !memcmp(first->data, second->data, first->len); /* same data */ } -#ifdef USE_SSL -static const struct alpn_spec ALPN_SPEC_H10 = { - { ALPN_HTTP_1_0 }, 1 -}; -static const struct alpn_spec ALPN_SPEC_H11 = { - { ALPN_HTTP_1_1 }, 1 -}; -#ifdef USE_HTTP2 -static const struct alpn_spec ALPN_SPEC_H2_H11 = { - { ALPN_H2, ALPN_HTTP_1_1 }, 2 -}; -#endif - -static const struct alpn_spec *alpn_get_spec(int httpwant, bool use_alpn) -{ - if(!use_alpn) - return NULL; - if(httpwant == CURL_HTTP_VERSION_1_0) - return &ALPN_SPEC_H10; -#ifdef USE_HTTP2 - if(httpwant >= CURL_HTTP_VERSION_2) - return &ALPN_SPEC_H2_H11; -#endif - return &ALPN_SPEC_H11; -} -#endif /* USE_SSL */ - bool Curl_ssl_config_matches(struct ssl_primary_config *data, @@ -178,12 +148,13 @@ Curl_ssl_config_matches(struct ssl_primary_config *data, #ifdef USE_TLS_SRP !Curl_timestrcmp(data->username, needle->username) && !Curl_timestrcmp(data->password, needle->password) && + (data->authtype == needle->authtype) && #endif - strcasecompare(data->cipher_list, needle->cipher_list) && - strcasecompare(data->cipher_list13, needle->cipher_list13) && - strcasecompare(data->curves, needle->curves) && - strcasecompare(data->CRLfile, needle->CRLfile) && - strcasecompare(data->pinned_key, needle->pinned_key)) + Curl_safe_strcasecompare(data->cipher_list, needle->cipher_list) && + Curl_safe_strcasecompare(data->cipher_list13, needle->cipher_list13) && + Curl_safe_strcasecompare(data->curves, needle->curves) && + Curl_safe_strcasecompare(data->CRLfile, needle->CRLfile) && + Curl_safe_strcasecompare(data->pinned_key, needle->pinned_key)) return TRUE; return FALSE; @@ -200,6 +171,9 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config *source, dest->verifystatus = source->verifystatus; dest->sessionid = source->sessionid; dest->ssl_options = source->ssl_options; +#ifdef USE_TLS_SRP + dest->authtype = source->authtype; +#endif CLONE_BLOB(cert_blob); CLONE_BLOB(ca_info_blob); @@ -296,8 +270,8 @@ void Curl_ssl_cleanup(void) static bool ssl_prefs_check(struct Curl_easy *data) { /* check for CURLOPT_SSLVERSION invalid parameter value */ - const unsigned char sslver = data->set.ssl.primary.version; - if(sslver >= CURL_SSLVERSION_LAST) { + const long sslver = data->set.ssl.primary.version; + if((sslver < 0) || (sslver >= CURL_SSLVERSION_LAST)) { failf(data, "Unrecognized parameter value passed via CURLOPT_SSLVERSION"); return FALSE; } @@ -317,62 +291,89 @@ static bool ssl_prefs_check(struct Curl_easy *data) return TRUE; } -static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data, - const struct alpn_spec *alpn) +#ifndef CURL_DISABLE_PROXY +static CURLcode +ssl_connect_init_proxy(struct connectdata *conn, int sockindex) { - struct ssl_connect_data *ctx; + DEBUGASSERT(conn->bits.proxy_ssl_connected[sockindex]); + if(ssl_connection_complete == conn->ssl[sockindex].state && + !conn->proxy_ssl[sockindex].use) { + struct ssl_backend_data *pbdata; - (void)data; - ctx = calloc(1, sizeof(*ctx)); - if(!ctx) - return NULL; + if(!(Curl_ssl->supports & SSLSUPP_HTTPS_PROXY)) + return CURLE_NOT_BUILT_IN; - ctx->alpn = alpn; - ctx->backend = calloc(1, Curl_ssl->sizeof_ssl_backend_data); - if(!ctx->backend) { - free(ctx); - return NULL; - } - return ctx; -} + /* The pointers to the ssl backend data, which is opaque here, are swapped + rather than move the contents. */ + pbdata = conn->proxy_ssl[sockindex].backend; + conn->proxy_ssl[sockindex] = conn->ssl[sockindex]; -static void cf_ctx_free(struct ssl_connect_data *ctx) -{ - if(ctx) { - free(ctx->backend); - free(ctx); + DEBUGASSERT(pbdata != NULL); + + memset(&conn->ssl[sockindex], 0, sizeof(conn->ssl[sockindex])); + memset(pbdata, 0, Curl_ssl->sizeof_ssl_backend_data); + + conn->ssl[sockindex].backend = pbdata; } + return CURLE_OK; } +#endif -static CURLcode ssl_connect(struct Curl_cfilter *cf, struct Curl_easy *data) +CURLcode +Curl_ssl_connect(struct Curl_easy *data, struct connectdata *conn, + int sockindex) { - struct ssl_connect_data *connssl = cf->ctx; CURLcode result; +#ifndef CURL_DISABLE_PROXY + if(conn->bits.proxy_ssl_connected[sockindex]) { + result = ssl_connect_init_proxy(conn, sockindex); + if(result) + return result; + } +#endif + if(!ssl_prefs_check(data)) return CURLE_SSL_CONNECT_ERROR; /* mark this is being ssl-enabled from here on. */ - connssl->state = ssl_connection_negotiating; + conn->ssl[sockindex].use = TRUE; + conn->ssl[sockindex].state = ssl_connection_negotiating; - result = Curl_ssl->connect_blocking(cf, data); + result = Curl_ssl->connect_blocking(data, conn, sockindex); - if(!result) { - DEBUGASSERT(connssl->state == ssl_connection_complete); - } + if(!result) + Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSL is connected */ + else + conn->ssl[sockindex].use = FALSE; return result; } -static CURLcode -ssl_connect_nonblocking(struct Curl_cfilter *cf, struct Curl_easy *data, - bool *done) +CURLcode +Curl_ssl_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn, + bool isproxy, int sockindex, bool *done) { + CURLcode result; + +#ifndef CURL_DISABLE_PROXY + if(conn->bits.proxy_ssl_connected[sockindex]) { + result = ssl_connect_init_proxy(conn, sockindex); + if(result) + return result; + } +#endif if(!ssl_prefs_check(data)) return CURLE_SSL_CONNECT_ERROR; /* mark this is being ssl requested from here on. */ - return Curl_ssl->connect_nonblocking(cf, data, done); + conn->ssl[sockindex].use = TRUE; + result = Curl_ssl->connect_nonblocking(data, conn, sockindex, done); + if(result) + conn->ssl[sockindex].use = FALSE; + else if(*done && !isproxy) + Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSL is connected */ + return result; } /* @@ -397,26 +398,42 @@ void Curl_ssl_sessionid_unlock(struct Curl_easy *data) * Check if there's a session ID for the given connection in the cache, and if * there's one suitable, it is provided. Returns TRUE when no entry matched. */ -bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, - struct Curl_easy *data, +bool Curl_ssl_getsessionid(struct Curl_easy *data, + struct connectdata *conn, + const bool isProxy, void **ssl_sessionid, - size_t *idsize) /* set 0 if unknown */ + size_t *idsize, /* set 0 if unknown */ + int sockindex) { - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); struct Curl_ssl_session *check; size_t i; long *general_age; bool no_match = TRUE; +#ifndef CURL_DISABLE_PROXY + struct ssl_primary_config * const ssl_config = isProxy ? + &conn->proxy_ssl_config : + &conn->ssl_config; + const char * const name = isProxy ? + conn->http_proxy.host.name : conn->host.name; + int port = isProxy ? (int)conn->port : conn->remote_port; +#else + /* no proxy support */ + struct ssl_primary_config * const ssl_config = &conn->ssl_config; + const char * const name = conn->host.name; + int port = conn->remote_port; +#endif + (void)sockindex; *ssl_sessionid = NULL; - if(!ssl_config) + +#ifdef CURL_DISABLE_PROXY + if(isProxy) return TRUE; +#endif - DEBUGASSERT(ssl_config->primary.sessionid); + DEBUGASSERT(SSL_SET_OPTION(primary.sessionid)); - if(!ssl_config->primary.sessionid || !data->state.session) + if(!SSL_SET_OPTION(primary.sessionid) || !data->state.session) /* session ID re-use is disabled or the session cache has not been setup */ return TRUE; @@ -432,16 +449,16 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, if(!check->sessionid) /* not session ID means blank entry */ continue; - if(strcasecompare(connssl->hostname, check->name) && - ((!cf->conn->bits.conn_to_host && !check->conn_to_host) || - (cf->conn->bits.conn_to_host && check->conn_to_host && - strcasecompare(cf->conn->conn_to_host.name, check->conn_to_host))) && - ((!cf->conn->bits.conn_to_port && check->conn_to_port == -1) || - (cf->conn->bits.conn_to_port && check->conn_to_port != -1 && - cf->conn->conn_to_port == check->conn_to_port)) && - (connssl->port == check->remote_port) && - strcasecompare(cf->conn->handler->scheme, check->scheme) && - Curl_ssl_config_matches(conn_config, &check->ssl_config)) { + if(strcasecompare(name, check->name) && + ((!conn->bits.conn_to_host && !check->conn_to_host) || + (conn->bits.conn_to_host && check->conn_to_host && + strcasecompare(conn->conn_to_host.name, check->conn_to_host))) && + ((!conn->bits.conn_to_port && check->conn_to_port == -1) || + (conn->bits.conn_to_port && check->conn_to_port != -1 && + conn->conn_to_port == check->conn_to_port)) && + (port == check->remote_port) && + strcasecompare(conn->handler->scheme, check->scheme) && + Curl_ssl_config_matches(ssl_config, &check->ssl_config)) { /* yes, we have a session ID! */ (*general_age)++; /* increase general age */ check->age = *general_age; /* set this as used in this age */ @@ -455,8 +472,8 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, DEBUGF(infof(data, "%s Session ID in cache for %s %s://%s:%d", no_match? "Didn't find": "Found", - Curl_ssl_cf_is_proxy(cf) ? "proxy" : "host", - cf->conn->handler->scheme, connssl->hostname, connssl->port)); + isProxy ? "proxy" : "host", + conn->handler->scheme, name, port)); return no_match; } @@ -504,15 +521,14 @@ void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid) * layer. Curl_XXXX_session_free() will be called to free/kill the session ID * later on. */ -CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, - struct Curl_easy *data, +CURLcode Curl_ssl_addsessionid(struct Curl_easy *data, + struct connectdata *conn, + const bool isProxy, void *ssl_sessionid, size_t idsize, + int sockindex, bool *added) { - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); size_t i; struct Curl_ssl_session *store; long oldest_age; @@ -520,6 +536,17 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, char *clone_conn_to_host; int conn_to_port; long *general_age; +#ifndef CURL_DISABLE_PROXY + struct ssl_primary_config * const ssl_config = isProxy ? + &conn->proxy_ssl_config : + &conn->ssl_config; + const char *hostname = isProxy ? conn->http_proxy.host.name : + conn->host.name; +#else + struct ssl_primary_config * const ssl_config = &conn->ssl_config; + const char *hostname = conn->host.name; +#endif + (void)sockindex; if(added) *added = FALSE; @@ -529,15 +556,14 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, store = &data->state.session[0]; oldest_age = data->state.session[0].age; /* zero if unused */ - (void)ssl_config; - DEBUGASSERT(ssl_config->primary.sessionid); + DEBUGASSERT(SSL_SET_OPTION(primary.sessionid)); - clone_host = strdup(connssl->hostname); + clone_host = strdup(hostname); if(!clone_host) return CURLE_OUT_OF_MEMORY; /* bail out */ - if(cf->conn->bits.conn_to_host) { - clone_conn_to_host = strdup(cf->conn->conn_to_host.name); + if(conn->bits.conn_to_host) { + clone_conn_to_host = strdup(conn->conn_to_host.name); if(!clone_conn_to_host) { free(clone_host); return CURLE_OUT_OF_MEMORY; /* bail out */ @@ -546,8 +572,8 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, else clone_conn_to_host = NULL; - if(cf->conn->bits.conn_to_port) - conn_to_port = cf->conn->conn_to_port; + if(conn->bits.conn_to_port) + conn_to_port = conn->conn_to_port; else conn_to_port = -1; @@ -587,10 +613,10 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, store->conn_to_host = clone_conn_to_host; /* clone connect to host name */ store->conn_to_port = conn_to_port; /* connect to port number */ /* port number */ - store->remote_port = connssl->port; - store->scheme = cf->conn->handler->scheme; + store->remote_port = isProxy ? (int)conn->port : conn->remote_port; + store->scheme = conn->handler->scheme; - if(!Curl_clone_primary_ssl_config(conn_config, &store->ssl_config)) { + if(!Curl_clone_primary_ssl_config(ssl_config, &store->ssl_config)) { Curl_free_primary_ssl_config(&store->ssl_config); store->sessionid = NULL; /* let caller free sessionid */ free(clone_host); @@ -603,14 +629,30 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, DEBUGF(infof(data, "Added Session ID to cache for %s://%s:%d [%s]", store->scheme, store->name, store->remote_port, - Curl_ssl_cf_is_proxy(cf) ? "PROXY" : "server")); + isProxy ? "PROXY" : "server")); return CURLE_OK; } -void Curl_free_multi_ssl_backend_data(struct multi_ssl_backend_data *mbackend) +void Curl_ssl_associate_conn(struct Curl_easy *data, + struct connectdata *conn) { - if(Curl_ssl->free_multi_ssl_backend_data && mbackend) - Curl_ssl->free_multi_ssl_backend_data(mbackend); + if(Curl_ssl->associate_connection) { + Curl_ssl->associate_connection(data, conn, FIRSTSOCKET); + if((conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) && + conn->bits.sock_accepted) + Curl_ssl->associate_connection(data, conn, SECONDARYSOCKET); + } +} + +void Curl_ssl_detach_conn(struct Curl_easy *data, + struct connectdata *conn) +{ + if(Curl_ssl->disassociate_connection) { + Curl_ssl->disassociate_connection(data, FIRSTSOCKET); + if((conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) && + conn->bits.sock_accepted) + Curl_ssl->disassociate_connection(data, SECONDARYSOCKET); + } } void Curl_ssl_close_all(struct Curl_easy *data) @@ -629,27 +671,47 @@ void Curl_ssl_close_all(struct Curl_easy *data) Curl_ssl->close_all(data); } -int Curl_ssl_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) +int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks) { - struct ssl_connect_data *connssl = cf->ctx; - curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data); + struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET]; - if(sock != CURL_SOCKET_BAD) { - if(connssl->connecting_state == ssl_connect_2_writing) { - /* write mode */ - socks[0] = sock; - return GETSOCK_WRITESOCK(0); - } - if(connssl->connecting_state == ssl_connect_2_reading) { - /* read mode */ - socks[0] = sock; - return GETSOCK_READSOCK(0); - } + if(connssl->connecting_state == ssl_connect_2_writing) { + /* write mode */ + socks[0] = conn->sock[FIRSTSOCKET]; + return GETSOCK_WRITESOCK(0); + } + if(connssl->connecting_state == ssl_connect_2_reading) { + /* read mode */ + socks[0] = conn->sock[FIRSTSOCKET]; + return GETSOCK_READSOCK(0); } + return GETSOCK_BLANK; } +void Curl_ssl_close(struct Curl_easy *data, struct connectdata *conn, + int sockindex) +{ + DEBUGASSERT((sockindex <= 1) && (sockindex >= -1)); + Curl_ssl->close_one(data, conn, sockindex); + conn->ssl[sockindex].state = ssl_connection_none; +} + +CURLcode Curl_ssl_shutdown(struct Curl_easy *data, struct connectdata *conn, + int sockindex) +{ + if(Curl_ssl->shut_down(data, conn, sockindex)) + return CURLE_SSL_SHUTDOWN_FAILED; + + conn->ssl[sockindex].use = FALSE; /* get back to ordinary socket usage */ + conn->ssl[sockindex].state = ssl_connection_none; + + conn->recv[sockindex] = Curl_recv_plain; + conn->send[sockindex] = Curl_send_plain; + + return CURLE_OK; +} + /* Selects an SSL crypto engine */ CURLcode Curl_ssl_set_engine(struct Curl_easy *data, const char *engine) @@ -704,6 +766,25 @@ void Curl_ssl_version(char *buffer, size_t size) #endif } +/* + * This function tries to determine connection status. + * + * Return codes: + * 1 means the connection is still in place + * 0 means the connection has been closed + * -1 means the connection status is unknown + */ +int Curl_ssl_check_cxn(struct connectdata *conn) +{ + return Curl_ssl->check_cxn(conn); +} + +bool Curl_ssl_data_pending(const struct connectdata *conn, + int connindex) +{ + return Curl_ssl->data_pending(conn, connindex); +} + void Curl_ssl_free_certinfo(struct Curl_easy *data) { struct curl_certinfo *ci = &data->info.certs; @@ -781,6 +862,20 @@ CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data, return result; } +/* + * This is a convenience function for push_certinfo_len that takes a zero + * terminated value. + */ +CURLcode Curl_ssl_push_certinfo(struct Curl_easy *data, + int certnum, + const char *label, + const char *value) +{ + size_t valuelen = strlen(value); + + return Curl_ssl_push_certinfo_len(data, certnum, label, value, valuelen); +} + CURLcode Curl_ssl_random(struct Curl_easy *data, unsigned char *entropy, size_t length) @@ -893,8 +988,8 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, /* only do this if pinnedpubkey starts with "sha256//", length 8 */ if(strncmp(pinnedpubkey, "sha256//", 8) == 0) { CURLcode encode; - size_t encodedlen = 0, pinkeylen; - char *encoded = NULL, *pinkeycopy, *begin_pos, *end_pos; + size_t encodedlen, pinkeylen; + char *encoded, *pinkeycopy, *begin_pos, *end_pos; unsigned char *sha256sumdigest; if(!Curl_ssl->sha256sum) { @@ -907,12 +1002,14 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, if(!sha256sumdigest) return CURLE_OUT_OF_MEMORY; encode = Curl_ssl->sha256sum(pubkey, pubkeylen, - sha256sumdigest, CURL_SHA256_DIGEST_LENGTH); + sha256sumdigest, CURL_SHA256_DIGEST_LENGTH); + + if(encode != CURLE_OK) + return encode; - if(!encode) - encode = Curl_base64_encode((char *)sha256sumdigest, - CURL_SHA256_DIGEST_LENGTH, &encoded, - &encodedlen); + encode = Curl_base64_encode((char *)sha256sumdigest, + CURL_SHA256_DIGEST_LENGTH, &encoded, + &encodedlen); Curl_safefree(sha256sumdigest); if(encode) @@ -1041,13 +1138,20 @@ bool Curl_ssl_cert_status_request(void) /* * Check whether the SSL backend supports false start. */ -bool Curl_ssl_false_start(struct Curl_easy *data) +bool Curl_ssl_false_start(void) { - (void)data; return Curl_ssl->false_start(); } /* + * Check whether the SSL backend supports setting TLS 1.3 cipher suites + */ +bool Curl_ssl_tls13_ciphersuites(void) +{ + return Curl_ssl->supports & SSLSUPP_TLS13_CIPHERSUITES; +} + +/* * Default implementations for unsupported functions. */ @@ -1059,18 +1163,19 @@ int Curl_none_init(void) void Curl_none_cleanup(void) { } -int Curl_none_shutdown(struct Curl_cfilter *cf UNUSED_PARAM, - struct Curl_easy *data UNUSED_PARAM) +int Curl_none_shutdown(struct Curl_easy *data UNUSED_PARAM, + struct connectdata *conn UNUSED_PARAM, + int sockindex UNUSED_PARAM) { (void)data; - (void)cf; + (void)conn; + (void)sockindex; return 0; } -int Curl_none_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data) +int Curl_none_check_cxn(struct connectdata *conn UNUSED_PARAM) { - (void)cf; - (void)data; + (void)conn; return -1; } @@ -1094,11 +1199,11 @@ void Curl_none_session_free(void *ptr UNUSED_PARAM) (void)ptr; } -bool Curl_none_data_pending(struct Curl_cfilter *cf UNUSED_PARAM, - const struct Curl_easy *data UNUSED_PARAM) +bool Curl_none_data_pending(const struct connectdata *conn UNUSED_PARAM, + int connindex UNUSED_PARAM) { - (void)cf; - (void)data; + (void)conn; + (void)connindex; return 0; } @@ -1139,30 +1244,28 @@ static int multissl_init(void) return Curl_ssl->init(); } -static CURLcode multissl_connect(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode multissl_connect(struct Curl_easy *data, + struct connectdata *conn, int sockindex) { if(multissl_setup(NULL)) return CURLE_FAILED_INIT; - return Curl_ssl->connect_blocking(cf, data); + return Curl_ssl->connect_blocking(data, conn, sockindex); } -static CURLcode multissl_connect_nonblocking(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) +static CURLcode multissl_connect_nonblocking(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, bool *done) { if(multissl_setup(NULL)) return CURLE_FAILED_INIT; - return Curl_ssl->connect_nonblocking(cf, data, done); + return Curl_ssl->connect_nonblocking(data, conn, sockindex, done); } -static int multissl_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) +static int multissl_getsock(struct connectdata *conn, curl_socket_t *socks) { if(multissl_setup(NULL)) return 0; - return Curl_ssl->get_select_socks(cf, data, socks); + return Curl_ssl->getsock(conn, socks); } static void *multissl_get_internals(struct ssl_connect_data *connssl, @@ -1173,30 +1276,12 @@ static void *multissl_get_internals(struct ssl_connect_data *connssl, return Curl_ssl->get_internals(connssl, info); } -static void multissl_close(struct Curl_cfilter *cf, struct Curl_easy *data) +static void multissl_close(struct Curl_easy *data, struct connectdata *conn, + int sockindex) { if(multissl_setup(NULL)) return; - Curl_ssl->close(cf, data); -} - -static ssize_t multissl_recv_plain(struct Curl_cfilter *cf, - struct Curl_easy *data, - char *buf, size_t len, CURLcode *code) -{ - if(multissl_setup(NULL)) - return CURLE_FAILED_INIT; - return Curl_ssl->recv_plain(cf, data, buf, len, code); -} - -static ssize_t multissl_send_plain(struct Curl_cfilter *cf, - struct Curl_easy *data, - const void *mem, size_t len, - CURLcode *code) -{ - if(multissl_setup(NULL)) - return CURLE_FAILED_INIT; - return Curl_ssl->send_plain(cf, data, mem, len, code); + Curl_ssl->close_one(data, conn, sockindex); } static const struct Curl_ssl Curl_ssl_multi = { @@ -1214,7 +1299,7 @@ static const struct Curl_ssl Curl_ssl_multi = { Curl_none_cert_status_request, /* cert_status_request */ multissl_connect, /* connect */ multissl_connect_nonblocking, /* connect_nonblocking */ - multissl_get_select_socks, /* getsock */ + multissl_getsock, /* getsock */ multissl_get_internals, /* get_internals */ multissl_close, /* close_one */ Curl_none_close_all, /* close_all */ @@ -1225,10 +1310,7 @@ static const struct Curl_ssl Curl_ssl_multi = { Curl_none_false_start, /* false_start */ NULL, /* sha256sum */ NULL, /* associate_connection */ - NULL, /* disassociate_connection */ - NULL, /* free_multi_ssl_backend_data */ - multissl_recv_plain, /* recv decrypted data */ - multissl_send_plain, /* send data to encrypt */ + NULL /* disassociate_connection */ }; const struct Curl_ssl *Curl_ssl = @@ -1416,635 +1498,3 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, } #endif /* !USE_SSL */ - -#ifdef USE_SSL - -static void free_hostname(struct ssl_connect_data *connssl) -{ - if(connssl->dispname != connssl->hostname) - free(connssl->dispname); - free(connssl->hostname); - connssl->hostname = connssl->dispname = NULL; -} - -static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - if(connssl) { - Curl_ssl->close(cf, data); - connssl->state = ssl_connection_none; - free_hostname(connssl); - } - cf->connected = FALSE; -} - -static CURLcode reinit_hostname(struct Curl_cfilter *cf) -{ - struct ssl_connect_data *connssl = cf->ctx; - const char *ehostname, *edispname; - int eport; - - /* We need the hostname for SNI negotiation. Once handshaked, this - * remains the SNI hostname for the TLS connection. But when the - * connection is reused, the settings in cf->conn might change. - * So we keep a copy of the hostname we use for SNI. - */ -#ifndef CURL_DISABLE_PROXY - if(Curl_ssl_cf_is_proxy(cf)) { - ehostname = cf->conn->http_proxy.host.name; - edispname = cf->conn->http_proxy.host.dispname; - eport = cf->conn->http_proxy.port; - } - else -#endif - { - ehostname = cf->conn->host.name; - edispname = cf->conn->host.dispname; - eport = cf->conn->remote_port; - } - - /* change if ehostname changed */ - if(ehostname && (!connssl->hostname - || strcmp(ehostname, connssl->hostname))) { - free_hostname(connssl); - connssl->hostname = strdup(ehostname); - if(!connssl->hostname) { - free_hostname(connssl); - return CURLE_OUT_OF_MEMORY; - } - if(!edispname || !strcmp(ehostname, edispname)) - connssl->dispname = connssl->hostname; - else { - connssl->dispname = strdup(edispname); - if(!connssl->dispname) { - free_hostname(connssl); - return CURLE_OUT_OF_MEMORY; - } - } - } - connssl->port = eport; - return CURLE_OK; -} - -static void ssl_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - cf_close(cf, data); - CF_DATA_RESTORE(cf, save); - cf_ctx_free(cf->ctx); - cf->ctx = NULL; -} - -static void ssl_cf_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - cf_close(cf, data); - cf->next->cft->do_close(cf->next, data); - CF_DATA_RESTORE(cf, save); -} - -static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking, bool *done) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct cf_call_data save; - CURLcode result; - - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } - - CF_DATA_SAVE(save, cf, data); - (void)connssl; - DEBUGASSERT(data->conn); - DEBUGASSERT(data->conn == cf->conn); - DEBUGASSERT(connssl); - DEBUGASSERT(cf->conn->host.name); - - result = cf->next->cft->do_connect(cf->next, data, blocking, done); - if(result || !*done) - goto out; - - *done = FALSE; - result = reinit_hostname(cf); - if(result) - goto out; - - if(blocking) { - result = ssl_connect(cf, data); - *done = (result == CURLE_OK); - } - else { - result = ssl_connect_nonblocking(cf, data, done); - } - - if(!result && *done) { - cf->connected = TRUE; - connssl->handshake_done = Curl_now(); - DEBUGASSERT(connssl->state == ssl_connection_complete); - } -out: - CF_DATA_RESTORE(cf, save); - return result; -} - -static bool ssl_cf_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data) -{ - struct cf_call_data save; - bool result; - - CF_DATA_SAVE(save, cf, data); - if(Curl_ssl->data_pending(cf, data)) - result = TRUE; - else - result = cf->next->cft->has_data_pending(cf->next, data); - CF_DATA_RESTORE(cf, save); - return result; -} - -static ssize_t ssl_cf_send(struct Curl_cfilter *cf, - struct Curl_easy *data, const void *buf, size_t len, - CURLcode *err) -{ - struct cf_call_data save; - ssize_t nwritten; - - CF_DATA_SAVE(save, cf, data); - *err = CURLE_OK; - nwritten = Curl_ssl->send_plain(cf, data, buf, len, err); - CF_DATA_RESTORE(cf, save); - return nwritten; -} - -static ssize_t ssl_cf_recv(struct Curl_cfilter *cf, - struct Curl_easy *data, char *buf, size_t len, - CURLcode *err) -{ - struct cf_call_data save; - ssize_t nread; - - CF_DATA_SAVE(save, cf, data); - *err = CURLE_OK; - nread = Curl_ssl->recv_plain(cf, data, buf, len, err); - if(nread > 0) { - DEBUGASSERT((size_t)nread <= len); - } - else if(nread == 0) { - /* eof */ - *err = CURLE_OK; - } - DEBUGF(LOG_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", len, nread, *err)); - CF_DATA_RESTORE(cf, save); - return nread; -} - -static int ssl_cf_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) -{ - struct cf_call_data save; - int result; - - CF_DATA_SAVE(save, cf, data); - result = Curl_ssl->get_select_socks(cf, data, socks); - CF_DATA_RESTORE(cf, save); - return result; -} - -static CURLcode ssl_cf_cntrl(struct Curl_cfilter *cf, - struct Curl_easy *data, - int event, int arg1, void *arg2) -{ - struct cf_call_data save; - - (void)arg1; - (void)arg2; - switch(event) { - case CF_CTRL_DATA_ATTACH: - if(Curl_ssl->attach_data) { - CF_DATA_SAVE(save, cf, data); - Curl_ssl->attach_data(cf, data); - CF_DATA_RESTORE(cf, save); - } - break; - case CF_CTRL_DATA_DETACH: - if(Curl_ssl->detach_data) { - CF_DATA_SAVE(save, cf, data); - Curl_ssl->detach_data(cf, data); - CF_DATA_RESTORE(cf, save); - } - break; - default: - break; - } - return CURLE_OK; -} - -static CURLcode ssl_cf_query(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query, int *pres1, void *pres2) -{ - struct ssl_connect_data *connssl = cf->ctx; - - switch(query) { - case CF_QUERY_TIMER_APPCONNECT: { - struct curltime *when = pres2; - if(cf->connected && !Curl_ssl_cf_is_proxy(cf)) - *when = connssl->handshake_done; - return CURLE_OK; - } - default: - break; - } - return cf->next? - cf->next->cft->query(cf->next, data, query, pres1, pres2) : - CURLE_UNKNOWN_OPTION; -} - -static bool cf_ssl_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data, - bool *input_pending) -{ - struct cf_call_data save; - int result; - /* - * This function tries to determine connection status. - * - * Return codes: - * 1 means the connection is still in place - * 0 means the connection has been closed - * -1 means the connection status is unknown - */ - CF_DATA_SAVE(save, cf, data); - result = Curl_ssl->check_cxn(cf, data); - CF_DATA_RESTORE(cf, save); - if(result > 0) { - *input_pending = TRUE; - return TRUE; - } - if(result == 0) { - *input_pending = FALSE; - return FALSE; - } - /* ssl backend does not know */ - return cf->next? - cf->next->cft->is_alive(cf->next, data, input_pending) : - FALSE; /* pessimistic in absence of data */ -} - -struct Curl_cftype Curl_cft_ssl = { - "SSL", - CF_TYPE_SSL, - CURL_LOG_DEFAULT, - ssl_cf_destroy, - ssl_cf_connect, - ssl_cf_close, - Curl_cf_def_get_host, - ssl_cf_get_select_socks, - ssl_cf_data_pending, - ssl_cf_send, - ssl_cf_recv, - ssl_cf_cntrl, - cf_ssl_is_alive, - Curl_cf_def_conn_keep_alive, - ssl_cf_query, -}; - -struct Curl_cftype Curl_cft_ssl_proxy = { - "SSL-PROXY", - CF_TYPE_SSL, - CURL_LOG_DEFAULT, - ssl_cf_destroy, - ssl_cf_connect, - ssl_cf_close, - Curl_cf_def_get_host, - ssl_cf_get_select_socks, - ssl_cf_data_pending, - ssl_cf_send, - ssl_cf_recv, - ssl_cf_cntrl, - cf_ssl_is_alive, - Curl_cf_def_conn_keep_alive, - Curl_cf_def_query, -}; - -static CURLcode cf_ssl_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn) -{ - struct Curl_cfilter *cf = NULL; - struct ssl_connect_data *ctx; - CURLcode result; - - DEBUGASSERT(data->conn); - - ctx = cf_ctx_new(data, alpn_get_spec(data->state.httpwant, - conn->bits.tls_enable_alpn)); - if(!ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - result = Curl_cf_create(&cf, &Curl_cft_ssl, ctx); - -out: - if(result) - cf_ctx_free(ctx); - *pcf = result? NULL : cf; - return result; -} - -CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data, - struct connectdata *conn, - int sockindex) -{ - struct Curl_cfilter *cf; - CURLcode result; - - result = cf_ssl_create(&cf, data, conn); - if(!result) - Curl_conn_cf_add(data, conn, sockindex, cf); - return result; -} - -CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data) -{ - struct Curl_cfilter *cf; - CURLcode result; - - result = cf_ssl_create(&cf, data, cf_at->conn); - if(!result) - Curl_conn_cf_insert_after(cf_at, cf); - return result; -} - -#ifndef CURL_DISABLE_PROXY - -static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn) -{ - struct Curl_cfilter *cf = NULL; - struct ssl_connect_data *ctx; - CURLcode result; - bool use_alpn = conn->bits.tls_enable_alpn; - int httpwant = CURL_HTTP_VERSION_1_1; - -#ifdef USE_HTTP2 - if(conn->http_proxy.proxytype == CURLPROXY_HTTPS2) { - use_alpn = TRUE; - httpwant = CURL_HTTP_VERSION_2; - } -#endif - - ctx = cf_ctx_new(data, alpn_get_spec(httpwant, use_alpn)); - if(!ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - result = Curl_cf_create(&cf, &Curl_cft_ssl_proxy, ctx); - -out: - if(result) - cf_ctx_free(ctx); - *pcf = result? NULL : cf; - return result; -} - -CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data) -{ - struct Curl_cfilter *cf; - CURLcode result; - - result = cf_ssl_proxy_create(&cf, data, cf_at->conn); - if(!result) - Curl_conn_cf_insert_after(cf_at, cf); - return result; -} - -#endif /* !CURL_DISABLE_PROXY */ - -bool Curl_ssl_supports(struct Curl_easy *data, int option) -{ - (void)data; - return (Curl_ssl->supports & option)? TRUE : FALSE; -} - -void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex, - CURLINFO info, int n) -{ - void *result = NULL; - (void)n; - if(data->conn) { - struct Curl_cfilter *cf; - /* get first filter in chain, if any is present */ - cf = Curl_ssl_cf_get_ssl(data->conn->cfilter[sockindex]); - if(cf) { - struct cf_call_data save; - CF_DATA_SAVE(save, cf, data); - result = Curl_ssl->get_internals(cf->ctx, info); - CF_DATA_RESTORE(cf, save); - } - } - return result; -} - -CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data, - int sockindex) -{ - struct Curl_cfilter *cf, *head; - CURLcode result = CURLE_OK; - - (void)data; - head = data->conn? data->conn->cfilter[sockindex] : NULL; - for(cf = head; cf; cf = cf->next) { - if(cf->cft == &Curl_cft_ssl) { - if(Curl_ssl->shut_down(cf, data)) - result = CURLE_SSL_SHUTDOWN_FAILED; - Curl_conn_cf_discard_sub(head, cf, data, FALSE); - break; - } - } - return result; -} - -static struct Curl_cfilter *get_ssl_cf_engaged(struct connectdata *conn, - int sockindex) -{ - struct Curl_cfilter *cf, *lowest_ssl_cf = NULL; - - for(cf = conn->cfilter[sockindex]; cf; cf = cf->next) { - if(cf->cft == &Curl_cft_ssl || cf->cft == &Curl_cft_ssl_proxy) { - lowest_ssl_cf = cf; - if(cf->connected || (cf->next && cf->next->connected)) { - /* connected or about to start */ - return cf; - } - } - } - return lowest_ssl_cf; -} - -bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf) -{ - return (cf->cft == &Curl_cft_ssl_proxy); -} - -struct ssl_config_data * -Curl_ssl_cf_get_config(struct Curl_cfilter *cf, struct Curl_easy *data) -{ -#ifdef CURL_DISABLE_PROXY - (void)cf; - return &data->set.ssl; -#else - return Curl_ssl_cf_is_proxy(cf)? &data->set.proxy_ssl : &data->set.ssl; -#endif -} - -struct ssl_config_data * -Curl_ssl_get_config(struct Curl_easy *data, int sockindex) -{ - struct Curl_cfilter *cf; - - (void)data; - DEBUGASSERT(data->conn); - cf = get_ssl_cf_engaged(data->conn, sockindex); - return cf? Curl_ssl_cf_get_config(cf, data) : &data->set.ssl; -} - -struct ssl_primary_config * -Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf) -{ -#ifdef CURL_DISABLE_PROXY - return &cf->conn->ssl_config; -#else - return Curl_ssl_cf_is_proxy(cf)? - &cf->conn->proxy_ssl_config : &cf->conn->ssl_config; -#endif -} - -struct Curl_cfilter *Curl_ssl_cf_get_ssl(struct Curl_cfilter *cf) -{ - for(; cf; cf = cf->next) { - if(cf->cft == &Curl_cft_ssl || cf->cft == &Curl_cft_ssl_proxy) - return cf; - } - return NULL; -} - -CURLcode Curl_alpn_to_proto_buf(struct alpn_proto_buf *buf, - const struct alpn_spec *spec) -{ - size_t i, len; - int off = 0; - unsigned char blen; - - memset(buf, 0, sizeof(*buf)); - for(i = 0; spec && i < spec->count; ++i) { - len = strlen(spec->entries[i]); - if(len >= ALPN_NAME_MAX) - return CURLE_FAILED_INIT; - blen = (unsigned char)len; - if(off + blen + 1 >= (int)sizeof(buf->data)) - return CURLE_FAILED_INIT; - buf->data[off++] = blen; - memcpy(buf->data + off, spec->entries[i], blen); - off += blen; - } - buf->len = off; - return CURLE_OK; -} - -CURLcode Curl_alpn_to_proto_str(struct alpn_proto_buf *buf, - const struct alpn_spec *spec) -{ - size_t i, len; - size_t off = 0; - - memset(buf, 0, sizeof(*buf)); - for(i = 0; spec && i < spec->count; ++i) { - len = strlen(spec->entries[i]); - if(len >= ALPN_NAME_MAX) - return CURLE_FAILED_INIT; - if(off + len + 2 >= sizeof(buf->data)) - return CURLE_FAILED_INIT; - if(off) - buf->data[off++] = ','; - memcpy(buf->data + off, spec->entries[i], len); - off += len; - } - buf->data[off] = '\0'; - buf->len = (int)off; - return CURLE_OK; -} - -CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, - struct Curl_easy *data, - const unsigned char *proto, - size_t proto_len) -{ - int can_multi = 0; - unsigned char *palpn = -#ifndef CURL_DISABLE_PROXY - (cf->conn->bits.tunnel_proxy && Curl_ssl_cf_is_proxy(cf))? - &cf->conn->proxy_alpn : &cf->conn->alpn -#else - &cf->conn->alpn -#endif - ; - - if(proto && proto_len) { - if(proto_len == ALPN_HTTP_1_1_LENGTH && - !memcmp(ALPN_HTTP_1_1, proto, ALPN_HTTP_1_1_LENGTH)) { - *palpn = CURL_HTTP_VERSION_1_1; - } - else if(proto_len == ALPN_HTTP_1_0_LENGTH && - !memcmp(ALPN_HTTP_1_0, proto, ALPN_HTTP_1_0_LENGTH)) { - *palpn = CURL_HTTP_VERSION_1_0; - } -#ifdef USE_HTTP2 - else if(proto_len == ALPN_H2_LENGTH && - !memcmp(ALPN_H2, proto, ALPN_H2_LENGTH)) { - *palpn = CURL_HTTP_VERSION_2; - can_multi = 1; - } -#endif -#ifdef USE_HTTP3 - else if(proto_len == ALPN_H3_LENGTH && - !memcmp(ALPN_H3, proto, ALPN_H3_LENGTH)) { - *palpn = CURL_HTTP_VERSION_3; - can_multi = 1; - } -#endif - else { - *palpn = CURL_HTTP_VERSION_NONE; - failf(data, "unsupported ALPN protocol: '%.*s'", (int)proto_len, proto); - /* TODO: do we want to fail this? Previous code just ignored it and - * some vtls backends even ignore the return code of this function. */ - /* return CURLE_NOT_BUILT_IN; */ - goto out; - } - infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, (int)proto_len, proto); - } - else { - *palpn = CURL_HTTP_VERSION_NONE; - infof(data, VTLS_INFOF_NO_ALPN); - } - -out: - if(!Curl_ssl_cf_is_proxy(cf)) - Curl_multiuse_state(data, can_multi? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); - return CURLE_OK; -} - -#endif /* USE_SSL */ diff --git a/contrib/libs/curl/lib/vtls/vtls.h b/contrib/libs/curl/lib/vtls/vtls.h index 3516247301..50c53b3fbd 100644 --- a/contrib/libs/curl/lib/vtls/vtls.h +++ b/contrib/libs/curl/lib/vtls/vtls.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -26,9 +26,7 @@ #include "curl_setup.h" struct connectdata; -struct ssl_config_data; -struct ssl_primary_config; -struct Curl_ssl_session; +struct ssl_connect_data; #define SSLSUPP_CA_PATH (1<<0) /* supports CAPATH */ #define SSLSUPP_CERTINFO (1<<1) /* supports CURLOPT_CERTINFO */ @@ -49,14 +47,98 @@ struct Curl_ssl_session; #define VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR \ ALPN_ACCEPTED "%.*s" -/* Curl_multi SSL backend-specific data; declared differently by each SSL - backend */ -struct multi_ssl_backend_data; -struct Curl_cfilter; +struct Curl_ssl { + /* + * This *must* be the first entry to allow returning the list of available + * backends in curl_global_sslset(). + */ + curl_ssl_backend info; + unsigned int supports; /* bitfield, see above */ + size_t sizeof_ssl_backend_data; + + int (*init)(void); + void (*cleanup)(void); + + size_t (*version)(char *buffer, size_t size); + int (*check_cxn)(struct connectdata *cxn); + int (*shut_down)(struct Curl_easy *data, struct connectdata *conn, + int sockindex); + bool (*data_pending)(const struct connectdata *conn, + int connindex); + + /* return 0 if a find random is filled in */ + CURLcode (*random)(struct Curl_easy *data, unsigned char *entropy, + size_t length); + bool (*cert_status_request)(void); + + CURLcode (*connect_blocking)(struct Curl_easy *data, + struct connectdata *conn, int sockindex); + CURLcode (*connect_nonblocking)(struct Curl_easy *data, + struct connectdata *conn, int sockindex, + bool *done); + + /* If the SSL backend wants to read or write on this connection during a + handshake, set socks[0] to the connection's FIRSTSOCKET, and return + a bitmap indicating read or write with GETSOCK_WRITESOCK(0) or + GETSOCK_READSOCK(0). Otherwise return GETSOCK_BLANK. + Mandatory. */ + int (*getsock)(struct connectdata *conn, curl_socket_t *socks); + + void *(*get_internals)(struct ssl_connect_data *connssl, CURLINFO info); + void (*close_one)(struct Curl_easy *data, struct connectdata *conn, + int sockindex); + void (*close_all)(struct Curl_easy *data); + void (*session_free)(void *ptr); + + CURLcode (*set_engine)(struct Curl_easy *data, const char *engine); + CURLcode (*set_engine_default)(struct Curl_easy *data); + struct curl_slist *(*engines_list)(struct Curl_easy *data); + + bool (*false_start)(void); + CURLcode (*sha256sum)(const unsigned char *input, size_t inputlen, + unsigned char *sha256sum, size_t sha256sumlen); + + bool (*associate_connection)(struct Curl_easy *data, + struct connectdata *conn, + int sockindex); + void (*disassociate_connection)(struct Curl_easy *data, int sockindex); +}; + +#ifdef USE_SSL +extern const struct Curl_ssl *Curl_ssl; +#endif + +int Curl_none_init(void); +void Curl_none_cleanup(void); +int Curl_none_shutdown(struct Curl_easy *data, struct connectdata *conn, + int sockindex); +int Curl_none_check_cxn(struct connectdata *conn); +CURLcode Curl_none_random(struct Curl_easy *data, unsigned char *entropy, + size_t length); +void Curl_none_close_all(struct Curl_easy *data); +void Curl_none_session_free(void *ptr); +bool Curl_none_data_pending(const struct connectdata *conn, int connindex); +bool Curl_none_cert_status_request(void); +CURLcode Curl_none_set_engine(struct Curl_easy *data, const char *engine); +CURLcode Curl_none_set_engine_default(struct Curl_easy *data); +struct curl_slist *Curl_none_engines_list(struct Curl_easy *data); +bool Curl_none_false_start(void); +bool Curl_ssl_tls13_ciphersuites(void); CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, const curl_ssl_backend ***avail); +#include "openssl.h" /* OpenSSL versions */ +#include "gtls.h" /* GnuTLS versions */ +#include "nssg.h" /* NSS versions */ +#include "gskit.h" /* Global Secure ToolKit versions */ +#include "wolfssl.h" /* wolfSSL versions */ +#include "schannel.h" /* Schannel SSPI version */ +#include "sectransp.h" /* SecureTransport (Darwin) version */ +#include "mbedtls.h" /* mbedTLS versions */ +#include "bearssl.h" /* BearSSL versions */ +#include "rustls.h" /* rustls versions */ + #ifndef MAX_PINNED_PUBKEY_SIZE #define MAX_PINNED_PUBKEY_SIZE 1048576 /* 1MB */ #endif @@ -65,21 +147,78 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, #define CURL_SHA256_DIGEST_LENGTH 32 /* fixed size */ #endif +/* see https://www.iana.org/assignments/tls-extensiontype-values/ */ +#define ALPN_HTTP_1_1_LENGTH 8 +#define ALPN_HTTP_1_1 "http/1.1" +#define ALPN_H2_LENGTH 2 +#define ALPN_H2 "h2" + +/* set of helper macros for the backends to access the correct fields. For the + proxy or for the remote host - to properly support HTTPS proxy */ +#ifndef CURL_DISABLE_PROXY +#define SSL_IS_PROXY() \ + (CURLPROXY_HTTPS == conn->http_proxy.proxytype && \ + ssl_connection_complete != \ + conn->proxy_ssl[conn->sock[SECONDARYSOCKET] == \ + CURL_SOCKET_BAD ? FIRSTSOCKET : SECONDARYSOCKET].state) +#define SSL_SET_OPTION(var) \ + (SSL_IS_PROXY() ? data->set.proxy_ssl.var : data->set.ssl.var) +#define SSL_SET_OPTION_LVALUE(var) \ + (*(SSL_IS_PROXY() ? &data->set.proxy_ssl.var : &data->set.ssl.var)) +#define SSL_CONN_CONFIG(var) \ + (SSL_IS_PROXY() ? conn->proxy_ssl_config.var : conn->ssl_config.var) +#define SSL_HOST_NAME() \ + (SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name) +#define SSL_HOST_DISPNAME() \ + (SSL_IS_PROXY() ? conn->http_proxy.host.dispname : conn->host.dispname) +#define SSL_HOST_PORT() \ + (SSL_IS_PROXY() ? conn->port : conn->remote_port) +#define SSL_PINNED_PUB_KEY() (SSL_IS_PROXY() \ + ? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] \ + : data->set.str[STRING_SSL_PINNEDPUBLICKEY]) +#else +#define SSL_IS_PROXY() FALSE +#define SSL_SET_OPTION(var) data->set.ssl.var +#define SSL_SET_OPTION_LVALUE(var) data->set.ssl.var +#define SSL_CONN_CONFIG(var) conn->ssl_config.var +#define SSL_HOST_NAME() conn->host.name +#define SSL_HOST_DISPNAME() conn->host.dispname +#define SSL_HOST_PORT() conn->remote_port +#define SSL_PINNED_PUB_KEY() \ + data->set.str[STRING_SSL_PINNEDPUBLICKEY] +#endif + char *Curl_ssl_snihost(struct Curl_easy *data, const char *host, size_t *olen); bool Curl_ssl_config_matches(struct ssl_primary_config *data, struct ssl_primary_config *needle); bool Curl_clone_primary_ssl_config(struct ssl_primary_config *source, struct ssl_primary_config *dest); void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc); +/* An implementation of the getsock field of Curl_ssl that relies + on the ssl_connect_state enum. Asks for read or write depending + on whether conn->state is ssl_connect_2_reading or + ssl_connect_2_writing. */ +int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks); curl_sslbackend Curl_ssl_backend(void); #ifdef USE_SSL int Curl_ssl_init(void); void Curl_ssl_cleanup(void); +CURLcode Curl_ssl_connect(struct Curl_easy *data, struct connectdata *conn, + int sockindex); +CURLcode Curl_ssl_connect_nonblocking(struct Curl_easy *data, + struct connectdata *conn, + bool isproxy, + int sockindex, + bool *done); /* tell the SSL stuff to close down all open information regarding connections (and thus session ID caching etc) */ void Curl_ssl_close_all(struct Curl_easy *data); +void Curl_ssl_close(struct Curl_easy *data, struct connectdata *conn, + int sockindex); +CURLcode Curl_ssl_shutdown(struct Curl_easy *data, struct connectdata *conn, + int sockindex); CURLcode Curl_ssl_set_engine(struct Curl_easy *data, const char *engine); /* Sets engine as default for all SSL operations */ CURLcode Curl_ssl_set_engine_default(struct Curl_easy *data); @@ -88,6 +227,9 @@ struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data); /* init the SSL session ID cache */ CURLcode Curl_ssl_initsessions(struct Curl_easy *, size_t); void Curl_ssl_version(char *buffer, size_t size); +bool Curl_ssl_data_pending(const struct connectdata *conn, + int connindex); +int Curl_ssl_check_cxn(struct connectdata *conn); /* Certificate information list handling. */ @@ -113,6 +255,30 @@ void Curl_ssl_sessionid_lock(struct Curl_easy *data); /* Unlock session cache mutex */ void Curl_ssl_sessionid_unlock(struct Curl_easy *data); +/* extract a session ID + * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock). + * Caller must make sure that the ownership of returned sessionid object + * is properly taken (e.g. its refcount is incremented + * under sessionid mutex). + */ +bool Curl_ssl_getsessionid(struct Curl_easy *data, + struct connectdata *conn, + const bool isProxy, + void **ssl_sessionid, + size_t *idsize, /* set 0 if unknown */ + int sockindex); +/* add a new session ID + * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock). + * Caller must ensure that it has properly shared ownership of this sessionid + * object with cache (e.g. incrementing refcount on success) + */ +CURLcode Curl_ssl_addsessionid(struct Curl_easy *data, + struct connectdata *conn, + const bool isProxy, + void *ssl_sessionid, + size_t idsize, + int sockindex, + bool *added); /* Kill a single session ID entry in the cache * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock). * This will call engine-specific curlssl_session_free function, which must @@ -138,79 +304,41 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, bool Curl_ssl_cert_status_request(void); -bool Curl_ssl_false_start(struct Curl_easy *data); +bool Curl_ssl_false_start(void); -void Curl_free_multi_ssl_backend_data(struct multi_ssl_backend_data *mbackend); +void Curl_ssl_associate_conn(struct Curl_easy *data, + struct connectdata *conn); +void Curl_ssl_detach_conn(struct Curl_easy *data, + struct connectdata *conn); #define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */ -CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data, - struct connectdata *conn, - int sockindex); - -CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data); - -CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data, - int sockindex); - -#ifndef CURL_DISABLE_PROXY -CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data); -#endif /* !CURL_DISABLE_PROXY */ - -/** - * Get the SSL configuration that is used on the connection. - * This returns NULL if no SSL is configured. - * Otherwise it returns the config of the first (highest) one that is - * either connected, in handshake or about to start - * (e.g. all filters below it are connected). If SSL filters are present, - * but neither can start operating, return the config of the lowest one - * that will first come into effect when connecting. - */ -struct ssl_config_data *Curl_ssl_get_config(struct Curl_easy *data, - int sockindex); - -/** - * True iff the underlying SSL implementation supports the option. - * Option is one of the defined SSLSUPP_* values. - * `data` maybe NULL for the features of the default implementation. - */ -bool Curl_ssl_supports(struct Curl_easy *data, int ssl_option); - -/** - * Get the internal ssl instance (like OpenSSL's SSL*) from the filter - * chain at `sockindex` of type specified by `info`. - * For `n` == 0, the first active (top down) instance is returned. - * 1 gives the second active, etc. - * NULL is returned when no active SSL filter is present. - */ -void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex, - CURLINFO info, int n); - -extern struct Curl_cftype Curl_cft_ssl; -extern struct Curl_cftype Curl_cft_ssl_proxy; - #else /* if not USE_SSL */ /* When SSL support is not present, just define away these function calls */ #define Curl_ssl_init() 1 #define Curl_ssl_cleanup() Curl_nop_stmt +#define Curl_ssl_connect(x,y,z) CURLE_NOT_BUILT_IN #define Curl_ssl_close_all(x) Curl_nop_stmt +#define Curl_ssl_close(x,y,z) Curl_nop_stmt +#define Curl_ssl_shutdown(x,y,z) CURLE_NOT_BUILT_IN #define Curl_ssl_set_engine(x,y) CURLE_NOT_BUILT_IN #define Curl_ssl_set_engine_default(x) CURLE_NOT_BUILT_IN #define Curl_ssl_engines_list(x) NULL +#define Curl_ssl_send(a,b,c,d,e) -1 +#define Curl_ssl_recv(a,b,c,d,e) -1 #define Curl_ssl_initsessions(x,y) CURLE_OK +#define Curl_ssl_data_pending(x,y) 0 +#define Curl_ssl_check_cxn(x) 0 #define Curl_ssl_free_certinfo(x) Curl_nop_stmt +#define Curl_ssl_connect_nonblocking(x,y,z,w,a) CURLE_NOT_BUILT_IN #define Curl_ssl_kill_session(x) Curl_nop_stmt #define Curl_ssl_random(x,y,z) ((void)x, CURLE_NOT_BUILT_IN) #define Curl_ssl_cert_status_request() FALSE -#define Curl_ssl_false_start(a) FALSE -#define Curl_ssl_get_internals(a,b,c,d) NULL -#define Curl_ssl_supports(a,b) FALSE -#define Curl_ssl_cfilter_add(a,b,c) CURLE_NOT_BUILT_IN -#define Curl_ssl_get_config(a,b) NULL -#define Curl_ssl_cfilter_remove(a,b) CURLE_OK +#define Curl_ssl_false_start() FALSE +#define Curl_ssl_tls13_ciphersuites() FALSE +#define Curl_ssl_associate_conn(a,b) Curl_nop_stmt +#define Curl_ssl_detach_conn(a,b) Curl_nop_stmt #endif #endif /* HEADER_CURL_VTLS_H */ diff --git a/contrib/libs/curl/lib/vtls/vtls_int.h b/contrib/libs/curl/lib/vtls/vtls_int.h deleted file mode 100644 index fe0115ca6d..0000000000 --- a/contrib/libs/curl/lib/vtls/vtls_int.h +++ /dev/null @@ -1,231 +0,0 @@ -#ifndef HEADER_CURL_VTLS_INT_H -#define HEADER_CURL_VTLS_INT_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ -#include "curl_setup.h" -#include "cfilters.h" -#include "urldata.h" - -#ifdef USE_SSL - -/* see https://www.iana.org/assignments/tls-extensiontype-values/ */ -#define ALPN_HTTP_1_1_LENGTH 8 -#define ALPN_HTTP_1_1 "http/1.1" -#define ALPN_HTTP_1_0_LENGTH 8 -#define ALPN_HTTP_1_0 "http/1.0" -#define ALPN_H2_LENGTH 2 -#define ALPN_H2 "h2" -#define ALPN_H3_LENGTH 2 -#define ALPN_H3 "h3" - -/* conservative sizes on the ALPN entries and count we are handling, - * we can increase these if we ever feel the need or have to accommodate - * ALPN strings from the "outside". */ -#define ALPN_NAME_MAX 10 -#define ALPN_ENTRIES_MAX 3 -#define ALPN_PROTO_BUF_MAX (ALPN_ENTRIES_MAX * (ALPN_NAME_MAX + 1)) - -struct alpn_spec { - const char entries[ALPN_ENTRIES_MAX][ALPN_NAME_MAX]; - size_t count; /* number of entries */ -}; - -struct alpn_proto_buf { - unsigned char data[ALPN_PROTO_BUF_MAX]; - int len; -}; - -CURLcode Curl_alpn_to_proto_buf(struct alpn_proto_buf *buf, - const struct alpn_spec *spec); -CURLcode Curl_alpn_to_proto_str(struct alpn_proto_buf *buf, - const struct alpn_spec *spec); - -CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, - struct Curl_easy *data, - const unsigned char *proto, - size_t proto_len); - -/* Information in each SSL cfilter context: cf->ctx */ -struct ssl_connect_data { - ssl_connection_state state; - ssl_connect_state connecting_state; - char *hostname; /* hostname for verification */ - char *dispname; /* display version of hostname */ - const struct alpn_spec *alpn; /* ALPN to use or NULL for none */ - void *backend; /* vtls backend specific props */ - struct cf_call_data call_data; /* data handle used in current call */ - struct curltime handshake_done; /* time when handshake finished */ - int port; /* remote port at origin */ - BIT(use_alpn); /* if ALPN shall be used in handshake */ -}; - - -#undef CF_CTX_CALL_DATA -#define CF_CTX_CALL_DATA(cf) \ - ((struct ssl_connect_data *)(cf)->ctx)->call_data - - -/* Definitions for SSL Implementations */ - -struct Curl_ssl { - /* - * This *must* be the first entry to allow returning the list of available - * backends in curl_global_sslset(). - */ - curl_ssl_backend info; - unsigned int supports; /* bitfield, see above */ - size_t sizeof_ssl_backend_data; - - int (*init)(void); - void (*cleanup)(void); - - size_t (*version)(char *buffer, size_t size); - int (*check_cxn)(struct Curl_cfilter *cf, struct Curl_easy *data); - int (*shut_down)(struct Curl_cfilter *cf, - struct Curl_easy *data); - bool (*data_pending)(struct Curl_cfilter *cf, - const struct Curl_easy *data); - - /* return 0 if a find random is filled in */ - CURLcode (*random)(struct Curl_easy *data, unsigned char *entropy, - size_t length); - bool (*cert_status_request)(void); - - CURLcode (*connect_blocking)(struct Curl_cfilter *cf, - struct Curl_easy *data); - CURLcode (*connect_nonblocking)(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done); - - /* If the SSL backend wants to read or write on this connection during a - handshake, set socks[0] to the connection's FIRSTSOCKET, and return - a bitmap indicating read or write with GETSOCK_WRITESOCK(0) or - GETSOCK_READSOCK(0). Otherwise return GETSOCK_BLANK. - Mandatory. */ - int (*get_select_socks)(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks); - - void *(*get_internals)(struct ssl_connect_data *connssl, CURLINFO info); - void (*close)(struct Curl_cfilter *cf, struct Curl_easy *data); - void (*close_all)(struct Curl_easy *data); - void (*session_free)(void *ptr); - - CURLcode (*set_engine)(struct Curl_easy *data, const char *engine); - CURLcode (*set_engine_default)(struct Curl_easy *data); - struct curl_slist *(*engines_list)(struct Curl_easy *data); - - bool (*false_start)(void); - CURLcode (*sha256sum)(const unsigned char *input, size_t inputlen, - unsigned char *sha256sum, size_t sha256sumlen); - - bool (*attach_data)(struct Curl_cfilter *cf, struct Curl_easy *data); - void (*detach_data)(struct Curl_cfilter *cf, struct Curl_easy *data); - - void (*free_multi_ssl_backend_data)(struct multi_ssl_backend_data *mbackend); - - ssize_t (*recv_plain)(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *code); - ssize_t (*send_plain)(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *mem, size_t len, CURLcode *code); - -}; - -extern const struct Curl_ssl *Curl_ssl; - - -int Curl_none_init(void); -void Curl_none_cleanup(void); -int Curl_none_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data); -int Curl_none_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data); -CURLcode Curl_none_random(struct Curl_easy *data, unsigned char *entropy, - size_t length); -void Curl_none_close_all(struct Curl_easy *data); -void Curl_none_session_free(void *ptr); -bool Curl_none_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data); -bool Curl_none_cert_status_request(void); -CURLcode Curl_none_set_engine(struct Curl_easy *data, const char *engine); -CURLcode Curl_none_set_engine_default(struct Curl_easy *data); -struct curl_slist *Curl_none_engines_list(struct Curl_easy *data); -bool Curl_none_false_start(void); -int Curl_ssl_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks); - -/** - * Get the ssl_config_data in `data` that is relevant for cfilter `cf`. - */ -struct ssl_config_data *Curl_ssl_cf_get_config(struct Curl_cfilter *cf, - struct Curl_easy *data); - -/** - * Get the primary config relevant for the filter from its connection. - */ -struct ssl_primary_config * - Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf); - -/** - * Get the first SSL filter in the chain starting with `cf`, or NULL. - */ -struct Curl_cfilter *Curl_ssl_cf_get_ssl(struct Curl_cfilter *cf); - -/** - * Get the SSL filter below the given one or NULL if there is none. - */ -bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf); - -/* extract a session ID - * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock). - * Caller must make sure that the ownership of returned sessionid object - * is properly taken (e.g. its refcount is incremented - * under sessionid mutex). - */ -bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, - struct Curl_easy *data, - void **ssl_sessionid, - size_t *idsize); /* set 0 if unknown */ -/* add a new session ID - * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock). - * Caller must ensure that it has properly shared ownership of this sessionid - * object with cache (e.g. incrementing refcount on success) - */ -CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, - struct Curl_easy *data, - void *ssl_sessionid, - size_t idsize, - bool *added); - -#include "openssl.h" /* OpenSSL versions */ -#include "gtls.h" /* GnuTLS versions */ -#include "nssg.h" /* NSS versions */ -#include "gskit.h" /* Global Secure ToolKit versions */ -#include "wolfssl.h" /* wolfSSL versions */ -#include "schannel.h" /* Schannel SSPI version */ -#include "sectransp.h" /* SecureTransport (Darwin) version */ -#include "mbedtls.h" /* mbedTLS versions */ -#include "bearssl.h" /* BearSSL versions */ -#include "rustls.h" /* rustls versions */ - -#endif /* USE_SSL */ - -#endif /* HEADER_CURL_VTLS_INT_H */ diff --git a/contrib/libs/curl/lib/vtls/wolfssl.c b/contrib/libs/curl/lib/vtls/wolfssl.c index e2f024fde1..b068639c80 100644 --- a/contrib/libs/curl/lib/vtls/wolfssl.c +++ b/contrib/libs/curl/lib/vtls/wolfssl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -55,7 +55,6 @@ #include "sendf.h" #include "inet_pton.h" #include "vtls.h" -#include "vtls_int.h" #include "keylog.h" #include "parsedate.h" #include "connect.h" /* for the connect timeout */ @@ -85,18 +84,14 @@ #endif #endif -#if defined(HAVE_WOLFSSL_FULL_BIO) && HAVE_WOLFSSL_FULL_BIO -#define USE_BIO_CHAIN -#else -#undef USE_BIO_CHAIN -#endif - -struct wolfssl_ssl_backend_data { +struct ssl_backend_data { SSL_CTX* ctx; SSL* handle; - CURLcode io_result; /* result of last BIO cfilter operation */ }; +static Curl_recv wolfssl_recv; +static Curl_send wolfssl_send; + #ifdef OPENSSL_EXTRA /* * Availability note: @@ -219,149 +214,46 @@ static const struct group_name_map gnm[] = { { WOLFSSL_KYBER_LEVEL1, "KYBER_LEVEL1" }, { WOLFSSL_KYBER_LEVEL3, "KYBER_LEVEL3" }, { WOLFSSL_KYBER_LEVEL5, "KYBER_LEVEL5" }, + { WOLFSSL_NTRU_HPS_LEVEL1, "NTRU_HPS_LEVEL1" }, + { WOLFSSL_NTRU_HPS_LEVEL3, "NTRU_HPS_LEVEL3" }, + { WOLFSSL_NTRU_HPS_LEVEL5, "NTRU_HPS_LEVEL5" }, + { WOLFSSL_NTRU_HRSS_LEVEL3, "NTRU_HRSS_LEVEL3" }, + { WOLFSSL_SABER_LEVEL1, "SABER_LEVEL1" }, + { WOLFSSL_SABER_LEVEL3, "SABER_LEVEL3" }, + { WOLFSSL_SABER_LEVEL5, "SABER_LEVEL5" }, + { WOLFSSL_KYBER_90S_LEVEL1, "KYBER_90S_LEVEL1" }, + { WOLFSSL_KYBER_90S_LEVEL3, "KYBER_90S_LEVEL3" }, + { WOLFSSL_KYBER_90S_LEVEL5, "KYBER_90S_LEVEL5" }, + { WOLFSSL_P256_NTRU_HPS_LEVEL1, "P256_NTRU_HPS_LEVEL1" }, + { WOLFSSL_P384_NTRU_HPS_LEVEL3, "P384_NTRU_HPS_LEVEL3" }, + { WOLFSSL_P521_NTRU_HPS_LEVEL5, "P521_NTRU_HPS_LEVEL5" }, + { WOLFSSL_P384_NTRU_HRSS_LEVEL3, "P384_NTRU_HRSS_LEVEL3" }, + { WOLFSSL_P256_SABER_LEVEL1, "P256_SABER_LEVEL1" }, + { WOLFSSL_P384_SABER_LEVEL3, "P384_SABER_LEVEL3" }, + { WOLFSSL_P521_SABER_LEVEL5, "P521_SABER_LEVEL5" }, { WOLFSSL_P256_KYBER_LEVEL1, "P256_KYBER_LEVEL1" }, { WOLFSSL_P384_KYBER_LEVEL3, "P384_KYBER_LEVEL3" }, { WOLFSSL_P521_KYBER_LEVEL5, "P521_KYBER_LEVEL5" }, + { WOLFSSL_P256_KYBER_90S_LEVEL1, "P256_KYBER_90S_LEVEL1" }, + { WOLFSSL_P384_KYBER_90S_LEVEL3, "P384_KYBER_90S_LEVEL3" }, + { WOLFSSL_P521_KYBER_90S_LEVEL5, "P521_KYBER_90S_LEVEL5" }, { 0, NULL } }; #endif -#ifdef USE_BIO_CHAIN - -static int bio_cf_create(WOLFSSL_BIO *bio) -{ - wolfSSL_BIO_set_shutdown(bio, 1); - wolfSSL_BIO_set_init(bio, 1); - wolfSSL_BIO_set_data(bio, NULL); - return 1; -} - -static int bio_cf_destroy(WOLFSSL_BIO *bio) -{ - if(!bio) - return 0; - return 1; -} - -static long bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr) -{ - struct Curl_cfilter *cf = BIO_get_data(bio); - long ret = 1; - - (void)cf; - (void)ptr; - switch(cmd) { - case BIO_CTRL_GET_CLOSE: - ret = (long)wolfSSL_BIO_get_shutdown(bio); - break; - case BIO_CTRL_SET_CLOSE: - wolfSSL_BIO_set_shutdown(bio, (int)num); - break; - case BIO_CTRL_FLUSH: - /* we do no delayed writes, but if we ever would, this - * needs to trigger it. */ - ret = 1; - break; - case BIO_CTRL_DUP: - ret = 1; - break; -#ifdef BIO_CTRL_EOF - case BIO_CTRL_EOF: - /* EOF has been reached on input? */ - return (!cf->next || !cf->next->connected); -#endif - default: - ret = 0; - break; - } - return ret; -} - -static int bio_cf_out_write(WOLFSSL_BIO *bio, const char *buf, int blen) -{ - struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio); - struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nwritten; - CURLcode result = CURLE_OK; - - DEBUGASSERT(data); - nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result); - backend->io_result = result; - DEBUGF(LOG_CF(data, cf, "bio_write(len=%d) -> %zd, %d", - blen, nwritten, result)); - wolfSSL_BIO_clear_retry_flags(bio); - if(nwritten < 0 && CURLE_AGAIN == result) - BIO_set_retry_read(bio); - return (int)nwritten; -} - -static int bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) -{ - struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio); - struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nread; - CURLcode result = CURLE_OK; - - DEBUGASSERT(data); - /* OpenSSL catches this case, so should we. */ - if(!buf) - return 0; - - nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result); - backend->io_result = result; - DEBUGF(LOG_CF(data, cf, "bio_read(len=%d) -> %zd, %d", - blen, nread, result)); - wolfSSL_BIO_clear_retry_flags(bio); - if(nread < 0 && CURLE_AGAIN == result) - BIO_set_retry_read(bio); - return (int)nread; -} - -static WOLFSSL_BIO_METHOD *bio_cf_method = NULL; - -static void bio_cf_init_methods(void) -{ - bio_cf_method = wolfSSL_BIO_meth_new(BIO_TYPE_MEM, "wolfSSL CF BIO"); - wolfSSL_BIO_meth_set_write(bio_cf_method, &bio_cf_out_write); - wolfSSL_BIO_meth_set_read(bio_cf_method, &bio_cf_in_read); - wolfSSL_BIO_meth_set_ctrl(bio_cf_method, &bio_cf_ctrl); - wolfSSL_BIO_meth_set_create(bio_cf_method, &bio_cf_create); - wolfSSL_BIO_meth_set_destroy(bio_cf_method, &bio_cf_destroy); -} - -static void bio_cf_free_methods(void) -{ - wolfSSL_BIO_meth_free(bio_cf_method); -} - -#else /* USE_BIO_CHAIN */ - -#define bio_cf_init_methods() Curl_nop_stmt -#define bio_cf_free_methods() Curl_nop_stmt - -#endif /* !USE_BIO_CHAIN */ - /* * This function loads all the client/CA certificates and CRLs. Setup the TLS * layer and do all necessary magic. */ static CURLcode -wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) +wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn, + int sockindex) { char *ciphers, *curves; - struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; - const struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; SSL_METHOD* req_method = NULL; + curl_socket_t sockfd = conn->sock[sockindex]; #ifdef HAVE_LIBOQS word16 oqsAlg = 0; size_t idx = 0; @@ -372,20 +264,19 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) #else #define use_sni(x) Curl_nop_stmt #endif - bool imported_ca_info_blob = false; DEBUGASSERT(backend); if(connssl->state == ssl_connection_complete) return CURLE_OK; - if(conn_config->version_max != CURL_SSLVERSION_MAX_NONE) { + if(SSL_CONN_CONFIG(version_max) != CURL_SSLVERSION_MAX_NONE) { failf(data, "wolfSSL does not support to set maximum SSL/TLS version"); return CURLE_SSL_CONNECT_ERROR; } /* check to see if we've been told to use an explicit SSL/TLS version */ - switch(conn_config->version) { + switch(SSL_CONN_CONFIG(version)) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: #if LIBWOLFSSL_VERSION_HEX >= 0x03003000 /* >= 3.3.0 */ @@ -417,13 +308,8 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) #endif break; case CURL_SSLVERSION_TLSv1_2: -#ifndef WOLFSSL_NO_TLS12 req_method = TLSv1_2_client_method(); use_sni(TRUE); -#else - failf(data, "wolfSSL does not support TLS 1.2"); - return CURLE_NOT_BUILT_IN; -#endif break; case CURL_SSLVERSION_TLSv1_3: #ifdef WOLFSSL_TLS13 @@ -453,7 +339,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_OUT_OF_MEMORY; } - switch(conn_config->version) { + switch(SSL_CONN_CONFIG(version)) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: #if LIBWOLFSSL_VERSION_HEX > 0x03004006 /* > 3.4.6 */ @@ -477,7 +363,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) break; } - ciphers = conn_config->cipher_list; + ciphers = SSL_CONN_CONFIG(cipher_list); if(ciphers) { if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) { failf(data, "failed setting cipher list: %s", ciphers); @@ -486,7 +372,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) infof(data, "Cipher selection: %s", ciphers); } - curves = conn_config->curves; + curves = SSL_CONN_CONFIG(curves); if(curves) { #ifdef HAVE_LIBOQS @@ -506,35 +392,20 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } } } - - if(ca_info_blob) { - if(wolfSSL_CTX_load_verify_buffer( - backend->ctx, ca_info_blob->data, ca_info_blob->len, - SSL_FILETYPE_PEM - ) != SSL_SUCCESS) { - failf(data, "error importing CA certificate blob"); - return CURLE_SSL_CACERT_BADFILE; - } - else { - imported_ca_info_blob = true; - infof(data, "successfully imported CA certificate blob"); - } - } - #ifndef NO_FILESYSTEM /* load trusted cacert */ - if(conn_config->CAfile) { + if(SSL_CONN_CONFIG(CAfile)) { if(1 != SSL_CTX_load_verify_locations(backend->ctx, - conn_config->CAfile, - conn_config->CApath)) { - if(conn_config->verifypeer && !imported_ca_info_blob) { + SSL_CONN_CONFIG(CAfile), + SSL_CONN_CONFIG(CApath))) { + if(SSL_CONN_CONFIG(verifypeer)) { /* Fail if we insist on successfully verifying the server. */ failf(data, "error setting certificate verify locations:" " CAfile: %s CApath: %s", - conn_config->CAfile? - conn_config->CAfile: "none", - conn_config->CApath? - conn_config->CApath : "none"); + SSL_CONN_CONFIG(CAfile)? + SSL_CONN_CONFIG(CAfile): "none", + SSL_CONN_CONFIG(CApath)? + SSL_CONN_CONFIG(CApath) : "none"); return CURLE_SSL_CACERT_BADFILE; } else { @@ -549,25 +420,25 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) infof(data, "successfully set certificate verify locations:"); } infof(data, " CAfile: %s", - conn_config->CAfile ? conn_config->CAfile : "none"); + SSL_CONN_CONFIG(CAfile) ? SSL_CONN_CONFIG(CAfile) : "none"); infof(data, " CApath: %s", - conn_config->CApath ? conn_config->CApath : "none"); + SSL_CONN_CONFIG(CApath) ? SSL_CONN_CONFIG(CApath) : "none"); } /* Load the client certificate, and private key */ - if(ssl_config->primary.clientcert && ssl_config->key) { - int file_type = do_file_type(ssl_config->cert_type); + if(SSL_SET_OPTION(primary.clientcert) && SSL_SET_OPTION(key)) { + int file_type = do_file_type(SSL_SET_OPTION(cert_type)); if(SSL_CTX_use_certificate_file(backend->ctx, - ssl_config->primary.clientcert, + SSL_SET_OPTION(primary.clientcert), file_type) != 1) { failf(data, "unable to use client certificate (no key or wrong pass" " phrase?)"); return CURLE_SSL_CONNECT_ERROR; } - file_type = do_file_type(ssl_config->key_type); - if(SSL_CTX_use_PrivateKey_file(backend->ctx, ssl_config->key, + file_type = do_file_type(SSL_SET_OPTION(key_type)); + if(SSL_CTX_use_PrivateKey_file(backend->ctx, SSL_SET_OPTION(key), file_type) != 1) { failf(data, "unable to set private key"); return CURLE_SSL_CONNECT_ERROR; @@ -580,8 +451,8 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ SSL_CTX_set_verify(backend->ctx, - conn_config->verifypeer?SSL_VERIFY_PEER: - SSL_VERIFY_NONE, + SSL_CONN_CONFIG(verifypeer)?SSL_VERIFY_PEER: + SSL_VERIFY_NONE, NULL); #ifdef HAVE_SNI @@ -590,16 +461,16 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) #ifdef ENABLE_IPV6 struct in6_addr addr6; #endif - size_t hostname_len = strlen(connssl->hostname); - + const char * const hostname = SSL_HOST_NAME(); + size_t hostname_len = strlen(hostname); if((hostname_len < USHRT_MAX) && - !Curl_inet_pton(AF_INET, connssl->hostname, &addr4) + !Curl_inet_pton(AF_INET, hostname, &addr4) #ifdef ENABLE_IPV6 - && !Curl_inet_pton(AF_INET6, connssl->hostname, &addr6) + && !Curl_inet_pton(AF_INET6, hostname, &addr6) #endif ) { size_t snilen; - char *snihost = Curl_ssl_snihost(data, connssl->hostname, &snilen); + char *snihost = Curl_ssl_snihost(data, hostname, &snilen); if(!snihost || wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME, snihost, (unsigned short)snilen) != 1) { @@ -620,7 +491,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } } #ifdef NO_FILESYSTEM - else if(conn_config->verifypeer) { + else if(SSL_CONN_CONFIG(verifypeer)) { failf(data, "SSL: Certificates can't be loaded because wolfSSL was built" " with \"no filesystem\". Either disable peer verification" " (insecure) or if you are building an application with libcurl you" @@ -647,18 +518,29 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) #endif #ifdef HAVE_ALPN - if(connssl->alpn) { - struct alpn_proto_buf proto; - CURLcode result; + if(conn->bits.tls_enable_alpn) { + char protocols[128]; + *protocols = '\0'; + + /* wolfSSL's ALPN protocol name list format is a comma separated string of + protocols in descending order of preference, eg: "h2,http/1.1" */ - result = Curl_alpn_to_proto_str(&proto, connssl->alpn); - if(result || - wolfSSL_UseALPN(backend->handle, (char *)proto.data, proto.len, +#ifdef USE_HTTP2 + if(data->state.httpwant >= CURL_HTTP_VERSION_2) { + strcpy(protocols + strlen(protocols), ALPN_H2 ","); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); + } +#endif + + strcpy(protocols + strlen(protocols), ALPN_HTTP_1_1); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); + + if(wolfSSL_UseALPN(backend->handle, protocols, + (unsigned)strlen(protocols), WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != SSL_SUCCESS) { failf(data, "SSL: failed setting ALPN protocols"); return CURLE_SSL_CONNECT_ERROR; } - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); } #endif /* HAVE_ALPN */ @@ -681,11 +563,13 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) #endif /* HAVE_SECURE_RENEGOTIATION */ /* Check if there's a cached ID we can/should use here! */ - if(ssl_config->primary.sessionid) { + if(SSL_SET_OPTION(primary.sessionid)) { void *ssl_sessionid = NULL; Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL)) { + if(!Curl_ssl_getsessionid(data, conn, + SSL_IS_PROXY() ? TRUE : FALSE, + &ssl_sessionid, NULL, sockindex)) { /* we got a session id, use it! */ if(!SSL_set_session(backend->handle, ssl_sessionid)) { Curl_ssl_delsessionid(data, ssl_sessionid); @@ -697,24 +581,11 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) Curl_ssl_sessionid_unlock(data); } -#ifdef USE_BIO_CHAIN - { - WOLFSSL_BIO *bio; - - bio = BIO_new(bio_cf_method); - if(!bio) - return CURLE_OUT_OF_MEMORY; - - wolfSSL_BIO_set_data(bio, cf); - wolfSSL_set_bio(backend->handle, bio, bio); - } -#else /* USE_BIO_CHAIN */ /* pass the raw socket into the SSL layer */ - if(!SSL_set_fd(backend->handle, (int)Curl_conn_cf_get_socket(cf, data))) { + if(!SSL_set_fd(backend->handle, (int)sockfd)) { failf(data, "SSL: SSL_set_fd failed"); return CURLE_SSL_CONNECT_ERROR; } -#endif /* !USE_BIO_CHAIN */ connssl->connecting_state = ssl_connect_2; return CURLE_OK; @@ -722,24 +593,25 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) static CURLcode -wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) +wolfssl_connect_step2(struct Curl_easy *data, struct connectdata *conn, + int sockindex) { int ret = -1; - struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf)? - data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: - data->set.str[STRING_SSL_PINNEDPUBLICKEY]; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; + const char * const dispname = SSL_HOST_DISPNAME(); + const char * const pinnedpubkey = SSL_PINNED_PUB_KEY(); DEBUGASSERT(backend); ERR_clear_error(); + conn->recv[sockindex] = wolfssl_recv; + conn->send[sockindex] = wolfssl_send; + /* Enable RFC2818 checks */ - if(conn_config->verifyhost) { - char *snihost = Curl_ssl_snihost(data, connssl->hostname, NULL); + if(SSL_CONN_CONFIG(verifyhost)) { + char *snihost = Curl_ssl_snihost(data, SSL_HOST_NAME(), NULL); if(!snihost || (wolfSSL_check_domain_name(backend->handle, snihost) == SSL_FAILURE)) return CURLE_SSL_CONNECT_ERROR; @@ -789,32 +661,32 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) else if(DOMAIN_NAME_MISMATCH == detail) { #if 1 failf(data, " subject alt name(s) or common name do not match \"%s\"", - connssl->dispname); + dispname); return CURLE_PEER_FAILED_VERIFICATION; #else /* When the wolfssl_check_domain_name() is used and you desire to - * continue on a DOMAIN_NAME_MISMATCH, i.e. 'ssl_config.verifyhost + * continue on a DOMAIN_NAME_MISMATCH, i.e. 'conn->ssl_config.verifyhost * == 0', CyaSSL version 2.4.0 will fail with an INCOMPLETE_DATA * error. The only way to do this is currently to switch the * Wolfssl_check_domain_name() in and out based on the - * 'ssl_config.verifyhost' value. */ - if(conn_config->verifyhost) { + * 'conn->ssl_config.verifyhost' value. */ + if(SSL_CONN_CONFIG(verifyhost)) { failf(data, " subject alt name(s) or common name do not match \"%s\"\n", - connssl->dispname); + dispname); return CURLE_PEER_FAILED_VERIFICATION; } else { infof(data, " subject alt name(s) and/or common name do not match \"%s\"", - connssl->dispname); + dispname); return CURLE_OK; } #endif } #if LIBWOLFSSL_VERSION_HEX >= 0x02007000 /* 2.7.0 */ else if(ASN_NO_SIGNER_E == detail) { - if(conn_config->verifypeer) { + if(SSL_CONN_CONFIG(verifypeer)) { failf(data, " CA signer not available for verification"); return CURLE_SSL_CACERT_BADFILE; } @@ -826,9 +698,6 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) } } #endif - else if(backend->io_result == CURLE_AGAIN) { - return CURLE_OK; - } else { failf(data, "SSL_connect failed with error %d: %s", detail, ERR_error_string(detail, error_buffer)); @@ -882,7 +751,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) } #ifdef HAVE_ALPN - if(connssl->alpn) { + if(conn->bits.tls_enable_alpn) { int rc; char *protocol = NULL; unsigned short protocol_len = 0; @@ -890,11 +759,25 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) rc = wolfSSL_ALPN_GetProtocol(backend->handle, &protocol, &protocol_len); if(rc == SSL_SUCCESS) { - Curl_alpn_set_negotiated(cf, data, (const unsigned char *)protocol, - protocol_len); + infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, protocol_len, protocol); + + if(protocol_len == ALPN_HTTP_1_1_LENGTH && + !memcmp(protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH)) + conn->alpn = CURL_HTTP_VERSION_1_1; +#ifdef USE_HTTP2 + else if(data->state.httpwant >= CURL_HTTP_VERSION_2 && + protocol_len == ALPN_H2_LENGTH && + !memcmp(protocol, ALPN_H2, ALPN_H2_LENGTH)) + conn->alpn = CURL_HTTP_VERSION_2; +#endif + else + infof(data, "ALPN, unrecognized protocol %.*s", protocol_len, + protocol); + Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ? + BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); } else if(rc == SSL_ALPN_NOT_FOUND) - Curl_alpn_set_negotiated(cf, data, NULL, 0); + infof(data, VTLS_INFOF_NO_ALPN); else { failf(data, "ALPN, failure getting protocol, error %d", rc); return CURLE_SSL_CONNECT_ERROR; @@ -916,27 +799,28 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) static CURLcode -wolfssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) +wolfssl_connect_step3(struct Curl_easy *data, struct connectdata *conn, + int sockindex) { CURLcode result = CURLE_OK; - struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; - const struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); DEBUGASSERT(backend); - if(ssl_config->primary.sessionid) { + if(SSL_SET_OPTION(primary.sessionid)) { bool incache; bool added = FALSE; void *old_ssl_sessionid = NULL; /* SSL_get1_session allocates memory that has to be freed. */ SSL_SESSION *our_ssl_sessionid = SSL_get1_session(backend->handle); + bool isproxy = SSL_IS_PROXY() ? TRUE : FALSE; if(our_ssl_sessionid) { Curl_ssl_sessionid_lock(data); - incache = !(Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL)); + incache = !(Curl_ssl_getsessionid(data, conn, isproxy, + &old_ssl_sessionid, NULL, sockindex)); if(incache) { if(old_ssl_sessionid != our_ssl_sessionid) { infof(data, "old SSL session ID is stale, removing"); @@ -946,7 +830,8 @@ wolfssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) } if(!incache) { - result = Curl_ssl_addsessionid(cf, data, our_ssl_sessionid, 0, NULL); + result = Curl_ssl_addsessionid(data, conn, isproxy, our_ssl_sessionid, + 0, sockindex, NULL); if(result) { Curl_ssl_sessionid_unlock(data); SSL_SESSION_free(our_ssl_sessionid); @@ -972,15 +857,15 @@ wolfssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) } -static ssize_t wolfssl_send(struct Curl_cfilter *cf, - struct Curl_easy *data, +static ssize_t wolfssl_send(struct Curl_easy *data, + int sockindex, const void *mem, size_t len, CURLcode *curlcode) { - struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; + struct connectdata *conn = data->conn; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; char error_buffer[WOLFSSL_MAX_ERROR_SZ]; int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; int rc; @@ -990,6 +875,7 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf, ERR_clear_error(); rc = SSL_write(backend->handle, mem, memlen); + if(rc <= 0) { int err = SSL_get_error(backend->handle, rc); @@ -997,17 +883,9 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf, case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: /* there's data pending, re-invoke SSL_write() */ - DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len)); *curlcode = CURLE_AGAIN; return -1; default: - if(backend->io_result == CURLE_AGAIN) { - DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len)); - *curlcode = CURLE_AGAIN; - return -1; - } - DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> %d, %d", - len, rc, err)); failf(data, "SSL write: %s, errno %d", ERR_error_string(err, error_buffer), SOCKERRNO); @@ -1015,15 +893,14 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf, return -1; } } - DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> %d", len, rc)); return rc; } -static void wolfssl_close(struct Curl_cfilter *cf, struct Curl_easy *data) +static void wolfssl_close(struct Curl_easy *data, struct connectdata *conn, + int sockindex) { - struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; (void) data; @@ -1044,22 +921,22 @@ static void wolfssl_close(struct Curl_cfilter *cf, struct Curl_easy *data) } } -static ssize_t wolfssl_recv(struct Curl_cfilter *cf, - struct Curl_easy *data, - char *buf, size_t blen, +static ssize_t wolfssl_recv(struct Curl_easy *data, + int num, + char *buf, + size_t buffersize, CURLcode *curlcode) { - struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; + struct connectdata *conn = data->conn; + struct ssl_connect_data *connssl = &conn->ssl[num]; + struct ssl_backend_data *backend = connssl->backend; char error_buffer[WOLFSSL_MAX_ERROR_SZ]; - int buffsize = (blen > (size_t)INT_MAX) ? INT_MAX : (int)blen; + int buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; int nread; DEBUGASSERT(backend); ERR_clear_error(); - *curlcode = CURLE_OK; nread = SSL_read(backend->handle, buf, buffsize); @@ -1068,31 +945,22 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf, switch(err) { case SSL_ERROR_ZERO_RETURN: /* no more data */ - DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen)); - *curlcode = CURLE_OK; - return 0; + break; case SSL_ERROR_NONE: /* FALLTHROUGH */ case SSL_ERROR_WANT_READ: /* FALLTHROUGH */ case SSL_ERROR_WANT_WRITE: /* there's data pending, re-invoke SSL_read() */ - DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen)); *curlcode = CURLE_AGAIN; return -1; default: - if(backend->io_result == CURLE_AGAIN) { - DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen)); - *curlcode = CURLE_AGAIN; - return -1; - } failf(data, "SSL read: %s, errno %d", ERR_error_string(err, error_buffer), SOCKERRNO); *curlcode = CURLE_RECV_ERROR; return -1; } } - DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> %d", blen, nread)); return nread; } @@ -1115,20 +983,15 @@ static size_t wolfssl_version(char *buffer, size_t size) static int wolfssl_init(void) { - int ret; - #ifdef OPENSSL_EXTRA Curl_tls_keylog_open(); #endif - ret = (wolfSSL_Init() == SSL_SUCCESS); - bio_cf_init_methods(); - return ret; + return (wolfSSL_Init() == SSL_SUCCESS); } static void wolfssl_cleanup(void) { - bio_cf_free_methods(); wolfSSL_Cleanup(); #ifdef OPENSSL_EXTRA Curl_tls_keylog_close(); @@ -1136,16 +999,12 @@ static void wolfssl_cleanup(void) } -static bool wolfssl_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data) +static bool wolfssl_data_pending(const struct connectdata *conn, + int connindex) { - struct ssl_connect_data *ctx = cf->ctx; - struct wolfssl_ssl_backend_data *backend; - - (void)data; - DEBUGASSERT(ctx && ctx->backend); - - backend = (struct wolfssl_ssl_backend_data *)ctx->backend; + const struct ssl_connect_data *connssl = &conn->ssl[connindex]; + struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); if(backend->handle) /* SSL is in use */ return (0 != SSL_pending(backend->handle)) ? TRUE : FALSE; else @@ -1157,17 +1016,17 @@ static bool wolfssl_data_pending(struct Curl_cfilter *cf, * This function is called to shut down the SSL layer but keep the * socket open (CCC - Clear Command Channel) */ -static int wolfssl_shutdown(struct Curl_cfilter *cf, - struct Curl_easy *data) +static int wolfssl_shutdown(struct Curl_easy *data, struct connectdata *conn, + int sockindex) { - struct ssl_connect_data *ctx = cf->ctx; - struct wolfssl_ssl_backend_data *backend; int retval = 0; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; - (void)data; - DEBUGASSERT(ctx && ctx->backend); + (void) data; + + DEBUGASSERT(backend); - backend = (struct wolfssl_ssl_backend_data *)ctx->backend; if(backend->handle) { ERR_clear_error(); SSL_free(backend->handle); @@ -1178,14 +1037,15 @@ static int wolfssl_shutdown(struct Curl_cfilter *cf, static CURLcode -wolfssl_connect_common(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool nonblocking, - bool *done) +wolfssl_connect_common(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + bool nonblocking, + bool *done) { CURLcode result; - struct ssl_connect_data *connssl = cf->ctx; - curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + curl_socket_t sockfd = conn->sock[sockindex]; int what; /* check if the connection has already been established */ @@ -1204,7 +1064,7 @@ wolfssl_connect_common(struct Curl_cfilter *cf, return CURLE_OPERATION_TIMEDOUT; } - result = wolfssl_connect_step1(cf, data); + result = wolfssl_connect_step1(data, conn, sockindex); if(result) return result; } @@ -1259,7 +1119,7 @@ wolfssl_connect_common(struct Curl_cfilter *cf, * ensuring that a client using select() or epoll() will always * have a valid fdset to wait on. */ - result = wolfssl_connect_step2(cf, data); + result = wolfssl_connect_step2(data, conn, sockindex); if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state || ssl_connect_2_reading == connssl->connecting_state || @@ -1268,13 +1128,15 @@ wolfssl_connect_common(struct Curl_cfilter *cf, } /* repeat step2 until all transactions are done. */ if(ssl_connect_3 == connssl->connecting_state) { - result = wolfssl_connect_step3(cf, data); + result = wolfssl_connect_step3(data, conn, sockindex); if(result) return result; } if(ssl_connect_done == connssl->connecting_state) { connssl->state = ssl_connection_complete; + conn->recv[sockindex] = wolfssl_recv; + conn->send[sockindex] = wolfssl_send; *done = TRUE; } else @@ -1287,21 +1149,21 @@ wolfssl_connect_common(struct Curl_cfilter *cf, } -static CURLcode wolfssl_connect_nonblocking(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) +static CURLcode wolfssl_connect_nonblocking(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, bool *done) { - return wolfssl_connect_common(cf, data, TRUE, done); + return wolfssl_connect_common(data, conn, sockindex, TRUE, done); } -static CURLcode wolfssl_connect(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode wolfssl_connect(struct Curl_easy *data, + struct connectdata *conn, int sockindex) { CURLcode result; bool done = FALSE; - result = wolfssl_connect_common(cf, data, FALSE, &done); + result = wolfssl_connect_common(data, conn, sockindex, FALSE, &done); if(result) return result; @@ -1342,8 +1204,7 @@ static CURLcode wolfssl_sha256sum(const unsigned char *tmp, /* input */ static void *wolfssl_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; + struct ssl_backend_data *backend = connssl->backend; (void)info; DEBUGASSERT(backend); return backend->handle; @@ -1355,13 +1216,9 @@ const struct Curl_ssl Curl_ssl_wolfssl = { #ifdef KEEP_PEER_CERT SSLSUPP_PINNEDPUBKEY | #endif -#ifdef USE_BIO_CHAIN - SSLSUPP_HTTPS_PROXY | -#endif - SSLSUPP_CAINFO_BLOB | SSLSUPP_SSL_CTX, - sizeof(struct wolfssl_ssl_backend_data), + sizeof(struct ssl_backend_data), wolfssl_init, /* init */ wolfssl_cleanup, /* cleanup */ @@ -1373,7 +1230,7 @@ const struct Curl_ssl Curl_ssl_wolfssl = { Curl_none_cert_status_request, /* cert_status_request */ wolfssl_connect, /* connect */ wolfssl_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks, /* getsock */ + Curl_ssl_getsock, /* getsock */ wolfssl_get_internals, /* get_internals */ wolfssl_close, /* close_one */ Curl_none_close_all, /* close_all */ @@ -1384,10 +1241,7 @@ const struct Curl_ssl Curl_ssl_wolfssl = { Curl_none_false_start, /* false_start */ wolfssl_sha256sum, /* sha256sum */ NULL, /* associate_connection */ - NULL, /* disassociate_connection */ - NULL, /* free_multi_ssl_backend_data */ - wolfssl_recv, /* recv decrypted data */ - wolfssl_send, /* send data to encrypt */ + NULL /* disassociate_connection */ }; #endif diff --git a/contrib/libs/curl/lib/vtls/wolfssl.h b/contrib/libs/curl/lib/vtls/wolfssl.h index a5ed848099..b2e7c3fde2 100644 --- a/contrib/libs/curl/lib/vtls/wolfssl.h +++ b/contrib/libs/curl/lib/vtls/wolfssl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/lib/vtls/x509asn1.c b/contrib/libs/curl/lib/vtls/x509asn1.c index acf8bdb2ab..0cfcbe87da 100644 --- a/contrib/libs/curl/lib/vtls/x509asn1.c +++ b/contrib/libs/curl/lib/vtls/x509asn1.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -48,7 +48,6 @@ #include "curl_ctype.h" #include "hostcheck.h" #include "vtls/vtls.h" -#include "vtls/vtls_int.h" #include "sendf.h" #include "inet_pton.h" #include "curl_base64.h" @@ -172,7 +171,7 @@ static const struct Curl_OID OIDtable[] = { * It is intended to support certificate information gathering for SSL backends * that offer a mean to get certificates as a whole, but do not supply * entry points to get particular certificate sub-fields. - * Please note there is no pretension here to rewrite a full SSL library. + * Please note there is no pretention here to rewrite a full SSL library. */ static const char *getASN1Element(struct Curl_asn1Element *elem, @@ -183,7 +182,7 @@ static const char *getASN1Element(struct Curl_asn1Element *elem, const char *beg, const char *end) { unsigned char b; - size_t len; + unsigned long len; struct Curl_asn1Element lelem; /* Get a single ASN.1 element into `elem', parse ASN.1 string at `beg' @@ -308,7 +307,7 @@ static const char *bit2str(const char *beg, const char *end) */ static const char *int2str(const char *beg, const char *end) { - unsigned int val = 0; + unsigned long val = 0; size_t n = end - beg; if(!n) @@ -324,7 +323,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%x", val >= 10? "0x": "", val); + return curl_maprintf("%s%lx", val >= 10? "0x": "", val); } /* @@ -918,20 +917,6 @@ static const char *dumpAlgo(struct Curl_asn1Element *param, return OID2str(oid.beg, oid.end, TRUE); } -/* - * This is a convenience function for push_certinfo_len that takes a zero - * terminated value. - */ -static CURLcode ssl_push_certinfo(struct Curl_easy *data, - int certnum, - const char *label, - const char *value) -{ - size_t valuelen = strlen(value); - - return Curl_ssl_push_certinfo_len(data, certnum, label, value, valuelen); -} - /* return 0 on success, 1 on error */ static int do_pubkey_field(struct Curl_easy *data, int certnum, const char *label, struct Curl_asn1Element *elem) @@ -944,7 +929,7 @@ static int do_pubkey_field(struct Curl_easy *data, int certnum, output = ASN1tostr(elem, 0); if(output) { if(data->set.ssl.certinfo) - result = ssl_push_certinfo(data, certnum, label, output); + result = Curl_ssl_push_certinfo(data, certnum, label, output); if(!certnum && !result) infof(data, " %s: %s", label, output); free((char *) output); @@ -968,13 +953,14 @@ static int do_pubkey(struct Curl_easy *data, int certnum, * ECC public key is all the data, a value of type BIT STRING mapped to * OCTET STRING and should not be parsed as an ASN.1 value. */ - const size_t len = ((pubkey->end - pubkey->beg - 2) * 4); + const unsigned long len = + (unsigned long)((pubkey->end - pubkey->beg - 2) * 4); if(!certnum) infof(data, " ECC Public Key (%lu bits)", len); if(data->set.ssl.certinfo) { char q[sizeof(len) * 8 / 3 + 1]; (void)msnprintf(q, sizeof(q), "%lu", len); - if(ssl_push_certinfo(data, certnum, "ECC Public Key", q)) + if(Curl_ssl_push_certinfo(data, certnum, "ECC Public Key", q)) return 1; } return do_pubkey_field(data, certnum, "ecPublicKey", pubkey); @@ -986,7 +972,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, if(strcasecompare(algo, "rsaEncryption")) { const char *q; - size_t len; + unsigned long len; p = getASN1Element(&elem, pk.beg, pk.end); if(!p) @@ -995,7 +981,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, /* Compute key length. */ for(q = elem.beg; !*q && q < elem.end; q++) ; - len = ((elem.end - q) * 8); + len = (unsigned long)((elem.end - q) * 8); if(len) { unsigned int i; for(i = *(unsigned char *) q; !(i & 0x80); i <<= 1) @@ -1008,7 +994,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, if(data->set.ssl.certinfo) { char r[sizeof(len) * 8 / 3 + 1]; msnprintf(r, sizeof(r), "%lu", len); - if(ssl_push_certinfo(data, certnum, "RSA Public Key", r)) + if(Curl_ssl_push_certinfo(data, certnum, "RSA Public Key", r)) return 1; } /* Generate coefficients. */ @@ -1087,7 +1073,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, size_t cl1; char *cp2; CURLcode result = CURLE_OK; - unsigned int version; + unsigned long version; size_t i; size_t j; @@ -1106,7 +1092,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, if(!ccp) return CURLE_OUT_OF_MEMORY; if(data->set.ssl.certinfo) { - result = ssl_push_certinfo(data, certnum, "Subject", ccp); + result = Curl_ssl_push_certinfo(data, certnum, "Subject", ccp); if(result) return result; } @@ -1119,7 +1105,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, if(!ccp) return CURLE_OUT_OF_MEMORY; if(data->set.ssl.certinfo) { - result = ssl_push_certinfo(data, certnum, "Issuer", ccp); + result = Curl_ssl_push_certinfo(data, certnum, "Issuer", ccp); } if(!certnum) infof(data, " Issuer: %s", ccp); @@ -1132,23 +1118,23 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, for(ccp = cert.version.beg; ccp < cert.version.end; ccp++) version = (version << 8) | *(const unsigned char *) ccp; if(data->set.ssl.certinfo) { - ccp = curl_maprintf("%x", version); + ccp = curl_maprintf("%lx", version); if(!ccp) return CURLE_OUT_OF_MEMORY; - result = ssl_push_certinfo(data, certnum, "Version", ccp); + result = Curl_ssl_push_certinfo(data, certnum, "Version", ccp); free((char *) ccp); if(result) return result; } if(!certnum) - infof(data, " Version: %u (0x%x)", version + 1, version); + infof(data, " Version: %lu (0x%lx)", version + 1, version); /* Serial number. */ ccp = ASN1tostr(&cert.serialNumber, 0); if(!ccp) return CURLE_OUT_OF_MEMORY; if(data->set.ssl.certinfo) - result = ssl_push_certinfo(data, certnum, "Serial Number", ccp); + result = Curl_ssl_push_certinfo(data, certnum, "Serial Number", ccp); if(!certnum) infof(data, " Serial Number: %s", ccp); free((char *) ccp); @@ -1161,7 +1147,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, if(!ccp) return CURLE_OUT_OF_MEMORY; if(data->set.ssl.certinfo) - result = ssl_push_certinfo(data, certnum, "Signature Algorithm", ccp); + result = Curl_ssl_push_certinfo(data, certnum, "Signature Algorithm", ccp); if(!certnum) infof(data, " Signature Algorithm: %s", ccp); free((char *) ccp); @@ -1173,7 +1159,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, if(!ccp) return CURLE_OUT_OF_MEMORY; if(data->set.ssl.certinfo) - result = ssl_push_certinfo(data, certnum, "Start Date", ccp); + result = Curl_ssl_push_certinfo(data, certnum, "Start Date", ccp); if(!certnum) infof(data, " Start Date: %s", ccp); free((char *) ccp); @@ -1185,7 +1171,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, if(!ccp) return CURLE_OUT_OF_MEMORY; if(data->set.ssl.certinfo) - result = ssl_push_certinfo(data, certnum, "Expire Date", ccp); + result = Curl_ssl_push_certinfo(data, certnum, "Expire Date", ccp); if(!certnum) infof(data, " Expire Date: %s", ccp); free((char *) ccp); @@ -1198,7 +1184,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, if(!ccp) return CURLE_OUT_OF_MEMORY; if(data->set.ssl.certinfo) - result = ssl_push_certinfo(data, certnum, "Public Key Algorithm", + result = Curl_ssl_push_certinfo(data, certnum, "Public Key Algorithm", ccp); if(!result) { int ret; @@ -1217,7 +1203,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, if(!ccp) return CURLE_OUT_OF_MEMORY; if(data->set.ssl.certinfo) - result = ssl_push_certinfo(data, certnum, "Signature", ccp); + result = Curl_ssl_push_certinfo(data, certnum, "Signature", ccp); if(!certnum) infof(data, " Signature: %s", ccp); free((char *) ccp); @@ -1252,7 +1238,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, cp2[i] = '\0'; free(cp1); if(data->set.ssl.certinfo) - result = ssl_push_certinfo(data, certnum, "Cert", cp2); + result = Curl_ssl_push_certinfo(data, certnum, "Cert", cp2); if(!certnum) infof(data, "%s", cp2); free(cp2); @@ -1290,12 +1276,9 @@ static const char *checkOID(const char *beg, const char *end, return matched? ccp: NULL; } -CURLcode Curl_verifyhost(struct Curl_cfilter *cf, - struct Curl_easy *data, +CURLcode Curl_verifyhost(struct Curl_easy *data, struct connectdata *conn, const char *beg, const char *end) { - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct Curl_X509certificate cert; struct Curl_asn1Element dn; struct Curl_asn1Element elem; @@ -1307,8 +1290,9 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf, int matched = -1; size_t addrlen = (size_t) -1; ssize_t len; - size_t hostlen; - + const char * const hostname = SSL_HOST_NAME(); + const char * const dispname = SSL_HOST_DISPNAME(); + size_t hostlen = strlen(hostname); #ifdef ENABLE_IPV6 struct in6_addr addr; #else @@ -1318,22 +1302,19 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf, /* Verify that connection server matches info in X509 certificate at `beg'..`end'. */ - if(!conn_config->verifyhost) + if(!SSL_CONN_CONFIG(verifyhost)) return CURLE_OK; if(Curl_parseX509(&cert, beg, end)) return CURLE_PEER_FAILED_VERIFICATION; - hostlen = strlen(connssl->hostname); - /* Get the server IP address. */ #ifdef ENABLE_IPV6 - if(cf->conn->bits.ipv6_ip && - Curl_inet_pton(AF_INET6, connssl->hostname, &addr)) + if(conn->bits.ipv6_ip && Curl_inet_pton(AF_INET6, hostname, &addr)) addrlen = sizeof(struct in6_addr); else #endif - if(Curl_inet_pton(AF_INET, connssl->hostname, &addr)) + if(Curl_inet_pton(AF_INET, hostname, &addr)) addrlen = sizeof(struct in_addr); /* Process extensions. */ @@ -1367,16 +1348,16 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf, len = utf8asn1str(&dnsname, CURL_ASN1_IA5_STRING, name.beg, name.end); if(len > 0 && (size_t)len == strlen(dnsname)) - matched = Curl_cert_hostcheck(dnsname, (size_t)len, - connssl->hostname, hostlen); + matched = Curl_cert_hostcheck(dnsname, + (size_t)len, hostname, hostlen); else matched = 0; free(dnsname); break; case 7: /* IP address. */ - matched = (size_t)(name.end - name.beg) == addrlen && - !memcmp(&addr, name.beg, addrlen); + matched = (size_t) (name.end - name.beg) == addrlen && + !memcmp(&addr, name.beg, addrlen); break; } } @@ -1386,12 +1367,12 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf, switch(matched) { case 1: /* an alternative name matched the server hostname */ - infof(data, " subjectAltName: %s matched", connssl->dispname); + infof(data, " subjectAltName: %s matched", dispname); return CURLE_OK; case 0: /* an alternative name field existed, but didn't match and then we MUST fail */ - infof(data, " subjectAltName does not match %s", connssl->dispname); + infof(data, " subjectAltName does not match %s", dispname); return CURLE_PEER_FAILED_VERIFICATION; } @@ -1428,14 +1409,14 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf, if(strlen(dnsname) != (size_t) len) /* Nul byte in string ? */ failf(data, "SSL: illegal cert name field"); else if(Curl_cert_hostcheck((const char *) dnsname, - len, connssl->hostname, hostlen)) { + len, hostname, hostlen)) { infof(data, " common name: %s (matched)", dnsname); free(dnsname); return CURLE_OK; } else failf(data, "SSL: certificate subject name '%s' does not match " - "target host name '%s'", dnsname, connssl->dispname); + "target host name '%s'", dnsname, dispname); free(dnsname); } diff --git a/contrib/libs/curl/lib/vtls/x509asn1.h b/contrib/libs/curl/lib/vtls/x509asn1.h index 5496de40e4..a18fa11530 100644 --- a/contrib/libs/curl/lib/vtls/x509asn1.h +++ b/contrib/libs/curl/lib/vtls/x509asn1.h @@ -8,7 +8,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -30,7 +30,6 @@ #if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \ defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) -#include "cfilters.h" #include "urldata.h" /* @@ -74,7 +73,7 @@ int Curl_parseX509(struct Curl_X509certificate *cert, const char *beg, const char *end); CURLcode Curl_extract_certinfo(struct Curl_easy *data, int certnum, const char *beg, const char *end); -CURLcode Curl_verifyhost(struct Curl_cfilter *cf, struct Curl_easy *data, +CURLcode Curl_verifyhost(struct Curl_easy *data, struct connectdata *conn, const char *beg, const char *end); #endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL * or USE_SECTRANSP */ diff --git a/contrib/libs/curl/lib/warnless.c b/contrib/libs/curl/lib/warnless.c index 65c5ec5354..b00d7a5a26 100644 --- a/contrib/libs/curl/lib/warnless.c +++ b/contrib/libs/curl/lib/warnless.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -35,12 +35,9 @@ #endif /* __INTEL_COMPILER && __unix__ */ -#include "warnless.h" +#define BUILDING_WARNLESS_C 1 -#ifdef WIN32 -#undef read -#undef write -#endif +#include "warnless.h" #include <limits.h> @@ -379,9 +376,6 @@ ssize_t curlx_write(int fd, const void *buf, size_t count) return (ssize_t)write(fd, buf, curlx_uztoui(count)); } -/* Ensure that warnless.h continues to have an effect in "unity" builds. */ -#undef HEADER_CURL_WARNLESS_H - #endif /* WIN32 */ #if defined(__INTEL_COMPILER) && defined(__unix__) diff --git a/contrib/libs/curl/lib/warnless.h b/contrib/libs/curl/lib/warnless.h index 2a5301628f..4367099d92 100644 --- a/contrib/libs/curl/lib/warnless.h +++ b/contrib/libs/curl/lib/warnless.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -75,10 +75,12 @@ ssize_t curlx_read(int fd, void *buf, size_t count); ssize_t curlx_write(int fd, const void *buf, size_t count); -#undef read -#define read(fd, buf, count) curlx_read(fd, buf, count) -#undef write -#define write(fd, buf, count) curlx_write(fd, buf, count) +#ifndef BUILDING_WARNLESS_C +# undef read +# define read(fd, buf, count) curlx_read(fd, buf, count) +# undef write +# define write(fd, buf, count) curlx_write(fd, buf, count) +#endif #endif /* WIN32 */ diff --git a/contrib/libs/curl/lib/macos.c b/contrib/libs/curl/lib/wildcard.c index 5fe4e0bf77..a3e24b6784 100644 --- a/contrib/libs/curl/lib/macos.c +++ b/contrib/libs/curl/lib/wildcard.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,39 +24,52 @@ #include "curl_setup.h" -#if defined(__APPLE__) +#ifndef CURL_DISABLE_FTP -#if !defined(TARGET_OS_OSX) || TARGET_OS_OSX +#include "wildcard.h" +#include "llist.h" +#include "fileinfo.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" -#include <curl/curl.h> +static void fileinfo_dtor(void *user, void *element) +{ + (void)user; + Curl_fileinfo_cleanup(element); +} -#include "macos.h" +CURLcode Curl_wildcard_init(struct WildcardData *wc) +{ + Curl_llist_init(&wc->filelist, fileinfo_dtor); + wc->state = CURLWC_INIT; -#if defined(ENABLE_IPV6) && defined(CURL_OSX_CALL_COPYPROXIES) -#include <SystemConfiguration/SCDynamicStoreCopySpecific.h> -#endif + return CURLE_OK; +} -CURLcode Curl_macos_init(void) +void Curl_wildcard_dtor(struct WildcardData *wc) { -#if defined(ENABLE_IPV6) && defined(CURL_OSX_CALL_COPYPROXIES) - { - /* - * The automagic conversion from IPv4 literals to IPv6 literals only - * works if the SCDynamicStoreCopyProxies system function gets called - * first. As Curl currently doesn't support system-wide HTTP proxies, we - * therefore don't use any value this function might return. - * - * This function is only available on a macOS and is not needed for - * IPv4-only builds, hence the conditions above. - */ - CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL); - if(dict) - CFRelease(dict); + if(!wc) + return; + + if(wc->dtor) { + wc->dtor(wc->protdata); + wc->dtor = ZERO_NULL; + wc->protdata = NULL; } -#endif - return CURLE_OK; -} + DEBUGASSERT(wc->protdata == NULL); + + Curl_llist_destroy(&wc->filelist, NULL); -#endif /* TARGET_OS_OSX */ -#endif /* __APPLE__ */ + free(wc->path); + wc->path = NULL; + free(wc->pattern); + wc->pattern = NULL; + + wc->customptr = NULL; + wc->state = CURLWC_INIT; +} + +#endif /* if disabled */ diff --git a/contrib/libs/curl/lib/wildcard.h b/contrib/libs/curl/lib/wildcard.h new file mode 100644 index 0000000000..21e933b9a4 --- /dev/null +++ b/contrib/libs/curl/lib/wildcard.h @@ -0,0 +1,70 @@ +#ifndef HEADER_CURL_WILDCARD_H +#define HEADER_CURL_WILDCARD_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2010 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifndef CURL_DISABLE_FTP +#include <curl/curl.h> +#include "llist.h" + +/* list of wildcard process states */ +typedef enum { + CURLWC_CLEAR = 0, + CURLWC_INIT = 1, + CURLWC_MATCHING, /* library is trying to get list of addresses for + downloading */ + CURLWC_DOWNLOADING, + CURLWC_CLEAN, /* deallocate resources and reset settings */ + CURLWC_SKIP, /* skip over concrete file */ + CURLWC_ERROR, /* error cases */ + CURLWC_DONE /* if is wildcard->state == CURLWC_DONE wildcard loop + will end */ +} wildcard_states; + +typedef void (*wildcard_dtor)(void *ptr); + +/* struct keeping information about wildcard download process */ +struct WildcardData { + wildcard_states state; + char *path; /* path to the directory, where we trying wildcard-match */ + char *pattern; /* wildcard pattern */ + struct Curl_llist filelist; /* llist with struct Curl_fileinfo */ + void *protdata; /* pointer to protocol specific temporary data */ + wildcard_dtor dtor; + void *customptr; /* for CURLOPT_CHUNK_DATA pointer */ +}; + +CURLcode Curl_wildcard_init(struct WildcardData *wc); +void Curl_wildcard_dtor(struct WildcardData *wc); + +struct Curl_easy; + +#else +/* FTP is disabled */ +#define Curl_wildcard_dtor(x) +#endif + +#endif /* HEADER_CURL_WILDCARD_H */ diff --git a/contrib/libs/curl/lib/ws.c b/contrib/libs/curl/lib/ws.c index 3c1964b860..a673446625 100644 --- a/contrib/libs/curl/lib/ws.c +++ b/contrib/libs/curl/lib/ws.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -27,11 +27,9 @@ #ifdef USE_WEBSOCKETS #include "urldata.h" -#include "bufq.h" #include "dynbuf.h" #include "rand.h" #include "curl_base64.h" -#include "connect.h" #include "sendf.h" #include "multiif.h" #include "ws.h" @@ -44,488 +42,6 @@ #include "curl_memory.h" #include "memdebug.h" - -#define WSBIT_FIN 0x80 -#define WSBIT_OPCODE_CONT 0 -#define WSBIT_OPCODE_TEXT (1) -#define WSBIT_OPCODE_BIN (2) -#define WSBIT_OPCODE_CLOSE (8) -#define WSBIT_OPCODE_PING (9) -#define WSBIT_OPCODE_PONG (0xa) -#define WSBIT_OPCODE_MASK (0xf) - -#define WSBIT_MASK 0x80 - -/* buffer dimensioning */ -#define WS_CHUNK_SIZE 65535 -#define WS_CHUNK_COUNT 2 - -struct ws_frame_meta { - char proto_opcode; - int flags; - const char *name; -}; - -static struct ws_frame_meta WS_FRAMES[] = { - { WSBIT_OPCODE_CONT, CURLWS_CONT, "CONT" }, - { WSBIT_OPCODE_TEXT, CURLWS_TEXT, "TEXT" }, - { WSBIT_OPCODE_BIN, CURLWS_BINARY, "BIN" }, - { WSBIT_OPCODE_CLOSE, CURLWS_CLOSE, "CLOSE" }, - { WSBIT_OPCODE_PING, CURLWS_PING, "PING" }, - { WSBIT_OPCODE_PONG, CURLWS_PONG, "PONG" }, -}; - -static const char *ws_frame_name_of_op(unsigned char proto_opcode) -{ - unsigned char opcode = proto_opcode & WSBIT_OPCODE_MASK; - size_t i; - for(i = 0; i < sizeof(WS_FRAMES)/sizeof(WS_FRAMES[0]); ++i) { - if(WS_FRAMES[i].proto_opcode == opcode) - return WS_FRAMES[i].name; - } - return "???"; -} - -static int ws_frame_op2flags(unsigned char proto_opcode) -{ - unsigned char opcode = proto_opcode & WSBIT_OPCODE_MASK; - size_t i; - for(i = 0; i < sizeof(WS_FRAMES)/sizeof(WS_FRAMES[0]); ++i) { - if(WS_FRAMES[i].proto_opcode == opcode) - return WS_FRAMES[i].flags; - } - return 0; -} - -static unsigned char ws_frame_flags2op(int flags) -{ - size_t i; - for(i = 0; i < sizeof(WS_FRAMES)/sizeof(WS_FRAMES[0]); ++i) { - if(WS_FRAMES[i].flags & flags) - return WS_FRAMES[i].proto_opcode; - } - return 0; -} - -static void ws_dec_info(struct ws_decoder *dec, struct Curl_easy *data, - const char *msg) -{ - switch(dec->head_len) { - case 0: - break; - case 1: - infof(data, "WS-DEC: %s [%s%s]", msg, - ws_frame_name_of_op(dec->head[0]), - (dec->head[0] & WSBIT_FIN)? "" : " NON-FINAL"); - break; - default: - if(dec->head_len < dec->head_total) { - infof(data, "WS-DEC: %s [%s%s](%d/%d)", msg, - ws_frame_name_of_op(dec->head[0]), - (dec->head[0] & WSBIT_FIN)? "" : " NON-FINAL", - dec->head_len, dec->head_total); - } - else { - infof(data, "WS-DEC: %s [%s%s payload=%" CURL_FORMAT_CURL_OFF_T - "/%" CURL_FORMAT_CURL_OFF_T "]", - msg, ws_frame_name_of_op(dec->head[0]), - (dec->head[0] & WSBIT_FIN)? "" : " NON-FINAL", - dec->payload_offset, dec->payload_len); - } - break; - } -} - -typedef ssize_t ws_write_payload(const unsigned char *buf, size_t buflen, - int frame_age, int frame_flags, - curl_off_t payload_offset, - curl_off_t payload_len, - void *userp, - CURLcode *err); - - -static void ws_dec_reset(struct ws_decoder *dec) -{ - dec->frame_age = 0; - dec->frame_flags = 0; - dec->payload_offset = 0; - dec->payload_len = 0; - dec->head_len = dec->head_total = 0; - dec->state = WS_DEC_INIT; -} - -static void ws_dec_init(struct ws_decoder *dec) -{ - ws_dec_reset(dec); -} - -static CURLcode ws_dec_read_head(struct ws_decoder *dec, - struct Curl_easy *data, - struct bufq *inraw) -{ - const unsigned char *inbuf; - size_t inlen; - - while(Curl_bufq_peek(inraw, &inbuf, &inlen)) { - if(dec->head_len == 0) { - dec->head[0] = *inbuf; - Curl_bufq_skip(inraw, 1); - - dec->frame_flags = ws_frame_op2flags(dec->head[0]); - if(!dec->frame_flags) { - failf(data, "WS: unknown opcode: %x", dec->head[0]); - ws_dec_reset(dec); - return CURLE_RECV_ERROR; - } - dec->head_len = 1; - /* ws_dec_info(dec, data, "seeing opcode"); */ - continue; - } - else if(dec->head_len == 1) { - dec->head[1] = *inbuf; - Curl_bufq_skip(inraw, 1); - dec->head_len = 2; - - if(dec->head[1] & WSBIT_MASK) { - /* A client MUST close a connection if it detects a masked frame. */ - failf(data, "WS: masked input frame"); - ws_dec_reset(dec); - return CURLE_RECV_ERROR; - } - /* How long is the frame head? */ - if(dec->head[1] == 126) { - dec->head_total = 4; - continue; - } - else if(dec->head[1] == 127) { - dec->head_total = 10; - continue; - } - else { - dec->head_total = 2; - } - } - - if(dec->head_len < dec->head_total) { - dec->head[dec->head_len] = *inbuf; - Curl_bufq_skip(inraw, 1); - ++dec->head_len; - if(dec->head_len < dec->head_total) { - /* ws_dec_info(dec, data, "decoding head"); */ - continue; - } - } - /* got the complete frame head */ - DEBUGASSERT(dec->head_len == dec->head_total); - switch(dec->head_total) { - case 2: - dec->payload_len = dec->head[1]; - break; - case 4: - dec->payload_len = (dec->head[2] << 8) | dec->head[3]; - break; - case 10: - dec->payload_len = ((curl_off_t)dec->head[2] << 56) | - (curl_off_t)dec->head[3] << 48 | - (curl_off_t)dec->head[4] << 40 | - (curl_off_t)dec->head[5] << 32 | - (curl_off_t)dec->head[6] << 24 | - (curl_off_t)dec->head[7] << 16 | - (curl_off_t)dec->head[8] << 8 | - dec->head[9]; - break; - default: - /* this should never happen */ - DEBUGASSERT(0); - failf(data, "WS: unexpected frame header length"); - return CURLE_RECV_ERROR; - } - - dec->frame_age = 0; - dec->payload_offset = 0; - ws_dec_info(dec, data, "decoded"); - return CURLE_OK; - } - return CURLE_AGAIN; -} - -static CURLcode ws_dec_pass_payload(struct ws_decoder *dec, - struct Curl_easy *data, - struct bufq *inraw, - ws_write_payload *write_payload, - void *write_ctx) -{ - const unsigned char *inbuf; - size_t inlen; - ssize_t nwritten; - CURLcode result; - curl_off_t remain = dec->payload_len - dec->payload_offset; - - (void)data; - while(remain && Curl_bufq_peek(inraw, &inbuf, &inlen)) { - if((curl_off_t)inlen > remain) - inlen = (size_t)remain; - nwritten = write_payload(inbuf, inlen, dec->frame_age, dec->frame_flags, - dec->payload_offset, dec->payload_len, - write_ctx, &result); - if(nwritten < 0) - return result; - Curl_bufq_skip(inraw, (size_t)nwritten); - dec->payload_offset += (curl_off_t)nwritten; - remain = dec->payload_len - dec->payload_offset; - /* infof(data, "WS-DEC: passed %zd bytes payload, %" - CURL_FORMAT_CURL_OFF_T " remain", - nwritten, remain); */ - } - - return remain? CURLE_AGAIN : CURLE_OK; -} - -static CURLcode ws_dec_pass(struct ws_decoder *dec, - struct Curl_easy *data, - struct bufq *inraw, - ws_write_payload *write_payload, - void *write_ctx) -{ - CURLcode result; - - if(Curl_bufq_is_empty(inraw)) - return CURLE_AGAIN; - - switch(dec->state) { - case WS_DEC_INIT: - ws_dec_reset(dec); - dec->state = WS_DEC_HEAD; - /* FALLTHROUGH */ - case WS_DEC_HEAD: - result = ws_dec_read_head(dec, data, inraw); - if(result) { - if(result != CURLE_AGAIN) { - infof(data, "WS: decode error %d", (int)result); - break; /* real error */ - } - /* incomplete ws frame head */ - DEBUGASSERT(Curl_bufq_is_empty(inraw)); - break; - } - /* head parsing done */ - dec->state = WS_DEC_PAYLOAD; - if(dec->payload_len == 0) { - ssize_t nwritten; - const unsigned char tmp = '\0'; - /* special case of a 0 length frame, need to write once */ - nwritten = write_payload(&tmp, 0, dec->frame_age, dec->frame_flags, - 0, 0, write_ctx, &result); - if(nwritten < 0) - return result; - dec->state = WS_DEC_INIT; - break; - } - /* FALLTHROUGH */ - case WS_DEC_PAYLOAD: - result = ws_dec_pass_payload(dec, data, inraw, write_payload, write_ctx); - ws_dec_info(dec, data, "passing"); - if(result) - return result; - /* paylod parsing done */ - dec->state = WS_DEC_INIT; - break; - default: - /* we covered all enums above, but some code analyzers are whimps */ - result = CURLE_FAILED_INIT; - } - return result; -} - -static void update_meta(struct websocket *ws, - int frame_age, int frame_flags, - curl_off_t payload_offset, - curl_off_t payload_len, - size_t cur_len) -{ - ws->frame.age = frame_age; - ws->frame.flags = frame_flags; - ws->frame.offset = payload_offset; - ws->frame.len = cur_len; - ws->frame.bytesleft = (payload_len - payload_offset - cur_len); -} - -static void ws_enc_info(struct ws_encoder *enc, struct Curl_easy *data, - const char *msg) -{ - infof(data, "WS-ENC: %s [%s%s%s payload=%" CURL_FORMAT_CURL_OFF_T - "/%" CURL_FORMAT_CURL_OFF_T "]", - msg, ws_frame_name_of_op(enc->firstbyte), - (enc->firstbyte & WSBIT_OPCODE_MASK) == WSBIT_OPCODE_CONT ? - " CONT" : "", - (enc->firstbyte & WSBIT_FIN)? "" : " NON-FIN", - enc->payload_len - enc->payload_remain, enc->payload_len); -} - -static void ws_enc_reset(struct ws_encoder *enc) -{ - enc->payload_remain = 0; - enc->xori = 0; - enc->contfragment = FALSE; -} - -static void ws_enc_init(struct ws_encoder *enc) -{ - ws_enc_reset(enc); -} - -/*** - RFC 6455 Section 5.2 - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-------+-+-------------+-------------------------------+ - |F|R|R|R| opcode|M| Payload len | Extended payload length | - |I|S|S|S| (4) |A| (7) | (16/64) | - |N|V|V|V| |S| | (if payload len==126/127) | - | |1|2|3| |K| | | - +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + - | Extended payload length continued, if payload len == 127 | - + - - - - - - - - - - - - - - - +-------------------------------+ - | |Masking-key, if MASK set to 1 | - +-------------------------------+-------------------------------+ - | Masking-key (continued) | Payload Data | - +-------------------------------- - - - - - - - - - - - - - - - + - : Payload Data continued ... : - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - | Payload Data continued ... | - +---------------------------------------------------------------+ -*/ - -static ssize_t ws_enc_write_head(struct Curl_easy *data, - struct ws_encoder *enc, - unsigned int flags, - curl_off_t payload_len, - struct bufq *out, - CURLcode *err) -{ - unsigned char firstbyte = 0; - unsigned char opcode; - unsigned char head[14]; - size_t hlen; - ssize_t n; - - if(enc->payload_remain > 0) { - /* trying to write a new frame before the previous one is finished */ - failf(data, "WS: starting new frame with %zd bytes from last one" - "remaining to be sent", (ssize_t)enc->payload_remain); - *err = CURLE_SEND_ERROR; - return -1; - } - - opcode = ws_frame_flags2op(flags); - if(!opcode) { - failf(data, "WS: provided flags not recognized '%x'", flags); - *err = CURLE_SEND_ERROR; - return -1; - } - - if(!(flags & CURLWS_CONT)) { - if(!enc->contfragment) - /* not marked as continuing, this is the final fragment */ - firstbyte |= WSBIT_FIN | opcode; - else - /* marked as continuing, this is the final fragment; set CONT - opcode and FIN bit */ - firstbyte |= WSBIT_FIN | WSBIT_OPCODE_CONT; - - enc->contfragment = FALSE; - } - else if(enc->contfragment) { - /* the previous fragment was not a final one and this isn't either, keep a - CONT opcode and no FIN bit */ - firstbyte |= WSBIT_OPCODE_CONT; - } - else { - firstbyte = opcode; - enc->contfragment = TRUE; - } - - head[0] = enc->firstbyte = firstbyte; - if(payload_len > 65535) { - head[1] = 127 | WSBIT_MASK; - head[2] = (unsigned char)((payload_len >> 56) & 0xff); - head[3] = (unsigned char)((payload_len >> 48) & 0xff); - head[4] = (unsigned char)((payload_len >> 40) & 0xff); - head[5] = (unsigned char)((payload_len >> 32) & 0xff); - head[6] = (unsigned char)((payload_len >> 24) & 0xff); - head[7] = (unsigned char)((payload_len >> 16) & 0xff); - head[8] = (unsigned char)((payload_len >> 8) & 0xff); - head[9] = (unsigned char)(payload_len & 0xff); - hlen = 10; - } - else if(payload_len >= 126) { - head[1] = 126 | WSBIT_MASK; - head[2] = (unsigned char)((payload_len >> 8) & 0xff); - head[3] = (unsigned char)(payload_len & 0xff); - hlen = 4; - } - else { - head[1] = (unsigned char)payload_len | WSBIT_MASK; - hlen = 2; - } - - enc->payload_remain = enc->payload_len = payload_len; - ws_enc_info(enc, data, "sending"); - - /* add 4 bytes mask */ - memcpy(&head[hlen], &enc->mask, 4); - hlen += 4; - /* reset for payload to come */ - enc->xori = 0; - - n = Curl_bufq_write(out, head, hlen, err); - if(n < 0) - return -1; - if((size_t)n != hlen) { - /* We use a bufq with SOFT_LIMIT, writing should always succeed */ - DEBUGASSERT(0); - *err = CURLE_SEND_ERROR; - return -1; - } - return n; -} - -static ssize_t ws_enc_write_payload(struct ws_encoder *enc, - struct Curl_easy *data, - const unsigned char *buf, size_t buflen, - struct bufq *out, CURLcode *err) -{ - ssize_t n; - size_t i, len; - - if(Curl_bufq_is_full(out)) { - *err = CURLE_AGAIN; - return -1; - } - - /* not the most performant way to do this */ - len = buflen; - if((curl_off_t)len > enc->payload_remain) - len = (size_t)enc->payload_remain; - - for(i = 0; i < len; ++i) { - unsigned char c = buf[i] ^ enc->mask[enc->xori]; - n = Curl_bufq_write(out, &c, 1, err); - if(n < 0) { - if((*err != CURLE_AGAIN) || !i) - return -1; - break; - } - enc->xori++; - enc->xori &= 3; - } - enc->payload_remain -= (curl_off_t)i; - ws_enc_info(enc, data, "buffered"); - return (ssize_t)i; -} - - struct wsfield { const char *name; const char *val; @@ -595,38 +111,18 @@ CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req) } } k->upgr101 = UPGR101_WS; + Curl_dyn_init(&data->req.p.http->ws.buf, MAX_WS_SIZE * 2); return result; } -/* - * 'nread' is number of bytes of websocket data already in the buffer at - * 'mem'. - */ -CURLcode Curl_ws_accept(struct Curl_easy *data, - const char *mem, size_t nread) +CURLcode Curl_ws_accept(struct Curl_easy *data) { struct SingleRequest *k = &data->req; - struct websocket *ws; + struct HTTP *ws = data->req.p.http; + struct connectdata *conn = data->conn; + struct websocket *wsp = &data->req.p.http->ws; CURLcode result; - DEBUGASSERT(data->conn); - ws = data->conn->proto.ws; - if(!ws) { - ws = calloc(1, sizeof(*ws)); - if(!ws) - return CURLE_OUT_OF_MEMORY; - data->conn->proto.ws = ws; - Curl_bufq_init(&ws->recvbuf, WS_CHUNK_SIZE, WS_CHUNK_COUNT); - Curl_bufq_init2(&ws->sendbuf, WS_CHUNK_SIZE, WS_CHUNK_COUNT, - BUFQ_OPT_SOFT_LIMIT); - ws_dec_init(&ws->dec); - ws_enc_init(&ws->enc); - } - else { - Curl_bufq_reset(&ws->recvbuf); - ws_dec_reset(&ws->dec); - ws_enc_reset(&ws->enc); - } /* Verify the Sec-WebSocket-Accept response. The sent value is the base64 encoded version of a SHA-1 hash done on the @@ -647,74 +143,184 @@ CURLcode Curl_ws_accept(struct Curl_easy *data, the WebSocket Connection. */ /* 4 bytes random */ - - result = Curl_rand(data, (unsigned char *)&ws->enc.mask, - sizeof(ws->enc.mask)); + result = Curl_rand(data, (unsigned char *)&ws->ws.mask, sizeof(ws->ws.mask)); if(result) return result; + infof(data, "Received 101, switch to WebSocket; mask %02x%02x%02x%02x", - ws->enc.mask[0], ws->enc.mask[1], ws->enc.mask[2], ws->enc.mask[3]); - - if(data->set.connect_only) { - ssize_t nwritten; - /* In CONNECT_ONLY setup, the payloads from `mem` need to be received - * when using `curl_ws_recv` later on after this transfer is already - * marked as DONE. */ - nwritten = Curl_bufq_write(&ws->recvbuf, (const unsigned char *)mem, - nread, &result); - if(nwritten < 0) - return result; - infof(data, "%zu bytes websocket payload", nread); - } + ws->ws.mask[0], ws->ws.mask[1], ws->ws.mask[2], ws->ws.mask[3]); k->upgr101 = UPGR101_RECEIVED; + if(data->set.connect_only) + /* switch off non-blocking sockets */ + (void)curlx_nonblock(conn->sock[FIRSTSOCKET], FALSE); + + wsp->oleft = 0; return result; } -static ssize_t ws_client_write(const unsigned char *buf, size_t buflen, - int frame_age, int frame_flags, - curl_off_t payload_offset, - curl_off_t payload_len, - void *userp, - CURLcode *err) +#define WSBIT_FIN 0x80 +#define WSBIT_OPCODE_CONT 0 +#define WSBIT_OPCODE_TEXT (1) +#define WSBIT_OPCODE_BIN (2) +#define WSBIT_OPCODE_CLOSE (8) +#define WSBIT_OPCODE_PING (9) +#define WSBIT_OPCODE_PONG (0xa) +#define WSBIT_OPCODE_MASK (0xf) + +#define WSBIT_MASK 0x80 + +/* remove the spent bytes from the beginning of the buffer as that part has + now been delivered to the application */ +static void ws_decode_clear(struct Curl_easy *data) { - struct Curl_easy *data = userp; - struct websocket *ws; - size_t wrote; - curl_off_t remain = (payload_len - (payload_offset + buflen)); - - (void)frame_age; - if(!data->conn || !data->conn->proto.ws) { - *err = CURLE_FAILED_INIT; - return -1; + struct websocket *wsp = &data->req.p.http->ws; + size_t spent = wsp->usedbuf; + size_t len = Curl_dyn_len(&wsp->buf); + size_t keep = len - spent; + DEBUGASSERT(len >= spent); + Curl_dyn_tail(&wsp->buf, keep); +} + +/* ws_decode() decodes a binary frame into structured WebSocket data, + + wpkt - the incoming raw data. If NULL, work on the already buffered data. + ilen - the size of the provided data, perhaps too little, perhaps too much + out - stored pointed to extracted data + olen - stored length of the extracted data + oleft - number of unread bytes pending to that belongs to this frame + more - if there is more data in there + flags - stored bitmask about the frame + + Returns CURLE_AGAIN if there is only a partial frame in the buffer. Then it + stores the first part in the ->extra buffer to be used in the next call + when more data is provided. +*/ + +static CURLcode ws_decode(struct Curl_easy *data, + unsigned char *wpkt, size_t ilen, + unsigned char **out, size_t *olen, + curl_off_t *oleft, + bool *more, + unsigned int *flags) +{ + bool fin; + unsigned char opcode; + curl_off_t total; + size_t dataindex = 2; + curl_off_t plen; /* size of data in the buffer */ + curl_off_t payloadsize; + struct websocket *wsp = &data->req.p.http->ws; + unsigned char *p; + CURLcode result; + + *olen = 0; + + /* add the incoming bytes, if any */ + if(wpkt) { + result = Curl_dyn_addn(&wsp->buf, wpkt, ilen); + if(result) + return result; + } + + plen = Curl_dyn_len(&wsp->buf); + if(plen < 2) { + /* the smallest possible frame is two bytes */ + infof(data, "WS: plen == %u, EAGAIN", (int)plen); + return CURLE_AGAIN; + } + + p = Curl_dyn_uptr(&wsp->buf); + + fin = p[0] & WSBIT_FIN; + opcode = p[0] & WSBIT_OPCODE_MASK; + infof(data, "WS:%d received FIN bit %u", __LINE__, (int)fin); + *flags = 0; + switch(opcode) { + case WSBIT_OPCODE_CONT: + if(!fin) + *flags |= CURLWS_CONT; + infof(data, "WS: received OPCODE CONT"); + break; + case WSBIT_OPCODE_TEXT: + infof(data, "WS: received OPCODE TEXT"); + *flags |= CURLWS_TEXT; + break; + case WSBIT_OPCODE_BIN: + infof(data, "WS: received OPCODE BINARY"); + *flags |= CURLWS_BINARY; + break; + case WSBIT_OPCODE_CLOSE: + infof(data, "WS: received OPCODE CLOSE"); + *flags |= CURLWS_CLOSE; + break; + case WSBIT_OPCODE_PING: + infof(data, "WS: received OPCODE PING"); + *flags |= CURLWS_PING; + break; + case WSBIT_OPCODE_PONG: + infof(data, "WS: received OPCODE PONG"); + *flags |= CURLWS_PONG; + break; } - ws = data->conn->proto.ws; - - if((frame_flags & CURLWS_PING) && !remain) { - /* auto-respond to PINGs, only works for single-frame payloads atm */ - size_t bytes; - infof(data, "WS: auto-respond to PING with a PONG"); - /* send back the exact same content as a PONG */ - *err = curl_ws_send(data, buf, buflen, &bytes, 0, CURLWS_PONG); - if(*err) - return -1; + + if(p[1] & WSBIT_MASK) { + /* A client MUST close a connection if it detects a masked frame. */ + failf(data, "WS: masked input frame"); + return CURLE_RECV_ERROR; } - else if(buflen || !remain) { - /* deliver the decoded frame to the user callback. The application - * may invoke curl_ws_meta() to access frame information. */ - update_meta(ws, frame_age, frame_flags, payload_offset, - payload_len, buflen); - Curl_set_in_callback(data, true); - wrote = data->set.fwrite_func((char *)buf, 1, - buflen, data->set.out); - Curl_set_in_callback(data, false); - if(wrote != buflen) { - *err = CURLE_RECV_ERROR; - return -1; + payloadsize = p[1]; + if(payloadsize == 126) { + if(plen < 4) { + infof(data, "WS:%d plen == %u, EAGAIN", __LINE__, (int)plen); + return CURLE_AGAIN; /* not enough data available */ } + payloadsize = (p[2] << 8) | p[3]; + dataindex += 2; + } + else if(payloadsize == 127) { + /* 64 bit payload size */ + if(plen < 10) + return CURLE_AGAIN; + if(p[2] & 80) { + failf(data, "WS: too large frame"); + return CURLE_RECV_ERROR; + } + dataindex += 8; + payloadsize = ((curl_off_t)p[2] << 56) | + (curl_off_t)p[3] << 48 | + (curl_off_t)p[4] << 40 | + (curl_off_t)p[5] << 32 | + (curl_off_t)p[6] << 24 | + (curl_off_t)p[7] << 16 | + (curl_off_t)p[8] << 8 | + p[9]; + } + + total = dataindex + payloadsize; + if(total > plen) { + /* deliver a partial frame */ + *oleft = total - dataindex; + payloadsize = total - dataindex; + } + else { + *oleft = 0; + if(plen > total) + /* there is another fragment after */ + *more = TRUE; } - *err = CURLE_OK; - return (ssize_t)buflen; + + /* point to the payload */ + *out = &p[dataindex]; + + /* return the payload length */ + *olen = payloadsize; + + /* number of bytes "used" from the buffer */ + wsp->usedbuf = dataindex + payloadsize; + infof(data, "WS: received %zu bytes payload (%zu left)", + payloadsize, *oleft); + return CURLE_OK; } /* Curl_ws_writecb() is the write callback for websocket traffic. The @@ -724,150 +330,91 @@ static ssize_t ws_client_write(const unsigned char *buf, size_t buflen, size_t Curl_ws_writecb(char *buffer, size_t size /* 1 */, size_t nitems, void *userp) { - struct Curl_easy *data = userp; - + struct HTTP *ws = (struct HTTP *)userp; + struct Curl_easy *data = ws->ws.data; + void *writebody_ptr = data->set.out; if(data->set.ws_raw_mode) - return data->set.fwrite_func(buffer, size, nitems, data->set.out); + return data->set.fwrite_func(buffer, size, nitems, writebody_ptr); else if(nitems) { - struct websocket *ws; + unsigned char *frame = NULL; + size_t flen = 0; + size_t wrote = 0; CURLcode result; - - if(!data->conn || !data->conn->proto.ws) { - failf(data, "WS: not a websocket transfer"); - return nitems - 1; - } - ws = data->conn->proto.ws; - - if(buffer) { - ssize_t nwritten; - - nwritten = Curl_bufq_write(&ws->recvbuf, (const unsigned char *)buffer, - nitems, &result); - if(nwritten < 0) { - infof(data, "WS: error adding data to buffer %d", (int)result); - return nitems - 1; - } - buffer = NULL; - } - - while(!Curl_bufq_is_empty(&ws->recvbuf)) { - - result = ws_dec_pass(&ws->dec, data, &ws->recvbuf, - ws_client_write, data); + bool more; /* there's is more to parse in the buffer */ + curl_off_t oleft; + + decode: + more = FALSE; + oleft = ws->ws.frame.bytesleft; + if(!oleft) { + unsigned int recvflags; + result = ws_decode(data, (unsigned char *)buffer, nitems, + &frame, &flen, &oleft, &more, &recvflags); if(result == CURLE_AGAIN) - /* insufficient amount of data, keep it for later. - * we pretend to have written all since we have a copy */ + /* insufficient amount of data, keep it for later */ return nitems; else if(result) { infof(data, "WS: decode error %d", (int)result); return nitems - 1; } + /* Store details about the frame to be reachable with curl_ws_meta() + from within the write callback */ + ws->ws.frame.age = 0; + ws->ws.frame.offset = 0; + ws->ws.frame.flags = recvflags; + ws->ws.frame.bytesleft = oleft; } - } - return nitems; -} - -struct ws_collect { - struct Curl_easy *data; - void *buffer; - size_t buflen; - size_t bufidx; - int frame_age; - int frame_flags; - curl_off_t payload_offset; - curl_off_t payload_len; - bool written; -}; - -static ssize_t ws_client_collect(const unsigned char *buf, size_t buflen, - int frame_age, int frame_flags, - curl_off_t payload_offset, - curl_off_t payload_len, - void *userp, - CURLcode *err) -{ - struct ws_collect *ctx = userp; - size_t nwritten; - curl_off_t remain = (payload_len - (payload_offset + buflen)); - - if(!ctx->bufidx) { - /* first write */ - ctx->frame_age = frame_age; - ctx->frame_flags = frame_flags; - ctx->payload_offset = payload_offset; - ctx->payload_len = payload_len; - } - - if((frame_flags & CURLWS_PING) && !remain) { - /* auto-respond to PINGs, only works for single-frame payloads atm */ - size_t bytes; - infof(ctx->data, "WS: auto-respond to PING with a PONG"); - /* send back the exact same content as a PONG */ - *err = curl_ws_send(ctx->data, buf, buflen, &bytes, 0, CURLWS_PONG); - if(*err) - return -1; - nwritten = bytes; - } - else { - ctx->written = TRUE; - DEBUGASSERT(ctx->buflen >= ctx->bufidx); - nwritten = CURLMIN(buflen, ctx->buflen - ctx->bufidx); - if(!nwritten) { - if(!buflen) { /* 0 length write, we accept that */ - *err = CURLE_OK; - return 0; + else { + if(nitems > (size_t)ws->ws.frame.bytesleft) { + nitems = ws->ws.frame.bytesleft; + more = TRUE; } - *err = CURLE_AGAIN; /* no more space */ - return -1; + else + more = FALSE; + ws->ws.frame.offset += nitems; + ws->ws.frame.bytesleft -= nitems; + frame = (unsigned char *)buffer; + flen = nitems; + } + if((ws->ws.frame.flags & CURLWS_PING) && !oleft) { + /* auto-respond to PINGs, only works for single-frame payloads atm */ + size_t bytes; + infof(data, "WS: auto-respond to PING with a PONG"); + DEBUGASSERT(frame); + /* send back the exact same content as a PONG */ + result = curl_ws_send(data, frame, flen, &bytes, 0, CURLWS_PONG); + if(result) + return result; + } + else { + /* deliver the decoded frame to the user callback */ + Curl_set_in_callback(data, true); + wrote = data->set.fwrite_func((char *)frame, 1, flen, writebody_ptr); + Curl_set_in_callback(data, false); + if(wrote != flen) + return 0; + } + if(oleft) + ws->ws.frame.offset += flen; + /* the websocket frame has been delivered */ + ws_decode_clear(data); + if(more) { + /* there's more websocket data to deal with in the buffer */ + buffer = NULL; /* the buffer as been drained already */ + goto decode; } - *err = CURLE_OK; - memcpy(ctx->buffer, buf, nwritten); - ctx->bufidx += nwritten; } - return nwritten; + return nitems; } -static ssize_t nw_in_recv(void *reader_ctx, - unsigned char *buf, size_t buflen, - CURLcode *err) -{ - struct Curl_easy *data = reader_ctx; - size_t nread; - - *err = curl_easy_recv(data, buf, buflen, &nread); - if(*err) - return -1; - return (ssize_t)nread; -} CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer, size_t buflen, size_t *nread, - const struct curl_ws_frame **metap) + struct curl_ws_frame **metap) { - struct connectdata *conn = data->conn; - struct websocket *ws; - bool done = FALSE; /* not filled passed buffer yet */ - struct ws_collect ctx; + size_t bytes; CURLcode result; - - if(!conn) { - /* Unhappy hack with lifetimes of transfers and connection */ - if(!data->set.connect_only) { - failf(data, "CONNECT_ONLY is required"); - return CURLE_UNSUPPORTED_PROTOCOL; - } - - Curl_getconnectinfo(data, &conn); - if(!conn) { - failf(data, "connection not found"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - } - ws = conn->proto.ws; - if(!ws) { - failf(data, "connection is not setup for websocket"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } + struct websocket *wsp = &data->req.p.http->ws; *nread = 0; *metap = NULL; @@ -876,223 +423,301 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer, if(result) return result; - memset(&ctx, 0, sizeof(ctx)); - ctx.data = data; - ctx.buffer = buffer; - ctx.buflen = buflen; - - while(!done) { - /* receive more when our buffer is empty */ - if(Curl_bufq_is_empty(&ws->recvbuf)) { - ssize_t n = Curl_bufq_slurp(&ws->recvbuf, nw_in_recv, data, &result); - if(n < 0) { + do { + bool drain = FALSE; /* if there is pending buffered data to drain */ + char *inbuf = data->state.buffer; + bytes = wsp->stillbuffer; + if(!bytes) { + result = curl_easy_recv(data, data->state.buffer, + data->set.buffer_size, &bytes); + if(result) return result; + } + else { + /* the pending bytes can be found here */ + inbuf = wsp->stillb; + drain = TRUE; + } + if(bytes) { + unsigned char *out; + size_t olen; + bool more; + unsigned int recvflags; + curl_off_t oleft = wsp->frame.bytesleft; + + infof(data, "WS: got %u websocket bytes to decode", (int)bytes); + if(!oleft && !drain) { + result = ws_decode(data, (unsigned char *)inbuf, bytes, + &out, &olen, &oleft, &more, &recvflags); + if(result == CURLE_AGAIN) + /* a packet fragment only */ + break; + else if(result) + return result; + wsp->frame.offset = 0; + wsp->frame.bytesleft = oleft; + wsp->frame.flags = recvflags; } - else if(n == 0) { - /* connection closed */ - infof(data, "connection expectedly closed?"); - return CURLE_GOT_NOTHING; + else { + olen = oleft; + out = (unsigned char *)wsp->stillb; + recvflags = wsp->frame.flags; + if((curl_off_t)buflen < oleft) + /* there is still data left after this */ + wsp->frame.bytesleft -= buflen; + else + wsp->frame.bytesleft = 0; } - DEBUGF(infof(data, "curl_ws_recv, added %zu bytes from network", - Curl_bufq_len(&ws->recvbuf))); - } - result = ws_dec_pass(&ws->dec, data, &ws->recvbuf, - ws_client_collect, &ctx); - if(result == CURLE_AGAIN) { - if(!ctx.written) { - ws_dec_info(&ws->dec, data, "need more input"); - continue; /* nothing written, try more input */ + /* auto-respond to PINGs */ + if((recvflags & CURLWS_PING) && !oleft) { + infof(data, "WS: auto-respond to PING with a PONG"); + /* send back the exact same content as a PONG */ + result = curl_ws_send(data, out, olen, &bytes, 0, CURLWS_PONG); + if(result) + return result; + } + else { + if(olen < buflen) { + /* copy the payload to the user buffer */ + memcpy(buffer, out, olen); + *nread = olen; + if(!oleft) + /* websocket frame has been delivered */ + ws_decode_clear(data); + } + else { + /* copy a partial payload */ + memcpy(buffer, out, buflen); + *nread = buflen; + /* remember what is left and where */ + wsp->stillbuffer = olen - buflen; + wsp->stillb = (char *)buffer + buflen; + } + wsp->frame.offset += *nread; } - done = TRUE; - break; - } - else if(result) { - return result; - } - else if(ctx.written) { - /* The decoded frame is passed back to our caller. - * There are frames like PING were we auto-respond to and - * that we do not return. For these `ctx.written` is not set. */ - done = TRUE; - break; } - } - - /* update frame information to be passed back */ - update_meta(ws, ctx.frame_age, ctx.frame_flags, ctx.payload_offset, - ctx.payload_len, ctx.bufidx); - *metap = &ws->frame; - *nread = ws->frame.len; - /* infof(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %" - CURL_FORMAT_CURL_OFF_T ", %" CURL_FORMAT_CURL_OFF_T " left)", - buflen, *nread, ws->frame.offset, ws->frame.bytesleft); */ + else + *nread = bytes; + break; + } while(1); + *metap = &wsp->frame; return CURLE_OK; } -static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, - bool complete) +static void ws_xor(struct Curl_easy *data, + const unsigned char *source, + unsigned char *dest, + size_t len) { - if(!Curl_bufq_is_empty(&ws->sendbuf)) { - CURLcode result; - const unsigned char *out; - size_t outlen; - ssize_t n; + struct websocket *wsp = &data->req.p.http->ws; + size_t i; + /* append payload after the mask, XOR appropriately */ + for(i = 0; i < len; i++) { + dest[i] = source[i] ^ wsp->mask[wsp->xori]; + wsp->xori++; + wsp->xori &= 3; + } +} - while(Curl_bufq_peek(&ws->sendbuf, &out, &outlen)) { - if(data->set.connect_only) - result = Curl_senddata(data, out, outlen, &n); - else - result = Curl_write(data, data->conn->writesockfd, out, outlen, &n); - if(result) { - if(result == CURLE_AGAIN) { - if(!complete) { - infof(data, "WS: flush EAGAIN, %zu bytes remain in buffer", - Curl_bufq_len(&ws->sendbuf)); - return result; - } - /* TODO: the current design does not allow for buffered writes. - * We need to flush the buffer now. There is no ws_flush() later */ - n = 0; - continue; - } - else if(result) { - failf(data, "WS: flush, write error %d", result); - return result; - } - } - else { - infof(data, "WS: flushed %zu bytes", (size_t)n); - Curl_bufq_skip(&ws->sendbuf, (size_t)n); - } - } +/*** + RFC 6455 Section 5.2 + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-------+-+-------------+-------------------------------+ + |F|R|R|R| opcode|M| Payload len | Extended payload length | + |I|S|S|S| (4) |A| (7) | (16/64) | + |N|V|V|V| |S| | (if payload len==126/127) | + | |1|2|3| |K| | | + +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + + | Extended payload length continued, if payload len == 127 | + + - - - - - - - - - - - - - - - +-------------------------------+ + | |Masking-key, if MASK set to 1 | + +-------------------------------+-------------------------------+ + | Masking-key (continued) | Payload Data | + +-------------------------------- - - - - - - - - - - - - - - - + + : Payload Data continued ... : + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + | Payload Data continued ... | + +---------------------------------------------------------------+ +*/ + +static size_t ws_packethead(struct Curl_easy *data, + size_t len, unsigned int flags) +{ + struct HTTP *ws = data->req.p.http; + unsigned char *out = (unsigned char *)data->state.ulbuf; + unsigned char firstbyte = 0; + int outi; + unsigned char opcode; + if(flags & CURLWS_TEXT) { + opcode = WSBIT_OPCODE_TEXT; + infof(data, "WS: send OPCODE TEXT"); } - return CURLE_OK; + else if(flags & CURLWS_CLOSE) { + opcode = WSBIT_OPCODE_CLOSE; + infof(data, "WS: send OPCODE CLOSE"); + } + else if(flags & CURLWS_PING) { + opcode = WSBIT_OPCODE_PING; + infof(data, "WS: send OPCODE PING"); + } + else if(flags & CURLWS_PONG) { + opcode = WSBIT_OPCODE_PONG; + infof(data, "WS: send OPCODE PONG"); + } + else { + opcode = WSBIT_OPCODE_BIN; + infof(data, "WS: send OPCODE BINARY"); + } + + if(!(flags & CURLWS_CONT)) { + /* if not marked as continuing, assume this is the final fragment */ + firstbyte |= WSBIT_FIN | opcode; + ws->ws.contfragment = FALSE; + } + else if(ws->ws.contfragment) { + /* the previous fragment was not a final one and this isn't either, keep a + CONT opcode and no FIN bit */ + firstbyte |= WSBIT_OPCODE_CONT; + } + else { + ws->ws.contfragment = TRUE; + } + out[0] = firstbyte; + if(len > 65535) { + out[1] = 127 | WSBIT_MASK; + out[2] = (len >> 8) & 0xff; + out[3] = len & 0xff; + outi = 10; + } + else if(len > 126) { + out[1] = 126 | WSBIT_MASK; + out[2] = (len >> 8) & 0xff; + out[3] = len & 0xff; + outi = 4; + } + else { + out[1] = (unsigned char)len | WSBIT_MASK; + outi = 2; + } + + infof(data, "WS: send FIN bit %u (byte %02x)", + firstbyte & WSBIT_FIN ? 1 : 0, + firstbyte); + infof(data, "WS: send payload len %u", (int)len); + + /* 4 bytes mask */ + memcpy(&out[outi], &ws->ws.mask, 4); + + if(data->set.upload_buffer_size < (len + 10)) + return 0; + + /* pass over the mask */ + outi += 4; + + ws->ws.xori = 0; + /* return packet size */ + return outi; } -CURL_EXTERN CURLcode curl_ws_send(CURL *data, const void *buffer, +CURL_EXTERN CURLcode curl_ws_send(struct Curl_easy *data, const void *buffer, size_t buflen, size_t *sent, - curl_off_t fragsize, - unsigned int flags) + curl_off_t totalsize, + unsigned int sendflags) { - struct websocket *ws; - ssize_t nwritten, n; - size_t space; CURLcode result; + size_t headlen; + char *out; + ssize_t written; + struct websocket *wsp = &data->req.p.http->ws; - *sent = 0; - if(!data->conn && data->set.connect_only) { - result = Curl_connect_only_attach(data); + if(!data->set.ws_raw_mode) { + result = Curl_get_upload_buffer(data); if(result) return result; } - if(!data->conn) { - failf(data, "No associated connection"); - return CURLE_SEND_ERROR; - } - if(!data->conn->proto.ws) { - failf(data, "Not a websocket transfer"); - return CURLE_SEND_ERROR; + else { + if(totalsize || sendflags) + return CURLE_BAD_FUNCTION_ARGUMENT; } - ws = data->conn->proto.ws; if(data->set.ws_raw_mode) { - if(fragsize || flags) - return CURLE_BAD_FUNCTION_ARGUMENT; if(!buflen) /* nothing to do */ return CURLE_OK; /* raw mode sends exactly what was requested, and this is from within the write callback */ - if(Curl_is_in_callback(data)) { + if(Curl_is_in_callback(data)) result = Curl_write(data, data->conn->writesockfd, buffer, buflen, - &nwritten); - } + &written); else - result = Curl_senddata(data, buffer, buflen, &nwritten); + result = Curl_senddata(data, buffer, buflen, &written); infof(data, "WS: wanted to send %zu bytes, sent %zu bytes", - buflen, nwritten); - *sent = (nwritten >= 0)? (size_t)nwritten : 0; + buflen, written); + *sent = written; return result; } - /* Not RAW mode, buf we do the frame encoding */ - result = ws_flush(data, ws, FALSE); - if(result) - return result; - - /* TODO: the current design does not allow partial writes, afaict. - * It is not clear who the application is supposed to react. */ - space = Curl_bufq_space(&ws->sendbuf); - DEBUGF(infof(data, "curl_ws_send(len=%zu), sendbuf len=%zu space %zu", - buflen, Curl_bufq_len(&ws->sendbuf), space)); - if(space < 14) - return CURLE_AGAIN; + if(buflen > (data->set.upload_buffer_size - 10)) + /* don't do more than this in one go */ + buflen = data->set.upload_buffer_size - 10; - if(flags & CURLWS_OFFSET) { - if(fragsize) { - /* a frame series 'fragsize' bytes big, this is the first */ - n = ws_enc_write_head(data, &ws->enc, flags, fragsize, - &ws->sendbuf, &result); - if(n < 0) - return result; + if(sendflags & CURLWS_OFFSET) { + if(totalsize) { + /* a frame series 'totalsize' bytes big, this is the first */ + headlen = ws_packethead(data, totalsize, sendflags); + wsp->sleft = totalsize - buflen; } else { - if((curl_off_t)buflen > ws->enc.payload_remain) { - infof(data, "WS: unaligned frame size (sending %zu instead of %" - CURL_FORMAT_CURL_OFF_T ")", - buflen, ws->enc.payload_remain); + headlen = 0; + if((curl_off_t)buflen > wsp->sleft) { + infof(data, "WS: unaligned frame size (sending %zu instead of %zu)", + buflen, wsp->sleft); + wsp->sleft = 0; } + else + wsp->sleft -= buflen; } } - else if(!ws->enc.payload_remain) { - n = ws_enc_write_head(data, &ws->enc, flags, (curl_off_t)buflen, - &ws->sendbuf, &result); - if(n < 0) - return result; - } + else + headlen = ws_packethead(data, buflen, sendflags); - n = ws_enc_write_payload(&ws->enc, data, - buffer, buflen, &ws->sendbuf, &result); - if(n < 0) - return result; + /* headlen is the size of the frame header */ + out = data->state.ulbuf; + if(buflen) + /* for PING and PONG etc there might not be a payload */ + ws_xor(data, buffer, (unsigned char *)out + headlen, buflen); - *sent = (size_t)n; - return ws_flush(data, ws, TRUE); -} + if(data->set.connect_only) + result = Curl_senddata(data, out, buflen + headlen, &written); + else + result = Curl_write(data, data->conn->writesockfd, out, + buflen + headlen, &written); -static void ws_free(struct connectdata *conn) -{ - if(conn && conn->proto.ws) { - Curl_bufq_free(&conn->proto.ws->recvbuf); - Curl_bufq_free(&conn->proto.ws->sendbuf); - Curl_safefree(conn->proto.ws); - } -} + infof(data, "WS: wanted to send %zu bytes, sent %zu bytes", + headlen + buflen, written); + *sent = written; -void Curl_ws_done(struct Curl_easy *data) -{ - (void)data; + return result; } -CURLcode Curl_ws_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection) +void Curl_ws_done(struct Curl_easy *data) { - (void)data; - (void)dead_connection; - ws_free(conn); - return CURLE_OK; + struct websocket *wsp = &data->req.p.http->ws; + DEBUGASSERT(wsp); + Curl_dyn_free(&wsp->buf); } -CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data) +CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data) { /* we only return something for websocket, called from within the callback when not using raw mode */ - if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) && data->conn && - data->conn->proto.ws && !data->set.ws_raw_mode) - return &data->conn->proto.ws->frame; + if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) && data->req.p.http && + !data->set.ws_raw_mode) + return &data->req.p.http->ws.frame; return NULL; } @@ -1100,31 +725,31 @@ CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data) CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, size_t *nread, - const struct curl_ws_frame **metap) + struct curl_ws_frame **metap) { (void)curl; (void)buffer; (void)buflen; (void)nread; (void)metap; - return CURLE_NOT_BUILT_IN; + return CURLE_OK; } CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer, size_t buflen, size_t *sent, - curl_off_t fragsize, - unsigned int flags) + curl_off_t framesize, + unsigned int sendflags) { (void)curl; (void)buffer; (void)buflen; (void)sent; - (void)fragsize; - (void)flags; - return CURLE_NOT_BUILT_IN; + (void)framesize; + (void)sendflags; + return CURLE_OK; } -CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data) +CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data) { (void)data; return NULL; diff --git a/contrib/libs/curl/lib/ws.h b/contrib/libs/curl/lib/ws.h index 0308a42545..341242e50e 100644 --- a/contrib/libs/curl/lib/ws.h +++ b/contrib/libs/curl/lib/ws.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,57 +33,37 @@ #define REQTYPE struct dynbuf #endif -/* a client-side WS frame decoder, parsing frame headers and - * payload, keeping track of current position and stats */ -enum ws_dec_state { - WS_DEC_INIT, - WS_DEC_HEAD, - WS_DEC_PAYLOAD -}; - -struct ws_decoder { - int frame_age; /* zero */ - int frame_flags; /* See the CURLWS_* defines */ - curl_off_t payload_offset; /* the offset parsing is at */ - curl_off_t payload_len; - unsigned char head[10]; - int head_len, head_total; - enum ws_dec_state state; -}; +/* this is the largest single fragment size we support */ +#define MAX_WS_SIZE 65535 -/* a client-side WS frame encoder, generating frame headers and - * converting payloads, tracking remaining data in current frame */ -struct ws_encoder { - curl_off_t payload_len; /* payload length of current frame */ - curl_off_t payload_remain; /* remaining payload of current */ - unsigned int xori; /* xor index */ - unsigned char mask[4]; /* 32 bit mask for this connection */ - unsigned char firstbyte; /* first byte of frame we encode */ - bool contfragment; /* set TRUE if the previous fragment sent was not final */ -}; - -/* A websocket connection with en- and decoder that treat frames - * and keep track of boundaries. */ +/* part of 'struct HTTP', when used in the 'struct SingleRequest' in the + Curl_easy struct */ struct websocket { + bool contfragment; /* set TRUE if the previous fragment sent was not final */ + unsigned char mask[4]; /* 32 bit mask for this connection */ struct Curl_easy *data; /* used for write callback handling */ - struct ws_decoder dec; /* decode of we frames */ - struct ws_encoder enc; /* decode of we frames */ - struct bufq recvbuf; /* raw data from the server */ - struct bufq sendbuf; /* raw data to be sent to the server */ - struct curl_ws_frame frame; /* the current WS FRAME received */ + struct dynbuf buf; + size_t usedbuf; /* number of leading bytes in 'buf' the most recent complete + websocket frame uses */ + struct curl_ws_frame frame; /* the struct used for frame state */ + curl_off_t oleft; /* outstanding number of payload bytes left from the + server */ + curl_off_t stillbuffer; /* number of bytes left in the buffer to deliver in + the next curl_ws_recv() call */ + char *stillb; /* the stillbuffer pending bytes are here */ + curl_off_t sleft; /* outstanding number of payload bytes left to send */ + unsigned int xori; /* xor index */ }; CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req); -CURLcode Curl_ws_accept(struct Curl_easy *data, const char *mem, size_t len); +CURLcode Curl_ws_accept(struct Curl_easy *data); + size_t Curl_ws_writecb(char *buffer, size_t size, size_t nitems, void *userp); void Curl_ws_done(struct Curl_easy *data); -CURLcode Curl_ws_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection); + #else #define Curl_ws_request(x,y) CURLE_OK #define Curl_ws_done(x) Curl_nop_stmt -#define Curl_ws_free(x) Curl_nop_stmt #endif #endif /* HEADER_CURL_WS_H */ diff --git a/contrib/libs/curl/src/slist_wc.c b/contrib/libs/curl/src/slist_wc.c index 7f1e8f19be..68021e61e0 100644 --- a/contrib/libs/curl/src/slist_wc.c +++ b/contrib/libs/curl/src/slist_wc.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/slist_wc.h b/contrib/libs/curl/src/slist_wc.h index dd7b8c10a7..e309fd5a36 100644 --- a/contrib/libs/curl/src/slist_wc.h +++ b/contrib/libs/curl/src/slist_wc.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_binmode.c b/contrib/libs/curl/src/tool_binmode.c index e27ce9663c..68c6c36716 100644 --- a/contrib/libs/curl/src/tool_binmode.c +++ b/contrib/libs/curl/src/tool_binmode.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_binmode.h b/contrib/libs/curl/src/tool_binmode.h index bee837b000..0b3d24b389 100644 --- a/contrib/libs/curl/src/tool_binmode.h +++ b/contrib/libs/curl/src/tool_binmode.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_bname.c b/contrib/libs/curl/src/tool_bname.c index 4ba1a3b8ee..e70f7d76ff 100644 --- a/contrib/libs/curl/src/tool_bname.c +++ b/contrib/libs/curl/src/tool_bname.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_bname.h b/contrib/libs/curl/src/tool_bname.h index d091c2231a..0efd7f19cb 100644 --- a/contrib/libs/curl/src/tool_bname.h +++ b/contrib/libs/curl/src/tool_bname.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_cb_dbg.c b/contrib/libs/curl/src/tool_cb_dbg.c index d53a43978d..c1dba85ab8 100644 --- a/contrib/libs/curl/src/tool_cb_dbg.c +++ b/contrib/libs/curl/src/tool_cb_dbg.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -34,102 +34,45 @@ #include "memdebug.h" /* keep this as LAST include */ -static void dump(const char *timebuf, const char *idsbuf, const char *text, +static void dump(const char *timebuf, const char *text, FILE *stream, const unsigned char *ptr, size_t size, trace tracetype, curl_infotype infotype); /* - * Return the formatted HH:MM:SS for the tv_sec given. - * NOT thread safe. - */ -static const char *hms_for_sec(time_t tv_sec) -{ - static time_t cached_tv_sec; - static char hms_buf[12]; - static time_t epoch_offset; - static int known_epoch; - - if(tv_sec != cached_tv_sec) { - struct tm *now; - time_t secs; - /* recalculate */ - if(!known_epoch) { - epoch_offset = time(NULL) - tv_sec; - known_epoch = 1; - } - secs = epoch_offset + tv_sec; - /* !checksrc! disable BANNEDFUNC 1 */ - now = localtime(&secs); /* not thread safe but we don't care */ - msnprintf(hms_buf, sizeof(hms_buf), "%02d:%02d:%02d", - now->tm_hour, now->tm_min, now->tm_sec); - cached_tv_sec = tv_sec; - } - return hms_buf; -} - -static void log_line_start(FILE *log, const char *timebuf, - const char *idsbuf, curl_infotype type) -{ - /* - * This is the trace look that is similar to what libcurl makes on its - * own. - */ - static const char * const s_infotype[] = { - "* ", "< ", "> ", "{ ", "} ", "{ ", "} " - }; - if((timebuf && *timebuf) || (idsbuf && *idsbuf)) - fprintf(log, "%s%s%s", timebuf, idsbuf, s_infotype[type]); - else - fputs(s_infotype[type], log); -} - -#define TRC_IDS_FORMAT_IDS_1 "[%" CURL_FORMAT_CURL_OFF_T "-x] " -#define TRC_IDS_FORMAT_IDS_2 "[%" CURL_FORMAT_CURL_OFF_T "-%" \ - CURL_FORMAT_CURL_OFF_T "] " -/* ** callback for CURLOPT_DEBUGFUNCTION */ + int tool_debug_cb(CURL *handle, curl_infotype type, char *data, size_t size, void *userdata) { struct OperationConfig *operation = userdata; struct GlobalConfig *config = operation->global; - FILE *output = stderr; + FILE *output = config->errors; const char *text; struct timeval tv; char timebuf[20]; - /* largest signed 64bit is: 9,223,372,036,854,775,807 - * max length in decimal: 1 + (6*3) = 19 - * formatted via TRC_IDS_FORMAT_IDS_2 this becomes 2 + 19 + 1 + 19 + 2 = 43 - * negative xfer-id are not printed, negative conn-ids use TRC_IDS_FORMAT_1 - */ - char idsbuf[60]; - curl_off_t xfer_id, conn_id; + time_t secs; (void)handle; /* not used */ if(config->tracetime) { + struct tm *now; + static time_t epoch_offset; + static int known_offset; tv = tvnow(); - msnprintf(timebuf, sizeof(timebuf), "%s.%06ld ", - hms_for_sec(tv.tv_sec), (long)tv.tv_usec); - } - else - timebuf[0] = 0; - - if(handle && config->traceids && - !curl_easy_getinfo(handle, CURLINFO_XFER_ID, &xfer_id) && xfer_id >= 0) { - if(!curl_easy_getinfo(handle, CURLINFO_CONN_ID, &conn_id) && - conn_id >= 0) { - msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_2, - xfer_id, conn_id); - } - else { - msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_1, xfer_id); + if(!known_offset) { + epoch_offset = time(NULL) - tv.tv_sec; + known_offset = 1; } + secs = epoch_offset + tv.tv_sec; + /* !checksrc! disable BANNEDFUNC 1 */ + now = localtime(&secs); /* not thread safe but we don't care */ + msnprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d.%06ld ", + now->tm_hour, now->tm_min, now->tm_sec, (long)tv.tv_usec); } else - idsbuf[0] = 0; + timebuf[0] = 0; if(!config->trace_stream) { /* open for append */ @@ -137,7 +80,7 @@ int tool_debug_cb(CURL *handle, curl_infotype type, config->trace_stream = stdout; else if(!strcmp("%", config->trace_dump)) /* Ok, this is somewhat hackish but we do it undocumented for now */ - config->trace_stream = stderr; + config->trace_stream = config->errors; /* aka stderr */ else { config->trace_stream = fopen(config->trace_dump, FOPEN_WRITETEXT); config->trace_fopened = TRUE; @@ -153,6 +96,13 @@ int tool_debug_cb(CURL *handle, curl_infotype type, } if(config->tracetype == TRACE_PLAIN) { + /* + * This is the trace look that is similar to what libcurl makes on its + * own. + */ + static const char * const s_infotype[] = { + "*", "<", ">", "{", "}", "{", "}" + }; static bool newl = FALSE; static bool traced_data = FALSE; @@ -164,7 +114,7 @@ int tool_debug_cb(CURL *handle, curl_infotype type, for(i = 0; i < size - 1; i++) { if(data[i] == '\n') { /* LF */ if(!newl) { - log_line_start(output, timebuf, idsbuf, type); + fprintf(output, "%s%s ", timebuf, s_infotype[type]); } (void)fwrite(data + st, i - st + 1, 1, output); st = i + 1; @@ -172,7 +122,7 @@ int tool_debug_cb(CURL *handle, curl_infotype type, } } if(!newl) - log_line_start(output, timebuf, idsbuf, type); + fprintf(output, "%s%s ", timebuf, s_infotype[type]); (void)fwrite(data + st, i - st + 1, 1, output); } newl = (size && (data[size - 1] != '\n')) ? TRUE : FALSE; @@ -181,7 +131,7 @@ int tool_debug_cb(CURL *handle, curl_infotype type, case CURLINFO_TEXT: case CURLINFO_HEADER_IN: if(!newl) - log_line_start(output, timebuf, idsbuf, type); + fprintf(output, "%s%s ", timebuf, s_infotype[type]); (void)fwrite(data, size, 1, output); newl = (size && (data[size - 1] != '\n')) ? TRUE : FALSE; traced_data = FALSE; @@ -197,7 +147,7 @@ int tool_debug_cb(CURL *handle, curl_infotype type, function */ if(!config->isatty || ((output != stderr) && (output != stdout))) { if(!newl) - log_line_start(output, timebuf, idsbuf, type); + fprintf(output, "%s%s ", timebuf, s_infotype[type]); fprintf(output, "[%zu bytes data]\n", size); newl = FALSE; traced_data = TRUE; @@ -215,7 +165,7 @@ int tool_debug_cb(CURL *handle, curl_infotype type, switch(type) { case CURLINFO_TEXT: - fprintf(output, "%s%s== Info: %.*s", timebuf, idsbuf, (int)size, data); + fprintf(output, "%s== Info: %.*s", timebuf, (int)size, data); /* FALLTHROUGH */ default: /* in case a new one is introduced to shock us */ return 0; @@ -240,12 +190,12 @@ int tool_debug_cb(CURL *handle, curl_infotype type, break; } - dump(timebuf, idsbuf, text, output, (unsigned char *) data, size, - config->tracetype, type); + dump(timebuf, text, output, (unsigned char *) data, size, config->tracetype, + type); return 0; } -static void dump(const char *timebuf, const char *idsbuf, const char *text, +static void dump(const char *timebuf, const char *text, FILE *stream, const unsigned char *ptr, size_t size, trace tracetype, curl_infotype infotype) { @@ -258,8 +208,7 @@ static void dump(const char *timebuf, const char *idsbuf, const char *text, /* without the hex output, we can fit more on screen */ width = 0x40; - fprintf(stream, "%s%s%s, %zu bytes (0x%zx)\n", timebuf, idsbuf, - text, size, size); + fprintf(stream, "%s%s, %zu bytes (0x%zx)\n", timebuf, text, size, size); for(i = 0; i < size; i += width) { diff --git a/contrib/libs/curl/src/tool_cb_dbg.h b/contrib/libs/curl/src/tool_cb_dbg.h index d78afb36ea..bc69e56604 100644 --- a/contrib/libs/curl/src/tool_cb_dbg.h +++ b/contrib/libs/curl/src/tool_cb_dbg.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_cb_hdr.c b/contrib/libs/curl/src/tool_cb_hdr.c index dc6069f1ab..23700de222 100644 --- a/contrib/libs/curl/src/tool_cb_hdr.c +++ b/contrib/libs/curl/src/tool_cb_hdr.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -77,13 +77,22 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata) const char *end = (char *)ptr + cb; const char *scheme = NULL; + /* + * Once that libcurl has called back tool_header_cb() the returned value + * is checked against the amount that was intended to be written, if + * it does not match then it fails with CURLE_WRITE_ERROR. So at this + * point returning a value different from sz*nmemb indicates failure. + */ + size_t failure = (size && nmemb) ? 0 : 1; + if(!per->config) - return CURL_WRITEFUNC_ERROR; + return failure; #ifdef DEBUGBUILD if(size * nmemb > (size_t)CURL_MAX_HTTP_HEADER) { - warnf(per->config->global, "Header data exceeds single call write limit"); - return CURL_WRITEFUNC_ERROR; + warnf(per->config->global, "Header data exceeds single call write " + "limit!\n"); + return failure; } #endif @@ -166,7 +175,7 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata) if(outs->stream) { /* indication of problem, get out! */ free(filename); - return CURL_WRITEFUNC_ERROR; + return failure; } outs->is_cd_filename = TRUE; @@ -176,12 +185,12 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata) outs->alloc_filename = TRUE; hdrcbdata->honor_cd_filename = FALSE; /* done now! */ if(!tool_create_output_file(outs, per->config)) - return CURL_WRITEFUNC_ERROR; + return failure; } break; } if(!outs->stream && !tool_create_output_file(outs, per->config)) - return CURL_WRITEFUNC_ERROR; + return failure; } if(hdrcbdata->config->writeout) { char *value = memchr(ptr, ':', cb); @@ -201,7 +210,7 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata) char *value = NULL; if(!outs->stream && !tool_create_output_file(outs, per->config)) - return CURL_WRITEFUNC_ERROR; + return failure; if(hdrcbdata->global->isatty && hdrcbdata->global->styled_output) value = memchr(ptr, ':', cb); @@ -343,15 +352,6 @@ void write_linked_location(CURL *curl, const char *location, size_t loclen, char *copyloc = NULL, *locurl = NULL, *scheme = NULL, *finalurl = NULL; const char *loc = location; size_t llen = loclen; - char *vver = getenv("VTE_VERSION"); - - if(vver) { - long vvn = strtol(vver, NULL, 10); - /* Skip formatting for old versions of VTE <= 0.48.1 (Mar 2017) since some - of those versions have formatting bugs. (#10428) */ - if(0 < vvn && vvn <= 4801) - goto locout; - } /* Strip leading whitespace of the redirect URL */ while(llen && *loc == ' ') { diff --git a/contrib/libs/curl/src/tool_cb_hdr.h b/contrib/libs/curl/src/tool_cb_hdr.h index a855052d0d..01175bb6ab 100644 --- a/contrib/libs/curl/src/tool_cb_hdr.h +++ b/contrib/libs/curl/src/tool_cb_hdr.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_cb_prg.c b/contrib/libs/curl/src/tool_cb_prg.c index 9c8ffb2b5a..3532c31bc1 100644 --- a/contrib/libs/curl/src/tool_cb_prg.c +++ b/contrib/libs/curl/src/tool_cb_prg.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -116,8 +116,8 @@ static void fly(struct ProgressData *bar, bool moved) #define MAX_BARLENGTH 256 -#if (SIZEOF_CURL_OFF_T < 8) -#error "too small curl_off_t" +#if (SIZEOF_CURL_OFF_T == 4) +# define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFF) #else /* assume SIZEOF_CURL_OFF_T == 8 */ # define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFFFFFFFFFF) @@ -274,7 +274,7 @@ void progressbarinit(struct ProgressData *bar, else if(bar->width > MAX_BARLENGTH) bar->width = MAX_BARLENGTH; - bar->out = stderr; + bar->out = config->global->errors; bar->tick = 150; bar->barmove = 1; } diff --git a/contrib/libs/curl/src/tool_cb_prg.h b/contrib/libs/curl/src/tool_cb_prg.h index 565ad565a9..7d8fbae8f9 100644 --- a/contrib/libs/curl/src/tool_cb_prg.h +++ b/contrib/libs/curl/src/tool_cb_prg.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_cb_rea.c b/contrib/libs/curl/src/tool_cb_rea.c index d70a9b9091..4aed55c3aa 100644 --- a/contrib/libs/curl/src/tool_cb_rea.c +++ b/contrib/libs/curl/src/tool_cb_rea.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -23,10 +23,6 @@ ***************************************************************************/ #include "tool_setup.h" -#ifdef HAVE_SYS_SELECT_H -#include <sys/select.h> -#endif - #define ENABLE_CURLX_PRINTF /* use our own printf() functions */ #include "curlx.h" @@ -34,8 +30,6 @@ #include "tool_cfgable.h" #include "tool_cb_rea.h" #include "tool_operate.h" -#include "tool_util.h" -#include "tool_msgs.h" #include "memdebug.h" /* keep this as LAST include */ @@ -45,65 +39,20 @@ size_t tool_read_cb(char *buffer, size_t sz, size_t nmemb, void *userdata) { - ssize_t rc = 0; - struct per_transfer *per = userdata; - struct OperationConfig *config = per->config; - - if((per->uploadfilesize != -1) && - (per->uploadedsofar == per->uploadfilesize)) { - /* done */ - return 0; - } - - if(config->timeout_ms) { - struct timeval now = tvnow(); - long msdelta = tvdiff(now, per->start); - - if(msdelta > config->timeout_ms) - /* timeout */ - return 0; -#ifndef WIN32 - /* this logic waits on read activity on a file descriptor that is not a - socket which makes it not work with select() on Windows */ - else { - fd_set bits; - struct timeval timeout; - long wait = config->timeout_ms - msdelta; + ssize_t rc; + struct InStruct *in = userdata; - /* wait this long at the most */ - timeout.tv_sec = wait/1000; - timeout.tv_usec = (int)((wait%1000)*1000); - - FD_ZERO(&bits); - FD_SET(per->infd, &bits); - if(!select(per->infd + 1, &bits, NULL, NULL, &timeout)) - return 0; /* timeout */ - } -#endif - } - - rc = read(per->infd, buffer, sz*nmemb); + rc = read(in->fd, buffer, sz*nmemb); if(rc < 0) { if(errno == EAGAIN) { errno = 0; - config->readbusy = TRUE; + in->config->readbusy = TRUE; return CURL_READFUNC_PAUSE; } /* since size_t is unsigned we can't return negative values fine */ rc = 0; } - if((per->uploadfilesize != -1) && - (per->uploadedsofar + rc > per->uploadfilesize)) { - /* do not allow uploading more than originally set out to do */ - curl_off_t delta = per->uploadedsofar + rc - per->uploadfilesize; - warnf(per->config->global, "File size larger in the end than when " - "started. Dropping at least %" CURL_FORMAT_CURL_OFF_T " bytes", - delta); - rc = (ssize_t)(per->uploadfilesize - per->uploadedsofar); - } - config->readbusy = FALSE; - - /* when select() returned zero here, it timed out */ + in->config->readbusy = FALSE; return (size_t)rc; } diff --git a/contrib/libs/curl/src/tool_cb_rea.h b/contrib/libs/curl/src/tool_cb_rea.h index 06899d3ef3..81b5f871a5 100644 --- a/contrib/libs/curl/src/tool_cb_rea.h +++ b/contrib/libs/curl/src/tool_cb_rea.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_cb_see.c b/contrib/libs/curl/src/tool_cb_see.c index 8351473c82..d24d526518 100644 --- a/contrib/libs/curl/src/tool_cb_see.c +++ b/contrib/libs/curl/src/tool_cb_see.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -28,7 +28,6 @@ #include "curlx.h" #include "tool_cfgable.h" -#include "tool_operate.h" #include "tool_cb_see.h" #include "memdebug.h" /* keep this as LAST include */ @@ -49,7 +48,7 @@ int tool_seek_cb(void *userdata, curl_off_t offset, int whence) { - struct per_transfer *per = userdata; + struct InStruct *in = userdata; #if(SIZEOF_CURL_OFF_T > SIZEOF_OFF_T) && !defined(USE_WIN32_LARGE_FILES) @@ -67,13 +66,13 @@ int tool_seek_cb(void *userdata, curl_off_t offset, int whence) /* this code path doesn't support other types */ return CURL_SEEKFUNC_FAIL; - if(LSEEK_ERROR == lseek(per->infd, 0, SEEK_SET)) + if(LSEEK_ERROR == lseek(in->fd, 0, SEEK_SET)) /* couldn't rewind to beginning */ return CURL_SEEKFUNC_FAIL; while(left) { long step = (left > OUR_MAX_SEEK_O) ? OUR_MAX_SEEK_L : (long)left; - if(LSEEK_ERROR == lseek(per->infd, step, SEEK_CUR)) + if(LSEEK_ERROR == lseek(in->fd, step, SEEK_CUR)) /* couldn't seek forwards the desired amount */ return CURL_SEEKFUNC_FAIL; left -= step; @@ -82,7 +81,7 @@ int tool_seek_cb(void *userdata, curl_off_t offset, int whence) } #endif - if(LSEEK_ERROR == lseek(per->infd, offset, whence)) + if(LSEEK_ERROR == lseek(in->fd, offset, whence)) /* couldn't rewind, the reason is in errno but errno is just not portable enough and we don't actually care that much why we failed. We'll let libcurl know that it may try other means if it wants to. */ diff --git a/contrib/libs/curl/src/tool_cb_see.h b/contrib/libs/curl/src/tool_cb_see.h index 14bbc4264f..4af0b0ab40 100644 --- a/contrib/libs/curl/src/tool_cb_see.h +++ b/contrib/libs/curl/src/tool_cb_see.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_cb_wrt.c b/contrib/libs/curl/src/tool_cb_wrt.c index 94d82cb84a..c9d1dbd1d2 100644 --- a/contrib/libs/curl/src/tool_cb_wrt.c +++ b/contrib/libs/curl/src/tool_cb_wrt.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -62,14 +62,14 @@ bool tool_create_output_file(struct OutStruct *outs, DEBUGASSERT(config); global = config->global; if(!fname || !*fname) { - warnf(global, "Remote filename has no length"); + warnf(global, "Remote filename has no length!\n"); return FALSE; } if(config->output_dir && outs->is_cd_filename) { aname = aprintf("%s/%s", config->output_dir, fname); if(!aname) { - errorf(global, "out of memory"); + errorf(global, "out of memory\n"); return FALSE; } fname = aname; @@ -95,12 +95,12 @@ bool tool_create_output_file(struct OutStruct *outs, /* Guard against wraparound in new filename */ if(newlen < len) { free(aname); - errorf(global, "overflow in filename generation"); + errorf(global, "overflow in filename generation\n"); return FALSE; } newname = malloc(newlen); if(!newname) { - errorf(global, "out of memory"); + errorf(global, "out of memory\n"); free(aname); return FALSE; } @@ -133,7 +133,7 @@ bool tool_create_output_file(struct OutStruct *outs, } if(!file) { - warnf(global, "Failed to open the file %s: %s", fname, + warnf(global, "Failed to open the file %s: %s\n", fname, strerror(errno)); free(aname); return FALSE; @@ -164,6 +164,14 @@ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata) intptr_t fhnd; #endif + /* + * Once that libcurl has called back tool_write_cb() the returned value + * is checked against the amount that was intended to be written, if + * it does not match then it fails with CURLE_WRITE_ERROR. So at this + * point returning a value different from sz*nmemb indicates failure. + */ + const size_t failure = bytes ? 0 : 1; + #ifdef DEBUGBUILD { char *tty = curlx_getenv("CURL_ISATTY"); @@ -176,14 +184,14 @@ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata) if(config->show_headers) { if(bytes > (size_t)CURL_MAX_HTTP_HEADER) { warnf(config->global, "Header data size exceeds single call write " - "limit"); - return CURL_WRITEFUNC_ERROR; + "limit!\n"); + return failure; } } else { if(bytes > (size_t)CURL_MAX_WRITE_SIZE) { - warnf(config->global, "Data size exceeds single call write limit"); - return CURL_WRITEFUNC_ERROR; + warnf(config->global, "Data size exceeds single call write limit!\n"); + return failure; } } @@ -211,23 +219,23 @@ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata) check_fails = TRUE; } if(check_fails) { - warnf(config->global, "Invalid output struct data for write callback"); - return CURL_WRITEFUNC_ERROR; + warnf(config->global, "Invalid output struct data for write callback\n"); + return failure; } } #endif if(!outs->stream && !tool_create_output_file(outs, per->config)) - return CURL_WRITEFUNC_ERROR; + return failure; if(is_tty && (outs->bytes < 2000) && !config->terminal_binary_ok) { /* binary output to terminal? */ if(memchr(buffer, 0, bytes)) { warnf(config->global, "Binary output can mess up your terminal. " "Use \"--output -\" to tell curl to output it to your terminal " - "anyway, or consider \"--output <FILE>\" to save to a file."); + "anyway, or consider \"--output <FILE>\" to save to a file.\n"); config->synthetic_error = TRUE; - return CURL_WRITEFUNC_ERROR; + return failure; } } @@ -243,13 +251,13 @@ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata) wc_len = MultiByteToWideChar(CP_UTF8, 0, buffer, in_len, NULL, 0); wc_buf = (wchar_t*) malloc(wc_len * sizeof(wchar_t)); if(!wc_buf) - return CURL_WRITEFUNC_ERROR; + return failure; /* calculate buffer size for multi-byte characters */ wc_len = MultiByteToWideChar(CP_UTF8, 0, buffer, in_len, wc_buf, wc_len); if(!wc_len) { free(wc_buf); - return CURL_WRITEFUNC_ERROR; + return failure; } if(!WriteConsoleW( @@ -259,7 +267,7 @@ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata) &wc_len, NULL)) { free(wc_buf); - return CURL_WRITEFUNC_ERROR; + return failure; } free(wc_buf); rc = bytes; @@ -281,7 +289,7 @@ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata) /* output buffering disabled */ int res = fflush(outs->stream); if(res) - return CURL_WRITEFUNC_ERROR; + return failure; } return rc; diff --git a/contrib/libs/curl/src/tool_cb_wrt.h b/contrib/libs/curl/src/tool_cb_wrt.h index 55502f440d..0cbbceefe6 100644 --- a/contrib/libs/curl/src/tool_cb_wrt.h +++ b/contrib/libs/curl/src/tool_cb_wrt.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_cfgable.c b/contrib/libs/curl/src/tool_cfgable.c index 04f0b05f44..eccb3bcb59 100644 --- a/contrib/libs/curl/src/tool_cfgable.c +++ b/contrib/libs/curl/src/tool_cfgable.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,7 +24,6 @@ #include "tool_setup.h" #include "tool_cfgable.h" -#include "tool_formparse.h" #include "tool_main.h" #include "memdebug.h" /* keep this as LAST include */ @@ -54,13 +53,11 @@ static void free_config_fields(struct OperationConfig *config) Curl_safefree(config->useragent); Curl_safefree(config->altsvc); Curl_safefree(config->hsts); - Curl_safefree(config->haproxy_clientip); curl_slist_free_all(config->cookies); Curl_safefree(config->cookiejar); curl_slist_free_all(config->cookiefiles); Curl_safefree(config->postfields); - Curl_safefree(config->query); Curl_safefree(config->referer); Curl_safefree(config->headerfile); @@ -92,8 +89,6 @@ static void free_config_fields(struct OperationConfig *config) Curl_safefree(config->netrc_file); Curl_safefree(config->output_dir); - Curl_safefree(config->proto_str); - Curl_safefree(config->proto_redir_str); urlnode = config->url_list; while(urlnode) { @@ -137,12 +132,13 @@ static void free_config_fields(struct OperationConfig *config) Curl_safefree(config->engine); Curl_safefree(config->etag_save_file); Curl_safefree(config->etag_compare_file); - Curl_safefree(config->ssl_ec_curves); Curl_safefree(config->request_target); Curl_safefree(config->customrequest); Curl_safefree(config->krblevel); + Curl_safefree(config->oauth_bearer); Curl_safefree(config->sasl_authzid); + Curl_safefree(config->unix_socket_path); Curl_safefree(config->writeout); Curl_safefree(config->proto_default); diff --git a/contrib/libs/curl/src/tool_cfgable.h b/contrib/libs/curl/src/tool_cfgable.h index b35abaf7a7..c26cddd5e0 100644 --- a/contrib/libs/curl/src/tool_cfgable.h +++ b/contrib/libs/curl/src/tool_cfgable.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -26,6 +26,7 @@ #include "tool_setup.h" #include "tool_sdecls.h" #include "tool_urlglob.h" +#include "tool_formparse.h" struct GlobalConfig; @@ -36,11 +37,11 @@ struct State { char *outfiles; char *httpgetfields; char *uploadfile; - curl_off_t infilenum; /* number of files to upload */ - curl_off_t up; /* upload file counter within a single upload glob */ - curl_off_t urlnum; /* how many iterations this single URL has with ranges + unsigned long infilenum; /* number of files to upload */ + unsigned long up; /* upload file counter within a single upload glob */ + unsigned long urlnum; /* how many iterations this single URL has with ranges etc */ - curl_off_t li; + unsigned long li; }; struct OperationConfig { @@ -69,9 +70,8 @@ struct OperationConfig { char *postfields; curl_off_t postfieldsize; char *referer; - char *query; - long timeout_ms; - long connecttimeout_ms; + double timeout; + double connecttimeout; long maxredirs; curl_off_t max_filesize; char *output_dir; @@ -117,7 +117,7 @@ struct OperationConfig { bool dirlistonly; /* only get the FTP dir list */ bool followlocation; /* follow http redirects */ bool unrestricted_auth; /* Continue to send authentication (user+password) - when following redirects, even when hostname + when following ocations, even when hostname changed */ bool netrc_opt; bool netrc; @@ -215,7 +215,7 @@ struct OperationConfig { bool ftp_ssl_ccc; int ftp_ssl_ccc_mode; char *preproxy; - bool socks5_gssapi_nec; /* The NEC reference server does not protect the + int socks5_gssapi_nec; /* The NEC reference server does not protect the encryption type exchange */ unsigned long socks5_auth;/* auth bitmask for socks5 proxies */ char *proxy_service_name; /* set authentication service name for HTTP and @@ -254,13 +254,15 @@ struct OperationConfig { bool xattr; /* store metadata in extended attributes */ long gssapi_delegation; bool ssl_allow_beast; /* allow this SSL vulnerability */ - bool proxy_ssl_allow_beast; /* allow this SSL vulnerability for proxy */ + bool proxy_ssl_allow_beast; /* allow this SSL vulnerability for proxy*/ + bool ssl_no_revoke; /* disable SSL certificate revocation checks */ + /*bool proxy_ssl_no_revoke; */ + bool ssl_revoke_best_effort; /* ignore SSL revocation offline/missing revocation list errors */ - bool native_ca_store; /* use the native OS CA store */ - bool proxy_native_ca_store; /* use the native OS CA store for proxy */ + bool native_ca_store; /* use the native os ca store */ bool ssl_auto_client_cert; /* automatically locate and use a client certificate for authentication (Schannel) */ bool proxy_ssl_auto_client_cert; /* proxy version of ssl_auto_client_cert */ @@ -270,7 +272,7 @@ struct OperationConfig { bool abstract_unix_socket; /* path to an abstract Unix domain socket */ bool falsestart; bool path_as_is; - long expect100timeout_ms; + double expect100timeout; bool suppress_connect_headers; /* suppress proxy CONNECT response headers from user callbacks */ bool synthetic_error; /* if TRUE, this is tool-internal error */ @@ -278,7 +280,6 @@ struct OperationConfig { long happy_eyeballs_timeout_ms; /* happy eyeballs timeout in milliseconds. 0 is valid. default: CURL_HET_DEFAULT. */ bool haproxy_protocol; /* whether to send HAProxy protocol v1 */ - char *haproxy_clientip; /* client IP for HAProxy protocol */ bool disallow_username_in_url; /* disallow usernames in URLs */ char *aws_sigv4; enum { @@ -299,16 +300,19 @@ struct OperationConfig { }; struct GlobalConfig { - bool showerror; /* show errors when silent */ - bool silent; /* don't show messages, --silent given */ - bool noprogress; /* don't show progress bar */ + int showerror; /* -1 == unset, default => show errors + 0 => -s is used to NOT show errors + 1 => -S has been used to show errors */ + bool mute; /* don't show messages, --silent given */ + bool noprogress; /* don't show progress bar --silent given */ bool isatty; /* Updated internally if output is a tty */ + FILE *errors; /* Error stream, defaults to stderr */ + bool errors_fopened; /* Whether error stream isn't stderr */ char *trace_dump; /* file to dump the network trace to */ FILE *trace_stream; bool trace_fopened; trace tracetype; bool tracetime; /* include timestamp? */ - bool traceids; /* include xfer-/conn-id? */ int progressmode; /* CURL_PROGRESS_BAR / CURL_PROGRESS_STATS */ char *libcurl; /* Output libcurl code to this file name */ bool fail_early; /* exit on first transfer error */ @@ -319,7 +323,7 @@ struct GlobalConfig { bool test_event_based; #endif bool parallel; - unsigned short parallel_max; /* MAX_PARALLEL is the maximum */ + long parallel_max; bool parallel_connect; char *help_category; /* The help category, if set */ struct OperationConfig *first; diff --git a/contrib/libs/curl/src/tool_dirhie.c b/contrib/libs/curl/src/tool_dirhie.c index c01c19cfb2..1b6ac17480 100644 --- a/contrib/libs/curl/src/tool_dirhie.c +++ b/contrib/libs/curl/src/tool_dirhie.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -34,10 +34,15 @@ #include "curlx.h" #include "tool_dirhie.h" -#include "tool_msgs.h" #include "memdebug.h" /* keep this as LAST include */ +#ifdef NETWARE +# ifndef __NOVELL_LIBC__ +# define mkdir mkdir_510 +# endif +#endif + #if defined(WIN32) || (defined(MSDOS) && !defined(__DJGPP__)) # define mkdir(x,y) (mkdir)((x)) # ifndef F_OK @@ -45,38 +50,38 @@ # endif #endif -static void show_dir_errno(struct GlobalConfig *global, const char *name) +static void show_dir_errno(FILE *errors, const char *name) { switch(errno) { #ifdef EACCES case EACCES: - errorf(global, "You don't have permission to create %s", name); + fprintf(errors, "You don't have permission to create %s.\n", name); break; #endif #ifdef ENAMETOOLONG case ENAMETOOLONG: - errorf(global, "The directory name %s is too long", name); + fprintf(errors, "The directory name %s is too long.\n", name); break; #endif #ifdef EROFS case EROFS: - errorf(global, "%s resides on a read-only file system", name); + fprintf(errors, "%s resides on a read-only file system.\n", name); break; #endif #ifdef ENOSPC case ENOSPC: - errorf(global, "No space left on the file system that will " - "contain the directory %s", name); + fprintf(errors, "No space left on the file system that will " + "contain the directory %s.\n", name); break; #endif #ifdef EDQUOT case EDQUOT: - errorf(global, "Cannot create directory %s because you " - "exceeded your quota", name); + fprintf(errors, "Cannot create directory %s because you " + "exceeded your quota.\n", name); break; #endif - default: - errorf(global, "Error creating directory %s", name); + default : + fprintf(errors, "Error creating directory %s.\n", name); break; } } @@ -96,7 +101,7 @@ static void show_dir_errno(struct GlobalConfig *global, const char *name) #endif -CURLcode create_dir_hierarchy(const char *outfile, struct GlobalConfig *global) +CURLcode create_dir_hierarchy(const char *outfile, FILE *errors) { char *tempdir; char *tempdir2; @@ -152,7 +157,7 @@ CURLcode create_dir_hierarchy(const char *outfile, struct GlobalConfig *global) /* Create directory. Ignore access denied error to allow traversal. */ if(!skip && (-1 == mkdir(dirbuildup, (mode_t)0000750)) && (errno != EACCES) && (errno != EEXIST)) { - show_dir_errno(global, dirbuildup); + show_dir_errno(errors, dirbuildup); result = CURLE_WRITE_ERROR; break; /* get out of loop */ } diff --git a/contrib/libs/curl/src/tool_dirhie.h b/contrib/libs/curl/src/tool_dirhie.h index 0ee407fe54..954f3e2488 100644 --- a/contrib/libs/curl/src/tool_dirhie.h +++ b/contrib/libs/curl/src/tool_dirhie.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,9 +24,7 @@ * ***************************************************************************/ #include "tool_setup.h" -#include "tool_cfgable.h" -CURLcode create_dir_hierarchy(const char *outfile, - struct GlobalConfig *global); +CURLcode create_dir_hierarchy(const char *outfile, FILE *errors); #endif /* HEADER_CURL_TOOL_DIRHIE_H */ diff --git a/contrib/libs/curl/src/tool_doswin.c b/contrib/libs/curl/src/tool_doswin.c index e9347d298b..d8695e93c2 100644 --- a/contrib/libs/curl/src/tool_doswin.c +++ b/contrib/libs/curl/src/tool_doswin.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -626,7 +626,8 @@ CURLcode FindWin32CACert(struct OperationConfig *config, * ignored. We allow setting CA location for schannel only when explicitly * specified by the user via CURLOPT_CAINFO / --cacert. */ - if(feature_ssl && backend != CURLSSLBACKEND_SCHANNEL) { + if((curlinfo->features & CURL_VERSION_SSL) && + backend != CURLSSLBACKEND_SCHANNEL) { DWORD res_len; TCHAR buf[PATH_MAX]; diff --git a/contrib/libs/curl/src/tool_doswin.h b/contrib/libs/curl/src/tool_doswin.h index 669fdb6ed5..ff9bad97eb 100644 --- a/contrib/libs/curl/src/tool_doswin.h +++ b/contrib/libs/curl/src/tool_doswin.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_easysrc.c b/contrib/libs/curl/src/tool_easysrc.c index 6ef2be721c..e653b31170 100644 --- a/contrib/libs/curl/src/tool_easysrc.c +++ b/contrib/libs/curl/src/tool_easysrc.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -186,7 +186,7 @@ void dumpeasysrc(struct GlobalConfig *config) else out = stdout; if(!out) - warnf(config, "Failed to open %s to write libcurl code", o); + warnf(config, "Failed to open %s to write libcurl code!\n", o); else { int i; const char *c; diff --git a/contrib/libs/curl/src/tool_easysrc.h b/contrib/libs/curl/src/tool_easysrc.h index 8c8d131501..ec2fdd23f0 100644 --- a/contrib/libs/curl/src/tool_easysrc.h +++ b/contrib/libs/curl/src/tool_easysrc.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -51,7 +51,7 @@ void dumpeasysrc(struct GlobalConfig *config); #define easysrc_init() CURLE_OK #define easysrc_cleanup() #define dumpeasysrc(x) -#define easysrc_perform() CURLE_OK +#define easysrc_perform(x) CURLE_OK #endif /* CURL_DISABLE_LIBCURL_OPTION */ diff --git a/contrib/libs/curl/src/tool_filetime.c b/contrib/libs/curl/src/tool_filetime.c index 054d34fe2e..c095a49eee 100644 --- a/contrib/libs/curl/src/tool_filetime.c +++ b/contrib/libs/curl/src/tool_filetime.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ curl_off_t getfiletime(const char *filename, struct GlobalConfig *global) /* Windows stat() may attempt to adjust the unix GMT file time by a daylight saving time offset and since it's GMT that is bad behavior. When we have access to a 64-bit type we can bypass stat and get the times directly. */ -#if defined(WIN32) +#if defined(WIN32) && (SIZEOF_CURL_OFF_T >= 8) HANDLE hfile; TCHAR *tchar_filename = curlx_convert_UTF8_to_tchar((char *)filename); @@ -55,7 +55,7 @@ curl_off_t getfiletime(const char *filename, struct GlobalConfig *global) | ((curl_off_t)ft.dwHighDateTime) << 32; if(converted < CURL_OFF_T_C(116444736000000000)) { - warnf(global, "Failed to get filetime: underflow"); + warnf(global, "Failed to get filetime: underflow\n"); } else { result = (converted - CURL_OFF_T_C(116444736000000000)) / 10000000; @@ -63,14 +63,14 @@ curl_off_t getfiletime(const char *filename, struct GlobalConfig *global) } else { warnf(global, "Failed to get filetime: " - "GetFileTime failed: GetLastError %u", + "GetFileTime failed: GetLastError %u\n", (unsigned int)GetLastError()); } CloseHandle(hfile); } else if(GetLastError() != ERROR_FILE_NOT_FOUND) { warnf(global, "Failed to get filetime: " - "CreateFile failed: GetLastError %u", + "CreateFile failed: GetLastError %u\n", (unsigned int)GetLastError()); } #else @@ -79,13 +79,14 @@ curl_off_t getfiletime(const char *filename, struct GlobalConfig *global) result = (curl_off_t)statbuf.st_mtime; } else if(errno != ENOENT) { - warnf(global, "Failed to get filetime: %s", strerror(errno)); + warnf(global, "Failed to get filetime: %s\n", strerror(errno)); } #endif return result; } -#if defined(HAVE_UTIME) || defined(HAVE_UTIMES) || defined(WIN32) +#if defined(HAVE_UTIME) || defined(HAVE_UTIMES) || \ + (defined(WIN32) && (SIZEOF_CURL_OFF_T >= 8)) void setfiletime(curl_off_t filetime, const char *filename, struct GlobalConfig *global) { @@ -93,7 +94,7 @@ void setfiletime(curl_off_t filetime, const char *filename, /* Windows utime() may attempt to adjust the unix GMT file time by a daylight saving time offset and since it's GMT that is bad behavior. When we have access to a 64-bit type we can bypass utime and set the times directly. */ -#if defined(WIN32) +#if defined(WIN32) && (SIZEOF_CURL_OFF_T >= 8) HANDLE hfile; TCHAR *tchar_filename = curlx_convert_UTF8_to_tchar((char *)filename); @@ -101,7 +102,7 @@ void setfiletime(curl_off_t filetime, const char *filename, Windows FILETIME without overflow: 30827-12-31T23:59:59. */ if(filetime > CURL_OFF_T_C(910670515199)) { warnf(global, "Failed to set filetime %" CURL_FORMAT_CURL_OFF_T - " on outfile: overflow", filetime); + " on outfile: overflow\n", filetime); curlx_unicodefree(tchar_filename); return; } @@ -119,14 +120,14 @@ void setfiletime(curl_off_t filetime, const char *filename, ft.dwHighDateTime = (DWORD)(converted >> 32); if(!SetFileTime(hfile, NULL, &ft, &ft)) { warnf(global, "Failed to set filetime %" CURL_FORMAT_CURL_OFF_T - " on outfile: SetFileTime failed: GetLastError %u", + " on outfile: SetFileTime failed: GetLastError %u\n", filetime, (unsigned int)GetLastError()); } CloseHandle(hfile); } else { warnf(global, "Failed to set filetime %" CURL_FORMAT_CURL_OFF_T - " on outfile: CreateFile failed: GetLastError %u", + " on outfile: CreateFile failed: GetLastError %u\n", filetime, (unsigned int)GetLastError()); } @@ -136,7 +137,7 @@ void setfiletime(curl_off_t filetime, const char *filename, times[0].tv_usec = times[1].tv_usec = 0; if(utimes(filename, times)) { warnf(global, "Failed to set filetime %" CURL_FORMAT_CURL_OFF_T - " on '%s': %s", filetime, filename, strerror(errno)); + " on '%s': %s\n", filetime, filename, strerror(errno)); } #elif defined(HAVE_UTIME) @@ -145,10 +146,10 @@ void setfiletime(curl_off_t filetime, const char *filename, times.modtime = (time_t)filetime; if(utime(filename, ×)) { warnf(global, "Failed to set filetime %" CURL_FORMAT_CURL_OFF_T - " on '%s': %s", filetime, filename, strerror(errno)); + " on '%s': %s\n", filetime, filename, strerror(errno)); } #endif } } -#endif /* defined(HAVE_UTIME) || defined(HAVE_UTIMES) || \ - defined(WIN32) */ +#endif /* defined(HAVE_UTIME) || defined(HAVE_UTIMES) || \ + (defined(WIN32) && (SIZEOF_CURL_OFF_T >= 8)) */ diff --git a/contrib/libs/curl/src/tool_filetime.h b/contrib/libs/curl/src/tool_filetime.h index 923ec06407..3d88d8913b 100644 --- a/contrib/libs/curl/src/tool_filetime.h +++ b/contrib/libs/curl/src/tool_filetime.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_findfile.c b/contrib/libs/curl/src/tool_findfile.c index 201d8f0a81..51a45ff3a0 100644 --- a/contrib/libs/curl/src/tool_findfile.c +++ b/contrib/libs/curl/src/tool_findfile.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -49,7 +49,7 @@ struct finder { /* The order of the variables below is important, as the index number is used in the findfile() function */ -static const struct finder conf_list[] = { +static const struct finder list[] = { { "CURL_HOME", NULL, FALSE }, { "XDG_CONFIG_HOME", NULL, FALSE }, /* index == 1, used in the code */ { "HOME", NULL, FALSE }, @@ -109,8 +109,8 @@ char *findfile(const char *fname, int dotscore) if(!fname[0]) return NULL; - for(i = 0; conf_list[i].env; i++) { - char *home = curl_getenv(conf_list[i].env); + for(i = 0; list[i].env; i++) { + char *home = curl_getenv(list[i].env); if(home) { char *path; const char *filename = fname; @@ -120,14 +120,14 @@ char *findfile(const char *fname, int dotscore) curl_free(home); continue; } - if(conf_list[i].append) { - char *c = curl_maprintf("%s%s", home, conf_list[i].append); + if(list[i].append) { + char *c = curl_maprintf("%s%s", home, list[i].append); curl_free(home); if(!c) return NULL; home = c; } - if(conf_list[i].withoutdot) { + if(list[i].withoutdot) { if(!dotscore || xdg) { /* this is not looking for .curlrc, or the XDG_CONFIG_HOME was defined so we skip the extended check */ diff --git a/contrib/libs/curl/src/tool_findfile.h b/contrib/libs/curl/src/tool_findfile.h index faafd71cb8..5bae579e14 100644 --- a/contrib/libs/curl/src/tool_findfile.h +++ b/contrib/libs/curl/src/tool_findfile.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_formparse.c b/contrib/libs/curl/src/tool_formparse.c index fa38698d5e..d4fc651e25 100644 --- a/contrib/libs/curl/src/tool_formparse.c +++ b/contrib/libs/curl/src/tool_formparse.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -61,18 +61,17 @@ static struct tool_mime *tool_mime_new_parts(struct tool_mime *parent) } static struct tool_mime *tool_mime_new_data(struct tool_mime *parent, - char *mime_data) + char *data) { - char *mime_data_copy; struct tool_mime *m = NULL; - mime_data_copy = strdup(mime_data); - if(mime_data_copy) { + data = strdup(data); + if(data) { m = tool_mime_new(parent, TOOLMIME_DATA); if(!m) - free(mime_data_copy); + free(data); else - m->data = mime_data_copy; + m->data = data; } return m; } @@ -202,7 +201,7 @@ size_t tool_mime_stdin_read(char *buffer, if(ferror(stdin)) { /* Show error only once. */ if(sip->config) { - warnf(sip->config, "stdin: %s", strerror(errno)); + warnf(sip->config, "stdin: %s\n", strerror(errno)); sip->config = NULL; } return CURL_READFUNC_ABORT; @@ -369,7 +368,7 @@ static char *get_param_word(struct OperationConfig *config, char **str, ++ptr; } if(trailing_data) - warnf(config->global, "Trailing data after quoted form parameter"); + warnf(config->global, "Trailing data after quoted form parameter\n"); *str = ptr; return word_begin + 1; } @@ -417,7 +416,8 @@ static int read_field_headers(struct OperationConfig *config, if(hdrlen) { hdrbuf[hdrlen] = '\0'; if(slist_append(pheaders, hdrbuf)) { - errorf(config->global, "Out of memory for field headers"); + fprintf(config->global->errors, + "Out of memory for field headers!\n"); return -1; } hdrlen = 0; @@ -427,8 +427,8 @@ static int read_field_headers(struct OperationConfig *config, switch(c) { case EOF: if(ferror(fp)) { - errorf(config->global, "Header file %s read error: %s", filename, - strerror(errno)); + fprintf(config->global->errors, + "Header file %s read error: %s\n", filename, strerror(errno)); return -1; } return 0; /* Done. */ @@ -448,7 +448,7 @@ static int read_field_headers(struct OperationConfig *config, pos++; if(!incomment) { if(hdrlen == sizeof(hdrbuf) - 1) { - warnf(config->global, "File %s line %d: header too long (truncated)", + warnf(config->global, "File %s line %d: header too long (truncated)\n", filename, lineno); c = ' '; } @@ -506,7 +506,7 @@ static int get_param_part(struct OperationConfig *config, char endchar, /* verify that this is a fine type specifier */ if(2 != sscanf(type, "%127[^/ ]/%127[^;, \n]", type_major, type_minor)) { - warnf(config->global, "Illegally formatted content-type field"); + warnf(config->global, "Illegally formatted content-type field!\n"); curl_slist_free_all(headers); return -1; /* illegal content-type syntax! */ } @@ -558,7 +558,7 @@ static int get_param_part(struct OperationConfig *config, char endchar, *endpos = '\0'; fp = fopen(hdrfile, FOPEN_READTEXT); if(!fp) - warnf(config->global, "Cannot read from %s: %s", hdrfile, + warnf(config->global, "Cannot read from %s: %s\n", hdrfile, strerror(errno)); else { int i = read_field_headers(config, hdrfile, fp, &headers); @@ -584,7 +584,7 @@ static int get_param_part(struct OperationConfig *config, char endchar, sep = *p; *endpos = '\0'; if(slist_append(&headers, hdr)) { - errorf(config->global, "Out of memory for field header"); + fprintf(config->global->errors, "Out of memory for field header!\n"); curl_slist_free_all(headers); return -1; } @@ -620,7 +620,7 @@ static int get_param_part(struct OperationConfig *config, char endchar, sep = *p; *endpos = '\0'; if(*unknown) - warnf(config->global, "skip unknown form field: %s", unknown); + warnf(config->global, "skip unknown form field: %s\n", unknown); } } @@ -631,25 +631,25 @@ static int get_param_part(struct OperationConfig *config, char endchar, if(ptype) *ptype = type; else if(type) - warnf(config->global, "Field content type not allowed here: %s", type); + warnf(config->global, "Field content type not allowed here: %s\n", type); if(pfilename) *pfilename = filename; else if(filename) warnf(config->global, - "Field file name not allowed here: %s", filename); + "Field file name not allowed here: %s\n", filename); if(pencoder) *pencoder = encoder; else if(encoder) warnf(config->global, - "Field encoder not allowed here: %s", encoder); + "Field encoder not allowed here: %s\n", encoder); if(pheaders) *pheaders = headers; else if(headers) { warnf(config->global, - "Field headers not allowed here: %s", headers->data); + "Field headers not allowed here: %s\n", headers->data); curl_slist_free_all(headers); } @@ -772,7 +772,7 @@ int formparse(struct OperationConfig *config, else if(!name && !strcmp(contp, ")") && !literal_value) { /* Ending a multipart. */ if(*mimecurrent == *mimeroot) { - warnf(config->global, "no multipart to terminate"); + warnf(config->global, "no multipart to terminate!\n"); goto fail; } *mimecurrent = (*mimecurrent)->parent; @@ -818,7 +818,7 @@ int formparse(struct OperationConfig *config, libcurl. */ if(part->size > 0) { warnf(config->global, - "error while reading standard input"); + "error while reading standard input\n"); goto fail; } Curl_safefree(part->data); @@ -855,7 +855,7 @@ int formparse(struct OperationConfig *config, libcurl. */ if(part->size > 0) { warnf(config->global, - "error while reading standard input"); + "error while reading standard input\n"); goto fail; } Curl_safefree(part->data); @@ -888,7 +888,7 @@ int formparse(struct OperationConfig *config, if(sep) { *contp = (char) sep; warnf(config->global, - "garbage at end of field specification: %s", contp); + "garbage at end of field specification: %s\n", contp); } } @@ -896,11 +896,11 @@ int formparse(struct OperationConfig *config, SET_TOOL_MIME_PTR(part, name); } else { - warnf(config->global, "Illegally formatted input field"); + warnf(config->global, "Illegally formatted input field!\n"); goto fail; } err = 0; -fail: + fail: Curl_safefree(contents); curl_slist_free_all(headers); return err; diff --git a/contrib/libs/curl/src/tool_formparse.h b/contrib/libs/curl/src/tool_formparse.h index 35d5c952d5..8c0461c69e 100644 --- a/contrib/libs/curl/src/tool_formparse.h +++ b/contrib/libs/curl/src/tool_formparse.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_getparam.c b/contrib/libs/curl/src/tool_getparam.c index ac1a98cc7a..af4b3a6984 100644 --- a/contrib/libs/curl/src/tool_getparam.c +++ b/contrib/libs/curl/src/tool_getparam.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,8 +41,6 @@ #include "tool_paramhlp.h" #include "tool_parsecfg.h" #include "tool_main.h" -#include "dynbuf.h" -#include "tool_stderr.h" #include "memdebug.h" /* keep this as LAST include */ @@ -122,7 +120,6 @@ static const struct LongShort aliases[]= { {"*x", "krb4", ARG_STRING}, /* 'krb4' is the previous name */ {"*X", "haproxy-protocol", ARG_BOOL}, - {"*P", "haproxy-clientip", ARG_STRING}, {"*y", "max-filesize", ARG_STRING}, {"*z", "disable-eprt", ARG_BOOL}, {"*Z", "eprt", ARG_BOOL}, @@ -205,15 +202,12 @@ static const struct LongShort aliases[]= { {"$Z", "compressed-ssh", ARG_BOOL}, {"$~", "happy-eyeballs-timeout-ms", ARG_STRING}, {"$!", "retry-all-errors", ARG_BOOL}, - {"$%", "trace-ids", ARG_BOOL}, {"0", "http1.0", ARG_NONE}, {"01", "http1.1", ARG_NONE}, {"02", "http2", ARG_NONE}, {"03", "http2-prior-knowledge", ARG_NONE}, {"04", "http3", ARG_NONE}, - {"05", "http3-only", ARG_NONE}, {"09", "http0.9", ARG_BOOL}, - {"0a", "proxy-http2", ARG_BOOL}, {"1", "tlsv1", ARG_NONE}, {"10", "tlsv1.0", ARG_NONE}, {"11", "tlsv1.1", ARG_NONE}, @@ -239,7 +233,6 @@ static const struct LongShort aliases[]= { {"db", "data-binary", ARG_STRING}, {"de", "data-urlencode", ARG_STRING}, {"df", "json", ARG_STRING}, - {"dg", "url-query", ARG_STRING}, {"D", "dump-header", ARG_FILENAME}, {"e", "referer", ARG_STRING}, {"E", "cert", ARG_FILENAME}, @@ -249,8 +242,6 @@ static const struct LongShort aliases[]= { {"Ed", "key-type", ARG_STRING}, {"Ee", "pass", ARG_STRING}, {"Ef", "engine", ARG_STRING}, - {"EG", "ca-native", ARG_BOOL}, - {"EH", "proxy-ca-native", ARG_BOOL}, {"Eg", "capath", ARG_FILENAME}, {"Eh", "pubkey", ARG_STRING}, {"Ei", "hostpubmd5", ARG_STRING}, @@ -300,7 +291,7 @@ static const struct LongShort aliases[]= { {"F", "form", ARG_STRING}, {"Fs", "form-string", ARG_STRING}, {"g", "globoff", ARG_BOOL}, - {"G", "get", ARG_BOOL}, + {"G", "get", ARG_NONE}, {"Ga", "request-target", ARG_STRING}, {"h", "help", ARG_BOOL}, {"H", "header", ARG_STRING}, @@ -509,8 +500,8 @@ static ParameterError GetSizeParameter(struct GlobalConfig *global, char *unit; curl_off_t value; - if(curlx_strtoofft(arg, &unit, 10, &value)) { - warnf(global, "invalid number specified for %s", which); + if(curlx_strtoofft(arg, &unit, 0, &value)) { + warnf(global, "invalid number specified for %s\n", which); return PARAM_BAD_USE; } @@ -543,7 +534,7 @@ static ParameterError GetSizeParameter(struct GlobalConfig *global, /* for plain bytes, leave as-is */ break; default: - warnf(global, "unsupported %s unit. Use G, M, K or B", which); + warnf(global, "unsupported %s unit. Use G, M, K or B!\n", which); return PARAM_BAD_USE; } *value_out = value; @@ -607,7 +598,7 @@ static ParameterError data_urlencode(struct GlobalConfig *global, if(!file) warnf(global, "Couldn't read data from file \"%s\", this makes " - "an empty POST.", nextarg); + "an empty POST.\n", nextarg); } err = file2memory(&postdata, &size, file); @@ -664,20 +655,9 @@ static ParameterError data_urlencode(struct GlobalConfig *global, return PARAM_OK; } -static void sethttpver(struct GlobalConfig *global, - struct OperationConfig *config, - long httpversion) -{ - if(config->httpversion && - (config->httpversion != httpversion)) - warnf(global, "Overrides previous HTTP version option"); - - config->httpversion = httpversion; -} ParameterError getparameter(const char *flag, /* f or -long-flag */ char *nextarg, /* NULL if unset */ - argv_item_t cleararg, bool *usedarg, /* set to TRUE if the arg has been used */ struct GlobalConfig *global, @@ -695,6 +675,10 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ ParameterError err; bool toggle = TRUE; /* how to switch boolean options, on or off. Controlled by using --OPTION or --no-OPTION */ +#ifdef HAVE_WRITABLE_ARGV + argv_item_t clearthis = NULL; +#endif + static const char *redir_protos[] = { "http", "https", @@ -702,11 +686,6 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ "ftps", NULL }; -#ifdef HAVE_WRITABLE_ARGV - argv_item_t clearthis = NULL; -#else - (void)cleararg; -#endif *usedarg = FALSE; /* default is that we don't use the arg */ @@ -783,21 +762,20 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ /* this option requires an extra parameter */ if(!longopt && parse[1]) { nextarg = (char *)&parse[1]; /* this is the actual extra parameter */ +#ifdef HAVE_WRITABLE_ARGV + clearthis = nextarg; +#endif singleopt = TRUE; /* don't loop anymore after this */ } else if(!nextarg) return PARAM_REQUIRES_PARAMETER; - else { -#ifdef HAVE_WRITABLE_ARGV - clearthis = cleararg; -#endif + else *usedarg = TRUE; /* mark it as used */ - } if((aliases[hit].desc == ARG_FILENAME) && (nextarg[0] == '-') && nextarg[1]) { /* if the file name looks like a command line option */ - warnf(global, "The file name argument '%s' looks like a flag.", + warnf(global, "The file name argument '%s' looks like a flag.\n", nextarg); } } @@ -829,7 +807,8 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ config->authtype |= CURLAUTH_BEARER; break; case 'c': /* connect-timeout */ - err = secs2ms(&config->connecttimeout_ms, nextarg); + err = str2udouble(&config->connecttimeout, nextarg, + (double)LONG_MAX/1000); if(err) return err; break; @@ -866,17 +845,17 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ case 'g': /* --trace */ GetStr(&global->trace_dump, nextarg); if(global->tracetype && (global->tracetype != TRACE_BIN)) - warnf(global, "--trace overrides an earlier trace/verbose option"); + warnf(global, "--trace overrides an earlier trace/verbose option\n"); global->tracetype = TRACE_BIN; break; case 'G': /* --npn */ - warnf(global, "--npn is no longer supported"); + warnf(global, "--npn is no longer supported\n"); break; case 'h': /* --trace-ascii */ GetStr(&global->trace_dump, nextarg); if(global->tracetype && (global->tracetype != TRACE_ASCII)) warnf(global, - "--trace-ascii overrides an earlier trace/verbose option"); + "--trace-ascii overrides an earlier trace/verbose option\n"); global->tracetype = TRACE_ASCII; break; case 'H': /* --alpn */ @@ -888,7 +867,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ ParameterError pe = GetSizeParameter(global, nextarg, "rate", &value); if(pe != PARAM_OK) - return pe; + return pe; config->recvpersecond = value; config->sendpersecond = value; } @@ -931,7 +910,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ numerator = 24*60*60*1000; break; default: - errorf(global, "unsupported --rate unit"); + errorf(global, "unsupported --rate unit\n"); return PARAM_BAD_USE; } } @@ -940,7 +919,9 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ break; case 'j': /* --compressed */ - if(toggle && !(feature_libz || feature_brotli || feature_zstd)) + if(toggle && + !(curlinfo->features & (CURL_VERSION_LIBZ | + CURL_VERSION_BROTLI | CURL_VERSION_ZSTD))) return PARAM_LIBCURL_DOESNT_SUPPORT; config->encoding = toggle; break; @@ -957,30 +938,36 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ break; case 'l': /* --negotiate */ - if(!toggle) - config->authtype &= ~CURLAUTH_NEGOTIATE; - else if(feature_spnego) - config->authtype |= CURLAUTH_NEGOTIATE; + if(toggle) { + if(curlinfo->features & CURL_VERSION_SPNEGO) + config->authtype |= CURLAUTH_NEGOTIATE; + else + return PARAM_LIBCURL_DOESNT_SUPPORT; + } else - return PARAM_LIBCURL_DOESNT_SUPPORT; + config->authtype &= ~CURLAUTH_NEGOTIATE; break; case 'm': /* --ntlm */ - if(!toggle) - config->authtype &= ~CURLAUTH_NTLM; - else if(feature_ntlm) - config->authtype |= CURLAUTH_NTLM; + if(toggle) { + if(curlinfo->features & CURL_VERSION_NTLM) + config->authtype |= CURLAUTH_NTLM; + else + return PARAM_LIBCURL_DOESNT_SUPPORT; + } else - return PARAM_LIBCURL_DOESNT_SUPPORT; + config->authtype &= ~CURLAUTH_NTLM; break; case 'M': /* --ntlm-wb */ - if(!toggle) - config->authtype &= ~CURLAUTH_NTLM_WB; - else if(feature_ntlm_wb) - config->authtype |= CURLAUTH_NTLM_WB; + if(toggle) { + if(curlinfo->features & CURL_VERSION_NTLM_WB) + config->authtype |= CURLAUTH_NTLM_WB; + else + return PARAM_LIBCURL_DOESNT_SUPPORT; + } else - return PARAM_LIBCURL_DOESNT_SUPPORT; + config->authtype &= ~CURLAUTH_NTLM_WB; break; case 'n': /* --basic for completeness */ @@ -1026,9 +1013,10 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ break; case 't': /* --proxy-ntlm */ - if(!feature_ntlm) + if(curlinfo->features & CURL_VERSION_NTLM) + config->proxyntlm = toggle; + else return PARAM_LIBCURL_DOESNT_SUPPORT; - config->proxyntlm = toggle; break; case 'u': /* --crlf */ @@ -1042,7 +1030,19 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ break; case 'v': /* --stderr */ - tool_set_stderr_file(global, nextarg); + if(strcmp(nextarg, "-")) { + FILE *newfile = fopen(nextarg, FOPEN_WRITETEXT); + if(!newfile) + warnf(global, "Failed to open %s!\n", nextarg); + else { + if(global->errors_fopened) + fclose(global->errors); + global->errors = newfile; + global->errors_fopened = TRUE; + } + } + else + global->errors = stdout; break; case 'w': /* --interface */ /* interface */ @@ -1050,16 +1050,14 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ break; case 'x': /* --krb */ /* kerberos level string */ - if(!feature_spnego) + if(curlinfo->features & CURL_VERSION_SPNEGO) + GetStr(&config->krblevel, nextarg); + else return PARAM_LIBCURL_DOESNT_SUPPORT; - GetStr(&config->krblevel, nextarg); break; case 'X': /* --haproxy-protocol */ config->haproxy_protocol = toggle; break; - case 'P': /* --haproxy-clientip */ - GetStr(&config->haproxy_clientip, nextarg); - break; case 'y': /* --max-filesize */ { curl_off_t value; @@ -1067,7 +1065,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ GetSizeParameter(global, nextarg, "max-filesize", &value); if(pe != PARAM_OK) - return pe; + return pe; config->max_filesize = value; } break; @@ -1115,12 +1113,12 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ case '$': /* more options without a short option */ switch(subletter) { case 'a': /* --ssl */ - if(toggle && !feature_ssl) + if(toggle && !(curlinfo->features & CURL_VERSION_SSL)) return PARAM_LIBCURL_DOESNT_SUPPORT; config->ftp_ssl = toggle; if(config->ftp_ssl) warnf(global, - "--ssl is an insecure option, consider --ssl-reqd instead"); + "--ssl is an insecure option, consider --ssl-reqd instead\n"); break; case 'b': /* --ftp-pasv */ Curl_safefree(config->ftpport); @@ -1175,9 +1173,10 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ break; case 'k': /* --proxy-negotiate */ - if(!feature_spnego) + if(curlinfo->features & CURL_VERSION_SPNEGO) + config->proxynegotiate = toggle; + else return PARAM_LIBCURL_DOESNT_SUPPORT; - config->proxynegotiate = toggle; break; case 'l': /* --form-escape */ @@ -1238,7 +1237,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ GetStr(&config->ftp_alternative_to_user, nextarg); break; case 'v': /* --ssl-reqd */ - if(toggle && !feature_ssl) + if(toggle && !(curlinfo->features & CURL_VERSION_SSL)) return PARAM_LIBCURL_DOESNT_SUPPORT; config->ftp_ssl_reqd = toggle; break; @@ -1246,7 +1245,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ config->disable_sessionid = (!toggle)?TRUE:FALSE; break; case 'x': /* --ftp-ssl-control */ - if(toggle && !feature_ssl) + if(toggle && !(curlinfo->features & CURL_VERSION_SSL)) return PARAM_LIBCURL_DOESNT_SUPPORT; config->ftp_ssl_control = toggle; break; @@ -1262,7 +1261,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ case 'z': /* --libcurl */ #ifdef CURL_DISABLE_LIBCURL_OPTION warnf(global, - "--libcurl option was disabled at build-time"); + "--libcurl option was disabled at build-time!\n"); return PARAM_OPTION_UNKNOWN; #else GetStr(&global->libcurl, nextarg); @@ -1292,7 +1291,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ /* This specifies the noproxy list */ GetStr(&config->noproxy, nextarg); break; - case '7': /* --socks5-gssapi-nec */ + case '7': /* --socks5-gssapi-nec*/ config->socks5_gssapi_nec = toggle; break; case '8': /* --proxy1.0 */ @@ -1340,7 +1339,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ GetStr(&config->mail_auth, nextarg); break; case 'J': /* --metalink */ - errorf(global, "--metalink is disabled"); + errorf(global, "--metalink is disabled\n"); return PARAM_BAD_USE; case '6': /* --sasl-authzid */ GetStr(&config->sasl_authzid, nextarg); @@ -1352,7 +1351,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ #ifdef CURLDEBUG global->test_event_based = toggle; #else - warnf(global, "--test-event is ignored unless a debug build"); + warnf(global, "--test-event is ignored unless a debug build!\n"); #endif break; case 'M': /* --unix-socket */ @@ -1375,7 +1374,8 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ return err; break; case 'R': /* --expect100-timeout */ - err = secs2ms(&config->expect100timeout_ms, nextarg); + err = str2udouble(&config->expect100timeout, nextarg, + (double)LONG_MAX/1000); if(err) return err; break; @@ -1408,9 +1408,6 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ return err; /* 0 is a valid value for this timeout */ break; - case '%': /* --trace-ids */ - global->traceids = toggle; - break; } break; case '#': @@ -1430,46 +1427,31 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ switch(subletter) { case '\0': /* HTTP version 1.0 */ - sethttpver(global, config, CURL_HTTP_VERSION_1_0); + config->httpversion = CURL_HTTP_VERSION_1_0; break; case '1': /* HTTP version 1.1 */ - sethttpver(global, config, CURL_HTTP_VERSION_1_1); + config->httpversion = CURL_HTTP_VERSION_1_1; break; case '2': /* HTTP version 2.0 */ - if(!feature_http2) - return PARAM_LIBCURL_DOESNT_SUPPORT; - sethttpver(global, config, CURL_HTTP_VERSION_2_0); + config->httpversion = CURL_HTTP_VERSION_2_0; break; case '3': /* --http2-prior-knowledge */ - /* HTTP version 2.0 over clean TCP */ - if(!feature_http2) - return PARAM_LIBCURL_DOESNT_SUPPORT; - sethttpver(global, config, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE); + /* HTTP version 2.0 over clean TCP*/ + config->httpversion = CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE; break; case '4': /* --http3 */ - /* Try HTTP/3, allow fallback */ - if(!feature_http3) - return PARAM_LIBCURL_DOESNT_SUPPORT; - sethttpver(global, config, CURL_HTTP_VERSION_3); - break; - case '5': /* --http3-only */ - /* Try HTTP/3 without fallback */ - if(!feature_http3) + /* HTTP version 3 go over QUIC - at once */ + if(curlinfo->features & CURL_VERSION_HTTP3) + config->httpversion = CURL_HTTP_VERSION_3; + else return PARAM_LIBCURL_DOESNT_SUPPORT; - sethttpver(global, config, CURL_HTTP_VERSION_3ONLY); break; case '9': /* Allow HTTP/0.9 responses! */ config->http09_allowed = toggle; break; - case 'a': - /* --proxy-http2 */ - if(!feature_httpsproxy || !feature_http2) - return PARAM_LIBCURL_DOESNT_SUPPORT; - config->proxyver = CURLPROXY_HTTPS2; - break; } break; case '1': /* --tlsv1* options */ @@ -1504,11 +1486,11 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ break; case '2': /* SSL version 2 */ - warnf(global, "Ignores instruction to use SSLv2"); + warnf(global, "Ignores instruction to use SSLv2\n"); break; case '3': /* SSL version 3 */ - warnf(global, "Ignores instruction to use SSLv3"); + warnf(global, "Ignores instruction to use SSLv3\n"); break; case '4': /* IPv4 */ @@ -1529,14 +1511,16 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ case 'b': switch(subletter) { case 'a': /* --alt-svc */ - if(!feature_altsvc) + if(curlinfo->features & CURL_VERSION_ALTSVC) + GetStr(&config->altsvc, nextarg); + else return PARAM_LIBCURL_DOESNT_SUPPORT; - GetStr(&config->altsvc, nextarg); break; case 'b': /* --hsts */ - if(!feature_hsts) + if(curlinfo->features & CURL_VERSION_HSTS) + GetStr(&config->hsts, nextarg); + else return PARAM_LIBCURL_DOESNT_SUPPORT; - GetStr(&config->hsts, nextarg); break; default: /* --cookie string coming up: */ if(nextarg[0] == '@') { @@ -1585,39 +1569,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ size_t size = 0; bool raw_mode = (subletter == 'r'); - if(subletter == 'g') { /* --url-query */ -#define MAX_QUERY_LEN 100000 /* larger is not likely to ever work */ - char *query; - struct curlx_dynbuf dyn; - curlx_dyn_init(&dyn, MAX_QUERY_LEN); - - if(*nextarg == '+') { - /* use without encoding */ - query = strdup(&nextarg[1]); - if(!query) - return PARAM_NO_MEM; - } - else { - err = data_urlencode(global, nextarg, &query, &size); - if(err) - return err; - } - - if(config->query) { - CURLcode result = - curlx_dyn_addf(&dyn, "%s&%s", config->query, query); - free(query); - if(result) - return PARAM_NO_MEM; - free(config->query); - config->query = curlx_dyn_ptr(&dyn); - } - else - config->query = query; - - break; /* this is not a POST argument at all */ - } - else if(subletter == 'e') { /* --data-urlencode */ + if(subletter == 'e') { /* --data-urlencode */ err = data_urlencode(global, nextarg, &postdata, &size); if(err) return err; @@ -1636,7 +1588,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ file = fopen(nextarg, "rb"); if(!file) warnf(global, "Couldn't read data from file \"%s\", this makes " - "an empty POST.", nextarg); + "an empty POST.\n", nextarg); } if((subletter == 'b') || /* --data-binary */ @@ -1739,15 +1691,9 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ cleanarg(clearthis); GetFileAndPassword(nextarg, &config->cert, &config->key_passwd); break; - case 'a': /* --cacert CA info PEM file */ + case 'a': /* CA info PEM file */ GetStr(&config->cacert, nextarg); break; - case 'G': /* --ca-native */ - config->native_ca_store = toggle; - break; - case 'H': /* --proxy-ca-native */ - config->proxy_native_ca_store = toggle; - break; case 'b': /* cert file type */ GetStr(&config->cert_type, nextarg); break; @@ -1784,7 +1730,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ GetStr(&config->crlfile, nextarg); break; case 'k': /* TLS username */ - if(!feature_tls_srp) { + if(!(curlinfo->features & CURL_VERSION_TLSAUTH_SRP)) { cleanarg(clearthis); return PARAM_LIBCURL_DOESNT_SUPPORT; } @@ -1792,7 +1738,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ cleanarg(clearthis); break; case 'l': /* TLS password */ - if(!feature_tls_srp) { + if(!(curlinfo->features & CURL_VERSION_TLSAUTH_SRP)) { cleanarg(clearthis); return PARAM_LIBCURL_DOESNT_SUPPORT; } @@ -1800,24 +1746,26 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ cleanarg(clearthis); break; case 'm': /* TLS authentication type */ - if(!feature_tls_srp) + if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP) { + GetStr(&config->tls_authtype, nextarg); + if(!curl_strequal(config->tls_authtype, "SRP")) + return PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */ + } + else return PARAM_LIBCURL_DOESNT_SUPPORT; - GetStr(&config->tls_authtype, nextarg); - if(!curl_strequal(config->tls_authtype, "SRP")) - return PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */ break; case 'n': /* no empty SSL fragments, --ssl-allow-beast */ - if(feature_ssl) + if(curlinfo->features & CURL_VERSION_SSL) config->ssl_allow_beast = toggle; break; case 'o': /* --ssl-auto-client-cert */ - if(feature_ssl) + if(curlinfo->features & CURL_VERSION_SSL) config->ssl_auto_client_cert = toggle; break; case 'O': /* --proxy-ssl-auto-client-cert */ - if(feature_ssl) + if(curlinfo->features & CURL_VERSION_SSL) config->proxy_ssl_auto_client_cert = toggle; break; @@ -1842,12 +1790,12 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ break; case 's': /* --ssl-no-revoke */ - if(feature_ssl) + if(curlinfo->features & CURL_VERSION_SSL) config->ssl_no_revoke = TRUE; break; case 'S': /* --ssl-revoke-best-effort */ - if(feature_ssl) + if(curlinfo->features & CURL_VERSION_SSL) config->ssl_revoke_best_effort = TRUE; break; @@ -1857,24 +1805,28 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ case 'u': /* TLS username for proxy */ cleanarg(clearthis); - if(!feature_tls_srp) + if(!(curlinfo->features & CURL_VERSION_TLSAUTH_SRP)) { return PARAM_LIBCURL_DOESNT_SUPPORT; + } GetStr(&config->proxy_tls_username, nextarg); break; case 'v': /* TLS password for proxy */ cleanarg(clearthis); - if(!feature_tls_srp) + if(!(curlinfo->features & CURL_VERSION_TLSAUTH_SRP)) { return PARAM_LIBCURL_DOESNT_SUPPORT; + } GetStr(&config->proxy_tls_password, nextarg); break; case 'w': /* TLS authentication type for proxy */ - if(!feature_tls_srp) + if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP) { + GetStr(&config->proxy_tls_authtype, nextarg); + if(!curl_strequal(config->proxy_tls_authtype, "SRP")) + return PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */ + } + else return PARAM_LIBCURL_DOESNT_SUPPORT; - GetStr(&config->proxy_tls_authtype, nextarg); - if(!curl_strequal(config->proxy_tls_authtype, "SRP")) - return PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */ break; case 'x': /* certificate file for proxy */ @@ -1909,7 +1861,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ break; case '4': /* no empty SSL fragments for proxy */ - if(feature_ssl) + if(curlinfo->features & CURL_VERSION_SSL) config->proxy_ssl_allow_beast = toggle; break; @@ -1989,7 +1941,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ } if(config->failonerror && config->failwithbody) { errorf(config->global, "You must select either --fail or " - "--fail-with-body, not both."); + "--fail-with-body, not both.\n"); return PARAM_BAD_USE; } break; @@ -2015,7 +1967,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ GetStr(&config->request_target, nextarg); } else - config->use_httpget = toggle; + config->use_httpget = TRUE; break; case 'h': /* h for help */ @@ -2038,7 +1990,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ bool use_stdin = !strcmp(&nextarg[1], "-"); FILE *file = use_stdin?stdin:fopen(&nextarg[1], FOPEN_READTEXT); if(!file) - warnf(global, "Failed to open %s", &nextarg[1]); + warnf(global, "Failed to open %s!\n", &nextarg[1]); else { err = file2memory(&string, &len, file); if(!err && string) { @@ -2097,7 +2049,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ break; case 'K': /* parse config file */ if(parseconfig(nextarg, global)) { - errorf(global, "cannot read config from '%s'", nextarg); + errorf(global, "cannot read config from '%s'\n", nextarg); return PARAM_READ_ERROR; } break; @@ -2116,7 +2068,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ break; case 'm': /* specified max time */ - err = secs2ms(&config->timeout_ms, nextarg); + err = str2udouble(&config->timeout, nextarg, (double)LONG_MAX/1000); if(err) return err; break; @@ -2124,7 +2076,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ if(toggle) { /* --no-manual shows no manual... */ #ifndef USE_MANUAL warnf(global, - "built-in manual was disabled at build-time"); + "built-in manual was disabled at build-time!\n"); #endif return PARAM_MANUAL_REQUESTED; } @@ -2181,12 +2133,9 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ if(config->url_out) /* existing node */ url = config->url_out; - else { - if(!toggle && !config->default_node_flags) - break; + else /* there was no free node, create one! */ config->url_out = url = new_getout(config); - } if(!url) return PARAM_NO_MEM; @@ -2194,7 +2143,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ /* fill in the outfile */ if('o' == letter) { if(!*nextarg) { - warnf(global, "output file name has no length"); + warnf(global, "output file name has no length\n"); return PARAM_BAD_USE; } GetStr(&url->outfile, nextarg); @@ -2255,12 +2204,12 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ char buffer[32]; curl_off_t off; if(curlx_strtoofft(nextarg, NULL, 10, &off)) { - warnf(global, "unsupported range point"); + warnf(global, "unsupported range point\n"); return PARAM_BAD_USE; } warnf(global, "A specified range MUST include at least one dash (-). " - "Appending one for you"); + "Appending one for you!\n"); msnprintf(buffer, sizeof(buffer), "%" CURL_FORMAT_CURL_OFF_T "-", off); Curl_safefree(config->range); config->range = strdup(buffer); @@ -2275,7 +2224,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ warnf(global, "Invalid character is found in given range. " "A specified range MUST have only digits in " "\'start\'-\'stop\'. The server's response to this " - "request is uncertain."); + "request is uncertain.\n"); break; } tmp_range++; @@ -2287,11 +2236,21 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ /* use remote file's time */ config->remote_time = toggle; break; - case 's': /* --silent */ - global->silent = toggle; + case 's': + /* don't show progress meter, don't show errors : */ + if(toggle) + global->mute = global->noprogress = TRUE; + else + global->mute = global->noprogress = FALSE; + if(global->showerror < 0) + /* if still on the default value, set showerror to the reverse of + toggle. This is to allow -S and -s to be used in an independent + order but still have the same effect. */ + global->showerror = (!toggle)?TRUE:FALSE; /* toggle off */ break; - case 'S': /* --show-error */ - global->showerror = toggle; + case 'S': + /* show errors */ + global->showerror = toggle?1:0; /* toggle on if used with -s */ break; case 't': /* Telnet options */ @@ -2352,7 +2311,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ return PARAM_NO_MEM; if(global->tracetype && (global->tracetype != TRACE_PLAIN)) warnf(global, - "-v, --verbose overrides an earlier trace/verbose option"); + "-v, --verbose overrides an earlier trace/verbose option\n"); global->tracetype = TRACE_PLAIN; } else @@ -2400,8 +2359,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ default: /* --proxy */ GetStr(&config->proxy, nextarg); - if(config->proxyver != CURLPROXY_HTTPS2) - config->proxyver = CURLPROXY_HTTP; + config->proxyver = CURLPROXY_HTTP; break; } break; @@ -2430,20 +2388,16 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ case '\0': /* --parallel */ global->parallel = toggle; break; - case 'b': { /* --parallel-max */ - long val; - err = str2unum(&val, nextarg); + case 'b': /* --parallel-max */ + err = str2unum(&global->parallel_max, nextarg); if(err) return err; - if(val > MAX_PARALLEL) + if(global->parallel_max > MAX_PARALLEL) global->parallel_max = MAX_PARALLEL; - else if(val < 1) + else if(global->parallel_max < 1) global->parallel_max = PARALLEL_DEFAULT; - else - global->parallel_max = (unsigned short)val; break; - } - case 'c': /* --parallel-immediate */ + case 'c': /* --parallel-connect */ global->parallel_connect = toggle; break; } @@ -2483,7 +2437,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ warnf(global, "Illegal date format for -z, --time-cond (and not " "a file name). Disabling time condition. " - "See curl_getdate(3) for valid date syntax."); + "See curl_getdate(3) for valid date syntax.\n"); } } break; @@ -2528,7 +2482,7 @@ ParameterError parse_args(struct GlobalConfig *global, int argc, } } - result = getparameter(orig_opt, nextarg, argv[i + 1], &passarg, + result = getparameter(orig_opt, nextarg, &passarg, global, config); curlx_unicodefree(nextarg); @@ -2558,10 +2512,6 @@ ParameterError parse_args(struct GlobalConfig *global, int argc, else result = PARAM_NO_MEM; } - else { - errorf(global, "missing URL before --next"); - result = PARAM_BAD_USE; - } } else if(!result && passarg) i++; /* we're supposed to skip this */ @@ -2571,7 +2521,7 @@ ParameterError parse_args(struct GlobalConfig *global, int argc, bool used; /* Just add the URL please */ - result = getparameter("--url", orig_opt, argv[i], &used, global, config); + result = getparameter("--url", orig_opt, &used, global, config); } if(!result) @@ -2592,9 +2542,9 @@ ParameterError parse_args(struct GlobalConfig *global, int argc, const char *reason = param2text(result); if(orig_opt && strcmp(":", orig_opt)) - helpf(stderr, "option %s: %s", orig_opt, reason); + helpf(global->errors, "option %s: %s\n", orig_opt, reason); else - helpf(stderr, "%s", reason); + helpf(global->errors, "%s\n", reason); } curlx_unicodefree(orig_opt); diff --git a/contrib/libs/curl/src/tool_getparam.h b/contrib/libs/curl/src/tool_getparam.h index 827a04e81a..3eb1773918 100644 --- a/contrib/libs/curl/src/tool_getparam.h +++ b/contrib/libs/curl/src/tool_getparam.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -55,7 +55,6 @@ struct GlobalConfig; struct OperationConfig; ParameterError getparameter(const char *flag, char *nextarg, - argv_item_t cleararg, bool *usedarg, struct GlobalConfig *global, struct OperationConfig *operation); diff --git a/contrib/libs/curl/src/tool_getpass.c b/contrib/libs/curl/src/tool_getpass.c index c241fc6f21..97594f1103 100644 --- a/contrib/libs/curl/src/tool_getpass.c +++ b/contrib/libs/curl/src/tool_getpass.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -50,6 +50,14 @@ # include <conio.h> #endif +#ifdef NETWARE +# ifdef __NOVELL_LIBC__ +# include <screen.h> +# else +# error #include <nwconio.h> +# endif +#endif + #ifdef HAVE_UNISTD_H #include <unistd.h> #endif @@ -124,6 +132,45 @@ char *getpass_r(const char *prompt, char *buffer, size_t buflen) #define DONE #endif /* WIN32 */ +#ifdef NETWARE +/* NetWare implementation */ +#ifdef __NOVELL_LIBC__ +char *getpass_r(const char *prompt, char *buffer, size_t buflen) +{ + return getpassword(prompt, buffer, buflen); +} +#else +char *getpass_r(const char *prompt, char *buffer, size_t buflen) +{ + size_t i = 0; + + printf("%s", prompt); + do { + buffer[i++] = getch(); + if(buffer[i-1] == '\b') { + /* remove this letter and if this is not the first key, + remove the previous one as well */ + if(i > 1) { + printf("\b \b"); + i = i - 2; + } + else { + RingTheBell(); + i = i - 1; + } + } + else if(buffer[i-1] != 13) + putchar('*'); + + } while((buffer[i-1] != 13) && (i < buflen)); + buffer[i-1] = '\0'; + printf("\r\n"); + return buffer; +} +#endif /* __NOVELL_LIBC__ */ +#define DONE +#endif /* NETWARE */ + #ifndef DONE /* not previously provided */ #ifdef HAVE_TERMIOS_H diff --git a/contrib/libs/curl/src/tool_getpass.h b/contrib/libs/curl/src/tool_getpass.h index b93585d94d..01dc464015 100644 --- a/contrib/libs/curl/src/tool_getpass.h +++ b/contrib/libs/curl/src/tool_getpass.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_help.c b/contrib/libs/curl/src/tool_help.c index 729b78c366..65a1f43dfa 100644 --- a/contrib/libs/curl/src/tool_help.c +++ b/contrib/libs/curl/src/tool_help.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -26,6 +26,7 @@ /* use our own printf() functions */ #include "curlx.h" +#include "tool_panykey.h" #include "tool_help.h" #include "tool_libinfo.h" #include "tool_util.h" @@ -75,6 +76,42 @@ static const struct category_descriptors categories[] = { extern const struct helptxt helptext[]; +struct feat { + const char *name; + int bitmask; +}; + +static const struct feat feats[] = { + {"AsynchDNS", CURL_VERSION_ASYNCHDNS}, + {"Debug", CURL_VERSION_DEBUG}, + {"TrackMemory", CURL_VERSION_CURLDEBUG}, + {"IDN", CURL_VERSION_IDN}, + {"IPv6", CURL_VERSION_IPV6}, + {"Largefile", CURL_VERSION_LARGEFILE}, + {"Unicode", CURL_VERSION_UNICODE}, + {"SSPI", CURL_VERSION_SSPI}, + {"GSS-API", CURL_VERSION_GSSAPI}, + {"Kerberos", CURL_VERSION_KERBEROS5}, + {"SPNEGO", CURL_VERSION_SPNEGO}, + {"NTLM", CURL_VERSION_NTLM}, + {"NTLM_WB", CURL_VERSION_NTLM_WB}, + {"SSL", CURL_VERSION_SSL}, + {"libz", CURL_VERSION_LIBZ}, + {"brotli", CURL_VERSION_BROTLI}, + {"zstd", CURL_VERSION_ZSTD}, + {"CharConv", CURL_VERSION_CONV}, + {"TLS-SRP", CURL_VERSION_TLSAUTH_SRP}, + {"HTTP2", CURL_VERSION_HTTP2}, + {"HTTP3", CURL_VERSION_HTTP3}, + {"UnixSockets", CURL_VERSION_UNIX_SOCKETS}, + {"HTTPS-proxy", CURL_VERSION_HTTPS_PROXY}, + {"MultiSSL", CURL_VERSION_MULTI_SSL}, + {"PSL", CURL_VERSION_PSL}, + {"alt-svc", CURL_VERSION_ALTSVC}, + {"HSTS", CURL_VERSION_HSTS}, + {"gsasl", CURL_VERSION_GSASL}, + {"threadsafe", CURL_VERSION_THREADSAFE}, +}; static void print_category(curlhelp_t category) { @@ -151,21 +188,10 @@ void tool_help(char *category) free(category); } -static bool is_debug(void) -{ - const char *const *builtin; - for(builtin = feature_names; *builtin; ++builtin) - if(curl_strequal("debug", *builtin)) - return TRUE; - return FALSE; -} void tool_version_info(void) { - const char *const *builtin; - if(is_debug()) - fprintf(stderr, "WARNING: this libcurl is Debug-enabled, " - "do not use in production\n\n"); + const char *const *proto; printf(CURL_ID "%s\n", curl_version()); #ifdef CURL_PATCHSTAMP @@ -174,20 +200,28 @@ void tool_version_info(void) #else printf("Release-Date: %s\n", LIBCURL_TIMESTAMP); #endif - if(built_in_protos[0]) { - printf("Protocols:"); - for(builtin = built_in_protos; *builtin; ++builtin) { + if(curlinfo->protocols) { + printf("Protocols: "); + for(proto = curlinfo->protocols; *proto; ++proto) { /* Special case: do not list rtmp?* protocols. They may only appear together with "rtmp" */ - if(!curl_strnequal(*builtin, "rtmp", 4) || !builtin[0][4]) - printf(" %s", *builtin); + if(!curl_strnequal(*proto, "rtmp", 4) || !proto[0][4]) + printf("%s ", *proto); } puts(""); /* newline */ } - if(feature_names[0]) { + if(curlinfo->features) { + char *featp[ sizeof(feats) / sizeof(feats[0]) + 1]; + size_t numfeat = 0; + unsigned int i; printf("Features:"); - for(builtin = feature_names; *builtin; ++builtin) - printf(" %s", *builtin); + for(i = 0; i < sizeof(feats)/sizeof(feats[0]); i++) { + if(curlinfo->features & feats[i].bitmask) + featp[numfeat++] = (char *)feats[i].name; + } + qsort(&featp[0], numfeat, sizeof(char *), struplocompare4sort); + for(i = 0; i< numfeat; i++) + printf(" %s", featp[i]); puts(""); /* newline */ } if(strcmp(CURL_VERSION, curlinfo->version)) { diff --git a/contrib/libs/curl/src/tool_help.h b/contrib/libs/curl/src/tool_help.h index fe497ed34a..6fe244e2cf 100644 --- a/contrib/libs/curl/src/tool_help.h +++ b/contrib/libs/curl/src/tool_help.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_helpers.c b/contrib/libs/curl/src/tool_helpers.c index 1e36f0613a..3b2fe9d154 100644 --- a/contrib/libs/curl/src/tool_helpers.c +++ b/contrib/libs/curl/src/tool_helpers.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -99,7 +99,7 @@ int SetHTTPrequest(struct OperationConfig *config, HttpReq req, HttpReq *store) return 0; } warnf(config->global, "You can only select one HTTP request method! " - "You asked for both %s and %s.", + "You asked for both %s and %s.\n", reqname[req], reqname[*store]); return 1; @@ -122,11 +122,11 @@ void customrequest_helper(struct OperationConfig *config, HttpReq req, ; else if(curl_strequal(method, dflt[req])) { notef(config->global, "Unnecessary use of -X or --request, %s is already " - "inferred.", dflt[req]); + "inferred.\n", dflt[req]); } else if(curl_strequal(method, "head")) { warnf(config->global, "Setting custom HTTP method to HEAD with -X/--request may not work " - "the way you want. Consider using -I/--head instead."); + "the way you want. Consider using -I/--head instead.\n"); } } diff --git a/contrib/libs/curl/src/tool_helpers.h b/contrib/libs/curl/src/tool_helpers.h index 2cfbad21a3..cdc84106c8 100644 --- a/contrib/libs/curl/src/tool_helpers.h +++ b/contrib/libs/curl/src/tool_helpers.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_hugehelp.h b/contrib/libs/curl/src/tool_hugehelp.h index ce9af0c545..d5880ac251 100644 --- a/contrib/libs/curl/src/tool_hugehelp.h +++ b/contrib/libs/curl/src/tool_hugehelp.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_libinfo.c b/contrib/libs/curl/src/tool_libinfo.c index a2d30fc520..801fd579f3 100644 --- a/contrib/libs/curl/src/tool_libinfo.c +++ b/contrib/libs/curl/src/tool_libinfo.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -68,74 +68,15 @@ static struct proto_name_tokenp { { NULL, NULL } }; -bool feature_altsvc = FALSE; -bool feature_brotli = FALSE; -bool feature_hsts = FALSE; -bool feature_http2 = FALSE; -bool feature_http3 = FALSE; -bool feature_httpsproxy = FALSE; -bool feature_libz = FALSE; -bool feature_ntlm = FALSE; -bool feature_ntlm_wb = FALSE; -bool feature_spnego = FALSE; -bool feature_ssl = FALSE; -bool feature_tls_srp = FALSE; -bool feature_zstd = FALSE; - -static struct feature_name_presentp { - const char *feature_name; - bool *feature_presentp; - int feature_bitmask; -} const maybe_feature[] = { - /* Keep alphabetically sorted. */ - {"alt-svc", &feature_altsvc, CURL_VERSION_ALTSVC}, - {"AsynchDNS", NULL, CURL_VERSION_ASYNCHDNS}, - {"brotli", &feature_brotli, CURL_VERSION_BROTLI}, - {"CharConv", NULL, CURL_VERSION_CONV}, - {"Debug", NULL, CURL_VERSION_DEBUG}, - {"gsasl", NULL, CURL_VERSION_GSASL}, - {"GSS-API", NULL, CURL_VERSION_GSSAPI}, - {"HSTS", &feature_hsts, CURL_VERSION_HSTS}, - {"HTTP2", &feature_http2, CURL_VERSION_HTTP2}, - {"HTTP3", &feature_http3, CURL_VERSION_HTTP3}, - {"HTTPS-proxy", &feature_httpsproxy, CURL_VERSION_HTTPS_PROXY}, - {"IDN", NULL, CURL_VERSION_IDN}, - {"IPv6", NULL, CURL_VERSION_IPV6}, - {"Kerberos", NULL, CURL_VERSION_KERBEROS5}, - {"Largefile", NULL, CURL_VERSION_LARGEFILE}, - {"libz", &feature_libz, CURL_VERSION_LIBZ}, - {"MultiSSL", NULL, CURL_VERSION_MULTI_SSL}, - {"NTLM", &feature_ntlm, CURL_VERSION_NTLM}, - {"NTLM_WB", &feature_ntlm_wb, CURL_VERSION_NTLM_WB}, - {"PSL", NULL, CURL_VERSION_PSL}, - {"SPNEGO", &feature_spnego, CURL_VERSION_SPNEGO}, - {"SSL", &feature_ssl, CURL_VERSION_SSL}, - {"SSPI", NULL, CURL_VERSION_SSPI}, - {"threadsafe", NULL, CURL_VERSION_THREADSAFE}, - {"TLS-SRP", &feature_tls_srp, CURL_VERSION_TLSAUTH_SRP}, - {"TrackMemory", NULL, CURL_VERSION_CURLDEBUG}, - {"Unicode", NULL, CURL_VERSION_UNICODE}, - {"UnixSockets", NULL, CURL_VERSION_UNIX_SOCKETS}, - {"zstd", &feature_zstd, CURL_VERSION_ZSTD}, - {NULL, NULL, 0} -}; - -static const char *fnames[sizeof(maybe_feature) / sizeof(maybe_feature[0])]; -const char * const *feature_names = fnames; - /* * libcurl_info_init: retrieves run-time information about libcurl, * setting a global pointer 'curlinfo' to libcurl's run-time info * struct, count protocols and flag those we are interested in. - * Global pointer feature_names is set to the feature names array. If - * the latter is not returned by curl_version_info(), it is built from - * the returned features bit mask. */ CURLcode get_libcurl_info(void) { CURLcode result = CURLE_OK; - const char *const *builtin; /* Pointer to libcurl's run-time version information */ curlinfo = curl_version_info(CURLVERSION_NOW); @@ -143,6 +84,7 @@ CURLcode get_libcurl_info(void) return CURLE_FAILED_INIT; if(curlinfo->protocols) { + const char *const *builtin; const struct proto_name_tokenp *p; built_in_protos = curlinfo->protocols; @@ -158,30 +100,6 @@ CURLcode get_libcurl_info(void) proto_count = builtin - built_in_protos; } - if(curlinfo->age >= CURLVERSION_ELEVENTH && curlinfo->feature_names) - feature_names = curlinfo->feature_names; - else { - const struct feature_name_presentp *p; - const char **cpp = fnames; - - for(p = maybe_feature; p->feature_name; p++) - if(curlinfo->features & p->feature_bitmask) - *cpp++ = p->feature_name; - *cpp = NULL; - } - - /* Identify features we are interested in. */ - for(builtin = feature_names; *builtin; builtin++) { - const struct feature_name_presentp *p; - - for(p = maybe_feature; p->feature_name; p++) - if(curl_strequal(p->feature_name, *builtin)) { - if(p->feature_presentp) - *p->feature_presentp = TRUE; - break; - } - } - return CURLE_OK; } diff --git a/contrib/libs/curl/src/tool_libinfo.h b/contrib/libs/curl/src/tool_libinfo.h index 4937e4f3aa..40e5aff3ee 100644 --- a/contrib/libs/curl/src/tool_libinfo.h +++ b/contrib/libs/curl/src/tool_libinfo.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -29,12 +29,9 @@ extern curl_version_info_data *curlinfo; - extern const char * const *built_in_protos; extern size_t proto_count; -extern const char * const *feature_names; - extern const char *proto_file; extern const char *proto_ftp; extern const char *proto_ftps; @@ -45,20 +42,6 @@ extern const char *proto_scp; extern const char *proto_sftp; extern const char *proto_tftp; -extern bool feature_altsvc; -extern bool feature_brotli; -extern bool feature_hsts; -extern bool feature_http2; -extern bool feature_http3; -extern bool feature_httpsproxy; -extern bool feature_libz; -extern bool feature_ntlm; -extern bool feature_ntlm_wb; -extern bool feature_spnego; -extern bool feature_ssl; -extern bool feature_tls_srp; -extern bool feature_zstd; - CURLcode get_libcurl_info(void); const char *proto_token(const char *proto); diff --git a/contrib/libs/curl/src/tool_listhelp.c b/contrib/libs/curl/src/tool_listhelp.c index b1eaf60eb6..6a2d10fb69 100644 --- a/contrib/libs/curl/src/tool_listhelp.c +++ b/contrib/libs/curl/src/tool_listhelp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel.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 @@ -51,9 +51,6 @@ const struct helptxt helptext[] = { {" --basic", "Use HTTP Basic Authentication", CURLHELP_AUTH}, - {" --ca-native", - "Use CA certificates from the native OS", - CURLHELP_TLS}, {" --cacert <file>", "CA certificate to verify peer against", CURLHELP_TLS}, @@ -249,9 +246,6 @@ const struct helptxt helptext[] = { {" --haproxy-protocol", "Send HAProxy PROXY protocol v1 header", CURLHELP_HTTP | CURLHELP_PROXY}, - {" --haproxy-clientip", - "Sets the HAProxy PROXY protocol v1 client IP", - CURLHELP_HTTP | CURLHELP_PROXY}, {"-I, --head", "Show document info only", CURLHELP_HTTP | CURLHELP_FTP | CURLHELP_FILE}, @@ -280,7 +274,7 @@ const struct helptxt helptext[] = { "Use HTTP 1.1", CURLHELP_HTTP}, {" --http2", - "Use HTTP/2", + "Use HTTP 2", CURLHELP_HTTP}, {" --http2-prior-knowledge", "Use HTTP 2 without HTTP/1.1 Upgrade", @@ -288,9 +282,6 @@ const struct helptxt helptext[] = { {" --http3", "Use HTTP v3", CURLHELP_HTTP}, - {" --http3-only", - "Use HTTP v3 only", - CURLHELP_HTTP}, {" --ignore-content-length", "Ignore the size of the remote resource", CURLHELP_HTTP | CURLHELP_FTP}, @@ -480,9 +471,6 @@ const struct helptxt helptext[] = { {" --proxy-basic", "Use Basic authentication on the proxy", CURLHELP_PROXY | CURLHELP_AUTH}, - {" --proxy-ca-native", - "Use CA certificates from the native OS for proxy", - CURLHELP_TLS}, {" --proxy-cacert <file>", "CA certificate to verify peer against for proxy", CURLHELP_PROXY | CURLHELP_TLS}, @@ -507,9 +495,6 @@ const struct helptxt helptext[] = { {" --proxy-header <header/@file>", "Pass custom header(s) to proxy", CURLHELP_PROXY}, - {" --proxy-http2", - "Use HTTP/2 with HTTPS proxy", - CURLHELP_HTTP | CURLHELP_PROXY}, {" --proxy-insecure", "Do HTTPS proxy connections without verifying the proxy", CURLHELP_PROXY | CURLHELP_TLS}, @@ -759,9 +744,6 @@ const struct helptxt helptext[] = { {" --trace-ascii <file>", "Like --trace, but without hex output", CURLHELP_VERBOSE}, - {" --trace-ids", - "Add transfer/connection identifiers to trace/verbose output", - CURLHELP_VERBOSE}, {" --trace-time", "Add time stamps to trace/verbose output", CURLHELP_VERBOSE}, @@ -774,9 +756,6 @@ const struct helptxt helptext[] = { {" --url <url>", "URL to work with", CURLHELP_CURL}, - {" --url-query <data>", - "Add a URL query part", - CURLHELP_HTTP | CURLHELP_POST | CURLHELP_UPLOAD}, {"-B, --use-ascii", "Use ASCII/text transfer", CURLHELP_MISC}, diff --git a/contrib/libs/curl/src/tool_main.c b/contrib/libs/curl/src/tool_main.c index 0624c77b3c..84bc679b53 100644 --- a/contrib/libs/curl/src/tool_main.c +++ b/contrib/libs/curl/src/tool_main.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -50,10 +50,10 @@ #include "tool_doswin.h" #include "tool_msgs.h" #include "tool_operate.h" +#include "tool_panykey.h" #include "tool_vms.h" #include "tool_main.h" #include "tool_libinfo.h" -#include "tool_stderr.h" /* * This is low-level hard-hacking memory leak tracking and similar. Using @@ -78,7 +78,6 @@ int vms_show = 0; * when command-line argument globbing is enabled under the MSYS shell, so turn * it off. */ -extern int _CRT_glob; int _CRT_glob = 0; #endif /* __MINGW32__ */ @@ -157,7 +156,8 @@ static CURLcode main_init(struct GlobalConfig *config) #endif /* Initialise the global config */ - config->showerror = FALSE; /* show errors when silent */ + config->showerror = -1; /* Will show errors */ + config->errors = stderr; /* Default errors to stderr */ config->styled_output = TRUE; /* enable detection */ config->parallel_max = PARALLEL_DEFAULT; @@ -176,17 +176,17 @@ static CURLcode main_init(struct GlobalConfig *config) config->first->global = config; } else { - errorf(config, "error retrieving curl library information"); + errorf(config, "error retrieving curl library information\n"); free(config->first); } } else { - errorf(config, "error initializing curl library"); + errorf(config, "error initializing curl library\n"); free(config->first); } } else { - errorf(config, "error initializing curl"); + errorf(config, "error initializing curl\n"); result = CURLE_FAILED_INIT; } @@ -197,6 +197,10 @@ static void free_globalconfig(struct GlobalConfig *config) { Curl_safefree(config->trace_dump); + if(config->errors_fopened && config->errors) + fclose(config->errors); + config->errors = NULL; + if(config->trace_fopened && config->trace_stream) fclose(config->trace_stream); config->trace_stream = NULL; @@ -233,11 +237,6 @@ static void main_free(struct GlobalConfig *config) ** curl tool main function. */ #ifdef _UNICODE -#if defined(__GNUC__) -/* GCC doesn't know about wmain() */ -#pragma GCC diagnostic ignored "-Wmissing-prototypes" -#pragma GCC diagnostic ignored "-Wmissing-declarations" -#endif int wmain(int argc, wchar_t *argv[]) #else int main(int argc, char *argv[]) @@ -247,8 +246,6 @@ int main(int argc, char *argv[]) struct GlobalConfig global; memset(&global, 0, sizeof(global)); - tool_init_stderr(); - #ifdef WIN32 /* Undocumented diagnostic option to list the full paths of all loaded modules. This is purposely pre-init. */ @@ -262,13 +259,13 @@ int main(int argc, char *argv[]) /* win32_init must be called before other init routines. */ result = win32_init(); if(result) { - errorf(&global, "(%d) Windows-specific init failed", result); + fprintf(stderr, "curl: (%d) Windows-specific init failed.\n", result); return result; } #endif if(main_checkfds()) { - errorf(&global, "out of file descriptors"); + fprintf(stderr, "curl: out of file descriptors\n"); return CURLE_FAILED_INIT; } @@ -295,6 +292,11 @@ int main(int argc, char *argv[]) fflush(NULL); #endif +#ifdef __NOVELL_LIBC__ + if(!getenv("_IN_NETWARE_BASH_")) + tool_pressanykey(); +#endif + #ifdef __VMS vms_special_exit(result, vms_show); #else diff --git a/contrib/libs/curl/src/tool_main.h b/contrib/libs/curl/src/tool_main.h index cae520efbe..a1fd1070e9 100644 --- a/contrib/libs/curl/src/tool_main.h +++ b/contrib/libs/curl/src/tool_main.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_msgs.c b/contrib/libs/curl/src/tool_msgs.c index 9754870a39..a880a667eb 100644 --- a/contrib/libs/curl/src/tool_msgs.c +++ b/contrib/libs/curl/src/tool_msgs.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -42,8 +42,7 @@ static void voutf(struct GlobalConfig *config, va_list ap) { size_t width = (79 - strlen(prefix)); - DEBUGASSERT(!strchr(fmt, '\n')); - if(!config->silent) { + if(!config->mute) { size_t len; char *ptr; char *print_buffer; @@ -55,7 +54,7 @@ static void voutf(struct GlobalConfig *config, ptr = print_buffer; while(len > 0) { - fputs(prefix, stderr); + fputs(prefix, config->errors); if(len > width) { size_t cut = width-1; @@ -68,14 +67,13 @@ static void voutf(struct GlobalConfig *config, max text width then! */ cut = width-1; - (void)fwrite(ptr, cut + 1, 1, stderr); - fputs("\n", stderr); + (void)fwrite(ptr, cut + 1, 1, config->errors); + fputs("\n", config->errors); ptr += cut + 1; /* skip the space too */ len -= cut + 1; } else { - fputs(ptr, stderr); - fputs("\n", stderr); + fputs(ptr, config->errors); len = 0; } } @@ -117,11 +115,9 @@ void helpf(FILE *errors, const char *fmt, ...) if(fmt) { va_list ap; va_start(ap, fmt); - DEBUGASSERT(!strchr(fmt, '\n')); fputs("curl: ", errors); /* prefix it */ vfprintf(errors, fmt, ap); va_end(ap); - fputs("\n", errors); /* newline it */ } fprintf(errors, "curl: try 'curl --help' " #ifdef USE_MANUAL @@ -136,7 +132,7 @@ void helpf(FILE *errors, const char *fmt, ...) */ void errorf(struct GlobalConfig *config, const char *fmt, ...) { - if(!config->silent || config->showerror) { + if(!config->mute) { va_list ap; va_start(ap, fmt); voutf(config, ERROR_PREFIX, fmt, ap); diff --git a/contrib/libs/curl/src/tool_msgs.h b/contrib/libs/curl/src/tool_msgs.h index 9458991c01..e35884e9da 100644 --- a/contrib/libs/curl/src/tool_msgs.h +++ b/contrib/libs/curl/src/tool_msgs.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,7 +24,6 @@ * ***************************************************************************/ #include "tool_setup.h" -#include "tool_cfgable.h" void warnf(struct GlobalConfig *config, const char *fmt, ...); void notef(struct GlobalConfig *config, const char *fmt, ...); diff --git a/contrib/libs/curl/src/tool_operate.c b/contrib/libs/curl/src/tool_operate.c index 9cff7b3364..43c1c5e6c4 100644 --- a/contrib/libs/curl/src/tool_operate.c +++ b/contrib/libs/curl/src/tool_operate.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -205,7 +205,7 @@ static curl_off_t VmsSpecialSize(const char *name, struct per_transfer *transfers; /* first node */ static struct per_transfer *transfersl; /* last node */ -static curl_off_t all_pers; +static long all_pers; /* add_per_transfer creates a new 'per_transfer' node in the linked list of transfers */ @@ -306,7 +306,7 @@ static CURLcode pre_transfer(struct GlobalConfig *global, if((per->infd == -1) || fstat(per->infd, &fileinfo)) #endif { - helpf(stderr, "Can't open '%s'", per->uploadfile); + helpf(global->errors, "Can't open '%s'!\n", per->uploadfile); if(per->infd != -1) { close(per->infd); per->infd = STDIN_FILENO; @@ -319,28 +319,15 @@ static CURLcode pre_transfer(struct GlobalConfig *global, if(S_ISREG(fileinfo.st_mode)) uploadfilesize = fileinfo.st_size; -#ifdef DEBUGBUILD - /* allow dedicated test cases to override */ - { - char *ev = getenv("CURL_UPLOAD_SIZE"); - if(ev) { - int sz = atoi(ev); - uploadfilesize = (curl_off_t)sz; - } - } -#endif - if(uploadfilesize != -1) { struct OperationConfig *config = per->config; /* for the macro below */ #ifdef CURL_DISABLE_LIBCURL_OPTION (void)config; - (void)global; #endif my_setopt(per->curl, CURLOPT_INFILESIZE_LARGE, uploadfilesize); } + per->input.fd = per->infd; } - per->uploadfilesize = uploadfilesize; - per->start = tvnow(); return result; } @@ -395,7 +382,6 @@ static CURLcode post_per_transfer(struct GlobalConfig *global, struct OutStruct *outs = &per->outs; CURL *curl = per->curl; struct OperationConfig *config = per->config; - int rc; if(!curl || !config) return result; @@ -409,26 +395,25 @@ static CURLcode post_per_transfer(struct GlobalConfig *global, #ifdef __VMS if(is_vms_shell()) { /* VMS DCL shell behavior */ - if(global->silent && !global->showerror) + if(!global->showerror) vms_show = VMSSTS_HIDE; } else #endif - if(!config->synthetic_error && result && - (!global->silent || global->showerror)) { + if(!config->synthetic_error && result && global->showerror) { const char *msg = per->errorbuffer; - fprintf(stderr, "curl: (%d) %s\n", result, + fprintf(global->errors, "curl: (%d) %s\n", result, (msg && msg[0]) ? msg : curl_easy_strerror(result)); if(result == CURLE_PEER_FAILED_VERIFICATION) - fputs(CURL_CA_CERT_ERRORMSG, stderr); + fputs(CURL_CA_CERT_ERRORMSG, global->errors); } else if(config->failwithbody) { /* if HTTP response >= 400, return error */ long code = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); if(code >= 400) { - if(!global->silent || global->showerror) - fprintf(stderr, + if(global->showerror) + fprintf(global->errors, "curl: (%d) The requested URL returned error: %ld\n", CURLE_HTTP_RETURNED_ERROR, code); result = CURLE_HTTP_RETURNED_ERROR; @@ -436,9 +421,9 @@ static CURLcode post_per_transfer(struct GlobalConfig *global, } /* Set file extended attributes */ if(!result && config->xattr && outs->fopened && outs->stream) { - rc = fwrite_xattr(curl, per->this_url, fileno(outs->stream)); + int rc = fwrite_xattr(curl, per->this_url, fileno(outs->stream)); if(rc) - warnf(config->global, "Error setting extended attributes on '%s': %s", + warnf(config->global, "Error setting extended attributes on '%s': %s\n", outs->filename, strerror(errno)); } @@ -456,11 +441,12 @@ static CURLcode post_per_transfer(struct GlobalConfig *global, if(!outs->s_isreg && outs->stream) { /* Dump standard stream buffered data */ - rc = fflush(outs->stream); + int rc = fflush(outs->stream); if(!result && rc) { /* something went wrong in the writing process */ result = CURLE_WRITE_ERROR; - errorf(global, "Failed writing body"); + if(global->showerror) + fprintf(global->errors, "curl: (%d) Failed writing body\n", result); } } @@ -587,7 +573,7 @@ static CURLcode post_per_transfer(struct GlobalConfig *global, } warnf(config->global, "Problem %s. " "Will retry in %ld seconds. " - "%ld retries left.", + "%ld retries left.\n", m[retry], sleeptime/1000L, per->retry_numretries); per->retry_numretries--; @@ -597,18 +583,22 @@ static CURLcode post_per_transfer(struct GlobalConfig *global, per->retry_sleep = RETRY_SLEEP_MAX; } if(outs->bytes && outs->filename && outs->stream) { + int rc; /* We have written data to an output file, we truncate file */ - notef(config->global, - "Throwing away %" CURL_FORMAT_CURL_OFF_T " bytes", - outs->bytes); + if(!global->mute) + fprintf(global->errors, "Throwing away %" + CURL_FORMAT_CURL_OFF_T " bytes\n", + outs->bytes); fflush(outs->stream); /* truncate file at the position where we started appending */ #ifdef HAVE_FTRUNCATE if(ftruncate(fileno(outs->stream), outs->init)) { /* when truncate fails, we can't just append as then we'll create something strange, bail out */ - errorf(config->global, "Failed to truncate file"); + if(global->showerror) + fprintf(global->errors, + "curl: (23) Failed to truncate file\n"); return CURLE_WRITE_ERROR; } /* now seek to the end of the file, the position where we @@ -622,7 +612,9 @@ static CURLcode post_per_transfer(struct GlobalConfig *global, rc = fseek(outs->stream, (long)outs->init, SEEK_SET); #endif if(rc) { - errorf(config->global, "Failed seeking to end of file"); + if(global->showerror) + fprintf(global->errors, + "curl: (23) Failed seeking to end of file\n"); return CURLE_WRITE_ERROR; } outs->bytes = 0; /* clear for next round */ @@ -632,7 +624,7 @@ static CURLcode post_per_transfer(struct GlobalConfig *global, return CURLE_OK; } } /* if retry_numretries */ -noretry: + noretry: if((global->progressmode == CURL_PROGRESS_BAR) && per->progressbar.calls) @@ -642,14 +634,15 @@ noretry: /* Close the outs file */ if(outs->fopened && outs->stream) { - rc = fclose(outs->stream); + int rc = fclose(outs->stream); if(!result && rc) { /* something went wrong in the writing process */ result = CURLE_WRITE_ERROR; - errorf(config->global, "curl: (%d) Failed writing body", result); + if(global->showerror) + fprintf(global->errors, "curl: (%d) Failed writing body\n", result); } if(result && config->rm_partial) { - notef(global, "Removing output file: %s", outs->filename); + notef(global, "Removing output file: %s\n", outs->filename); unlink(outs->filename); } } @@ -666,7 +659,7 @@ noretry: /* Write the --write-out data before cleanup but after result is final */ if(config->writeout) - ourWriteOut(config, per, result); + ourWriteOut(config->writeout, per, result); /* Close function-local opened file descriptors */ if(per->heads.fopened && per->heads.stream) @@ -737,11 +730,11 @@ static CURLcode single_transfer(struct GlobalConfig *global, if(config->postfields) { if(config->use_httpget) { if(!httpgetfields) { - /* Use the postfields data for an HTTP get */ + /* Use the postfields data for a http get */ httpgetfields = state->httpgetfields = strdup(config->postfields); Curl_safefree(config->postfields); if(!httpgetfields) { - errorf(global, "out of memory"); + errorf(global, "out of memory\n"); result = CURLE_OUT_OF_MEMORY; } else if(SetHTTPrequest(config, @@ -784,7 +777,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, state->up = 0; if(!warn_more_options) { /* only show this once */ - warnf(config->global, "Got more output options than URLs"); + warnf(config->global, "Got more output options than URLs\n"); warn_more_options = TRUE; } continue; /* next URL please */ @@ -794,7 +787,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, if(urlnode->outfile && !state->outfiles) { state->outfiles = strdup(urlnode->outfile); if(!state->outfiles) { - errorf(global, "out of memory"); + errorf(global, "out of memory\n"); result = CURLE_OUT_OF_MEMORY; break; } @@ -805,15 +798,14 @@ static CURLcode single_transfer(struct GlobalConfig *global, if(!config->globoff && infiles && !inglob) { /* Unless explicitly shut off */ result = glob_url(&inglob, infiles, &state->infilenum, - (!global->silent || global->showerror)? - stderr:NULL); + global->showerror?global->errors:NULL); if(result) break; config->state.inglob = inglob; } { - curl_off_t urlnum; + unsigned long urlnum; if(!state->up && !infiles) Curl_nop_stmt; @@ -822,12 +814,12 @@ static CURLcode single_transfer(struct GlobalConfig *global, if(inglob) { result = glob_next_url(&state->uploadfile, inglob); if(result == CURLE_OUT_OF_MEMORY) - errorf(global, "out of memory"); + errorf(global, "out of memory\n"); } else if(!state->up) { state->uploadfile = strdup(infiles); if(!state->uploadfile) { - errorf(global, "out of memory"); + errorf(global, "out of memory\n"); result = CURLE_OUT_OF_MEMORY; } } @@ -841,8 +833,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, /* Unless explicitly shut off, we expand '{...}' and '[...]' expressions and return total number of URLs in pattern set */ result = glob_url(&state->urls, urlnode->url, &state->urlnum, - (!global->silent || global->showerror)? - stderr:NULL); + global->showerror?global->errors:NULL); if(result) break; urlnum = state->urlnum; @@ -856,6 +847,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, if(state->up < state->infilenum) { struct per_transfer *per = NULL; struct OutStruct *outs; + struct InStruct *input; struct OutStruct *heads; struct OutStruct *etag_save; struct HdrCbData *hdrcbdata = NULL; @@ -878,7 +870,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, FILE *file = fopen(config->etag_compare_file, FOPEN_READTEXT); if(!file && !config->etag_save_file) { errorf(global, - "Failed to open %s", config->etag_compare_file); + "Failed to open %s\n", config->etag_compare_file); result = CURLE_READ_ERROR; break; } @@ -895,7 +887,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, if(file) fclose(file); errorf(global, - "Failed to allocate memory for custom etag header"); + "Failed to allocate memory for custom etag header\n"); result = CURLE_OUT_OF_MEMORY; break; } @@ -918,7 +910,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, FILE *newfile = fopen(config->etag_save_file, "wb"); if(!newfile) { warnf(global, "Failed creating file for saving etags: \"%s\". " - "Skip this transfer", config->etag_save_file); + "Skip this transfer\n", config->etag_save_file); Curl_safefree(state->outfiles); glob_cleanup(state->urls); return CURLE_OK; @@ -976,25 +968,9 @@ static CURLcode single_transfer(struct GlobalConfig *global, /* open file for output: */ if(strcmp(config->headerfile, "-")) { FILE *newfile; - - /* - * Since every transfer has its own file handle for dumping - * the headers, we need to open it in append mode, since transfers - * might finish in any order. - * The first transfer just clears the file. - * TODO: Consider placing the file handle inside the - * OperationConfig, so that it does not need to be opened/closed - * for every transfer. - */ - if(!per->prev || per->prev->config != config) { - newfile = fopen(config->headerfile, "wb"); - if(newfile) - fclose(newfile); - } - newfile = fopen(config->headerfile, "ab"); - + newfile = fopen(config->headerfile, per->prev == NULL?"wb":"ab"); if(!newfile) { - errorf(global, "Failed to open %s", config->headerfile); + warnf(global, "Failed to open %s\n", config->headerfile); result = CURLE_WRITE_ERROR; break; } @@ -1014,6 +990,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, hdrcbdata = &per->hdrcbdata; outs = &per->outs; + input = &per->input; per->outfile = NULL; per->infdopen = FALSE; @@ -1060,11 +1037,11 @@ static CURLcode single_transfer(struct GlobalConfig *global, result = get_url_file_name(&per->outfile, per->this_url); if(result) { errorf(global, "Failed to extract a sensible file name" - " from the URL to use for storage"); + " from the URL to use for storage!\n"); break; } if(!*per->outfile && !config->content_disposition) { - errorf(global, "Remote file name has no length"); + errorf(global, "Remote file name has no length!\n"); result = CURLE_WRITE_ERROR; break; } @@ -1076,11 +1053,11 @@ static CURLcode single_transfer(struct GlobalConfig *global, Curl_safefree(storefile); if(result) { /* bad globbing */ - warnf(global, "bad output glob"); + warnf(global, "bad output glob!\n"); break; } if(!*per->outfile) { - warnf(global, "output glob produces empty string"); + warnf(global, "output glob produces empty string!\n"); result = CURLE_WRITE_ERROR; break; } @@ -1099,7 +1076,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, file output call */ if(config->create_dirs) { - result = create_dir_hierarchy(per->outfile, global); + result = create_dir_hierarchy(per->outfile, global->errors); /* create_dir_hierarchy shows error upon CURLE_WRITE_ERROR */ if(result) break; @@ -1135,7 +1112,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, FILE *file = fopen(per->outfile, "ab"); #endif if(!file) { - errorf(global, "Can't open '%s'", per->outfile); + errorf(global, "Can't open '%s'!\n", per->outfile); result = CURLE_WRITE_ERROR; break; } @@ -1182,7 +1159,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, warnf(global, "Using --anyauth or --proxy-anyauth with upload from stdin" " involves a big risk of it not working. Use a temporary" - " file or a fixed auth type instead"); + " file or a fixed auth type instead!\n"); } DEBUGASSERT(per->infdopen == FALSE); @@ -1192,7 +1169,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, if(!strcmp(per->uploadfile, ".")) { if(curlx_nonblock((curl_socket_t)per->infd, TRUE) < 0) warnf(global, - "fcntl failed on fd=%d: %s", per->infd, strerror(errno)); + "fcntl failed on fd=%d: %s\n", per->infd, strerror(errno)); } } @@ -1211,38 +1188,28 @@ static CURLcode single_transfer(struct GlobalConfig *global, global->isatty = orig_isatty; } - if(httpgetfields || config->query) { - char *q = httpgetfields ? httpgetfields : config->query; + if(httpgetfields) { CURLU *uh = curl_url(); if(uh) { - CURLUcode uerr; - uerr = curl_url_set(uh, CURLUPART_URL, per->this_url, - CURLU_GUESS_SCHEME); - if(uerr) { - result = urlerr_cvt(uerr); - errorf(global, "(%d) Could not parse the URL, " - "failed to set query", result); - config->synthetic_error = TRUE; - } - else { - char *updated = NULL; - uerr = curl_url_set(uh, CURLUPART_QUERY, q, CURLU_APPENDQUERY); - if(!uerr) - uerr = curl_url_get(uh, CURLUPART_URL, &updated, - CURLU_GUESS_SCHEME); - if(uerr) - result = urlerr_cvt(uerr); - else { - Curl_safefree(per->this_url); /* free previous URL */ - per->this_url = updated; /* use our new URL instead! */ - } + char *updated; + if(curl_url_set(uh, CURLUPART_URL, per->this_url, + CURLU_GUESS_SCHEME) || + curl_url_set(uh, CURLUPART_QUERY, httpgetfields, + CURLU_APPENDQUERY) || + curl_url_get(uh, CURLUPART_URL, &updated, CURLU_GUESS_SCHEME)) { + curl_url_cleanup(uh); + result = CURLE_OUT_OF_MEMORY; + break; } + Curl_safefree(per->this_url); /* free previous URL */ + per->this_url = updated; /* use our new URL instead! */ curl_url_cleanup(uh); - if(result) - break; } } + if(!global->errors) + global->errors = stderr; + if((!per->outfile || !strcmp(per->outfile, "-")) && !config->use_ascii) { /* We get the output to stdout and we have not got the ASCII/text @@ -1261,15 +1228,6 @@ static CURLcode single_transfer(struct GlobalConfig *global, use_proto = url_proto(per->this_url); - /* On most modern OSes, exiting works thoroughly, - we'll clean everything up via exit(), so don't bother with - slow cleanups. Crappy ones might need to skip this. - Note: avoid having this setopt added to the --libcurl source - output. */ - result = curl_easy_setopt(curl, CURLOPT_QUICK_EXIT, 1L); - if(result) - break; - if(!config->tcp_nodelay) my_setopt(curl, CURLOPT_TCP_NODELAY, 0L); @@ -1283,6 +1241,8 @@ static CURLcode single_transfer(struct GlobalConfig *global, /* what call to write */ my_setopt(curl, CURLOPT_WRITEFUNCTION, tool_write_cb); + /* for uploads */ + input->config = config; /* Note that if CURLOPT_READFUNCTION is fread (the default), then * lib/telnet.c will Curl_poll() on the input file descriptor * rather than calling the READFUNCTION at regular intervals. @@ -1290,36 +1250,24 @@ static CURLcode single_transfer(struct GlobalConfig *global, * behavior, by omitting to set the READFUNCTION & READDATA options, * have not been determined. */ - my_setopt(curl, CURLOPT_READDATA, per); + my_setopt(curl, CURLOPT_READDATA, input); /* what call to read */ my_setopt(curl, CURLOPT_READFUNCTION, tool_read_cb); /* in 7.18.0, the CURLOPT_SEEKFUNCTION/DATA pair is taking over what CURLOPT_IOCTLFUNCTION/DATA pair previously provided for seeking */ - my_setopt(curl, CURLOPT_SEEKDATA, per); + my_setopt(curl, CURLOPT_SEEKDATA, input); my_setopt(curl, CURLOPT_SEEKFUNCTION, tool_seek_cb); - { -#ifdef CURLDEBUG - char *env = getenv("CURL_BUFFERSIZE"); - if(env) { - long size = strtol(env, NULL, 10); - if(size) - my_setopt(curl, CURLOPT_BUFFERSIZE, size); - } - else -#endif - if(config->recvpersecond && - (config->recvpersecond < BUFFER_SIZE)) - /* use a smaller sized buffer for better sleeps */ - my_setopt(curl, CURLOPT_BUFFERSIZE, (long)config->recvpersecond); - else - my_setopt(curl, CURLOPT_BUFFERSIZE, (long)BUFFER_SIZE); - } + if(config->recvpersecond && + (config->recvpersecond < BUFFER_SIZE)) + /* use a smaller sized buffer for better sleeps */ + my_setopt(curl, CURLOPT_BUFFERSIZE, (long)config->recvpersecond); + else + my_setopt(curl, CURLOPT_BUFFERSIZE, (long)BUFFER_SIZE); my_setopt_str(curl, CURLOPT_URL, per->this_url); - my_setopt(curl, CURLOPT_NOPROGRESS, - global->noprogress || global->silent?1L:0L); + my_setopt(curl, CURLOPT_NOPROGRESS, global->noprogress?1L:0L); if(config->no_body) my_setopt(curl, CURLOPT_NOBODY, 1L); @@ -1329,7 +1277,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, my_setopt_str(curl, CURLOPT_PROXY, config->proxy); if(config->proxy && result) { - errorf(global, "proxy support is disabled in this libcurl"); + errorf(global, "proxy support is disabled in this libcurl\n"); config->synthetic_error = TRUE; result = CURLE_NOT_BUILT_IN; break; @@ -1396,34 +1344,23 @@ static CURLcode single_transfer(struct GlobalConfig *global, per->errorbuffer = global_errorbuffer; my_setopt(curl, CURLOPT_ERRORBUFFER, global_errorbuffer); } - my_setopt(curl, CURLOPT_TIMEOUT_MS, config->timeout_ms); + my_setopt(curl, CURLOPT_TIMEOUT_MS, (long)(config->timeout * 1000)); switch(config->httpreq) { case HTTPREQ_SIMPLEPOST: - if(config->resume_from) { - errorf(global, "cannot mix --continue-at with --data"); - result = CURLE_FAILED_INIT; - } - else { - my_setopt_str(curl, CURLOPT_POSTFIELDS, - config->postfields); - my_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, - config->postfieldsize); - } + my_setopt_str(curl, CURLOPT_POSTFIELDS, + config->postfields); + my_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, + config->postfieldsize); break; case HTTPREQ_MIMEPOST: /* free previous remainders */ curl_mime_free(config->mimepost); config->mimepost = NULL; - if(config->resume_from) { - errorf(global, "cannot mix --continue-at with --form"); - result = CURLE_FAILED_INIT; - } - else { - result = tool2curlmime(curl, config->mimeroot, &config->mimepost); - if(!result) - my_setopt_mimepost(curl, CURLOPT_MIMEPOST, config->mimepost); - } + result = tool2curlmime(curl, config->mimeroot, &config->mimepost); + if(result) + break; + my_setopt_mimepost(curl, CURLOPT_MIMEPOST, config->mimepost); break; default: break; @@ -1459,7 +1396,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, /* new in libcurl 7.36.0 */ if(config->proxyheaders) { my_setopt_slist(curl, CURLOPT_PROXYHEADER, config->proxyheaders); - my_setopt(curl, CURLOPT_HEADEROPT, (long)CURLHEADER_SEPARATE); + my_setopt(curl, CURLOPT_HEADEROPT, CURLHEADER_SEPARATE); } /* new in libcurl 7.5 */ @@ -1467,8 +1404,9 @@ static CURLcode single_transfer(struct GlobalConfig *global, if(config->httpversion) my_setopt_enum(curl, CURLOPT_HTTP_VERSION, config->httpversion); - else if(feature_http2) + else if(curlinfo->features & CURL_VERSION_HTTP2) { my_setopt_enum(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); + } /* curl 7.19.1 (the 301 version existed in 7.18.2), 303 was added in 7.26.0 */ @@ -1491,7 +1429,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, my_setopt(curl, CURLOPT_HTTP09_ALLOWED, config->http09_allowed ? 1L : 0L); if(result) { - errorf(global, "HTTP/0.9 is not supported in this build"); + errorf(global, "HTTP/0.9 is not supported in this build!\n"); return result; } @@ -1546,7 +1484,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, if(config->capath) { result = res_setopt_str(curl, CURLOPT_CAPATH, config->capath); if(result == CURLE_NOT_BUILT_IN) { - warnf(global, "ignoring %s, not supported by libcurl", + warnf(global, "ignoring %s, not supported by libcurl\n", capath_from_env? "SSL_CERT_DIR environment variable":"--capath"); } @@ -1563,7 +1501,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, if(result == CURLE_NOT_BUILT_IN) { if(config->proxy_capath) { warnf(global, - "ignoring --proxy-capath, not supported by libcurl"); + "ignoring --proxy-capath, not supported by libcurl\n"); } } else if(result) @@ -1583,10 +1521,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, if(config->ssl_ec_curves) my_setopt_str(curl, CURLOPT_SSL_EC_CURVES, config->ssl_ec_curves); - if(config->writeout) - my_setopt_str(curl, CURLOPT_CERTINFO, 1L); - - if(feature_ssl) { + if(curlinfo->features & CURL_VERSION_SSL) { /* Check if config->cert is a PKCS#11 URI and set the * config->cert_type if necessary */ if(config->cert) { @@ -1779,9 +1714,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, (config->proxy_ssl_allow_beast ? CURLSSLOPT_ALLOW_BEAST : 0) | (config->proxy_ssl_auto_client_cert ? - CURLSSLOPT_AUTO_CLIENT_CERT : 0) | - (config->proxy_native_ca_store ? - CURLSSLOPT_NATIVE_CA : 0); + CURLSSLOPT_AUTO_CLIENT_CERT : 0); if(mask) my_setopt_bitmask(curl, CURLOPT_PROXY_SSL_OPTIONS, mask); @@ -1805,7 +1738,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, break; } else - warnf(global, "Couldn't find a known_hosts file"); + warnf(global, "Couldn't find a known_hosts file!"); } if(config->no_body || config->remote_time) { @@ -1823,7 +1756,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, struct curl_slist *cl; /* The maximum size needs to match MAX_NAME in cookie.h */ -#define MAX_COOKIE_LINE 8200 +#define MAX_COOKIE_LINE 4096 curlx_dyn_init(&cookies, MAX_COOKIE_LINE); for(cl = config->cookies; cl; cl = cl->next) { if(cl == config->cookies) @@ -1834,7 +1767,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, if(result) { warnf(global, "skipped provided cookie, the cookie header " - "would go over %u bytes", MAX_COOKIE_LINE); + "would go over %u bytes\n", MAX_COOKIE_LINE); break; } } @@ -1861,7 +1794,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, my_setopt(curl, CURLOPT_TIMEVALUE_LARGE, config->condtime); my_setopt_str(curl, CURLOPT_CUSTOMREQUEST, config->customrequest); customrequest_helper(config, config->httpreq, config->customrequest); - my_setopt(curl, CURLOPT_STDERR, stderr); + my_setopt(curl, CURLOPT_STDERR, global->errors); /* three new ones in libcurl 7.3: */ my_setopt_str(curl, CURLOPT_INTERFACE, config->iface); @@ -1869,7 +1802,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, progressbarinit(&per->progressbar, config); if((global->progressmode == CURL_PROGRESS_BAR) && - !global->noprogress && !global->silent) { + !global->noprogress && !global->mute) { /* we want the alternative style, then we have to implement it ourselves! */ my_setopt(curl, CURLOPT_XFERINFOFUNCTION, tool_progress_cb); @@ -1893,13 +1826,14 @@ static CURLcode single_transfer(struct GlobalConfig *global, if(config->dns_ipv4_addr) my_setopt_str(curl, CURLOPT_DNS_LOCAL_IP4, config->dns_ipv4_addr); if(config->dns_ipv6_addr) - my_setopt_str(curl, CURLOPT_DNS_LOCAL_IP6, config->dns_ipv6_addr); + my_setopt_str(curl, CURLOPT_DNS_LOCAL_IP6, config->dns_ipv6_addr); /* new in libcurl 7.6.2: */ my_setopt_slist(curl, CURLOPT_TELNETOPTIONS, config->telnet_options); /* new in libcurl 7.7: */ - my_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, config->connecttimeout_ms); + my_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, + (long)(config->connecttimeout * 1000)); if(config->doh_url) my_setopt_str(curl, CURLOPT_DOH_URL, config->doh_url); @@ -1974,7 +1908,8 @@ static CURLcode single_transfer(struct GlobalConfig *global, /* new in curl 7.19.4 */ if(config->socks5_gssapi_nec) - my_setopt_str(curl, CURLOPT_SOCKS5_GSSAPI_NEC, 1L); + my_setopt_str(curl, CURLOPT_SOCKS5_GSSAPI_NEC, + config->socks5_gssapi_nec); /* new in curl 7.55.0 */ if(config->socks5_auth) @@ -2046,8 +1981,8 @@ static CURLcode single_transfer(struct GlobalConfig *global, my_setopt_slist(curl, CURLOPT_MAIL_RCPT, config->mail_rcpt); /* curl 7.69.x */ - my_setopt(curl, CURLOPT_MAIL_RCPT_ALLOWFAILS, - config->mail_rcpt_allowfails ? 1L : 0L); + my_setopt(curl, CURLOPT_MAIL_RCPT_ALLLOWFAILS, + config->mail_rcpt_allowfails ? 1L : 0L); /* curl 7.20.x */ if(config->ftp_pret) @@ -2086,7 +2021,7 @@ static CURLcode single_transfer(struct GlobalConfig *global, my_setopt_slist(curl, CURLOPT_CONNECT_TO, config->connect_to); /* new in 7.21.4 */ - if(feature_tls_srp) { + if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP) { if(config->tls_username) my_setopt_str(curl, CURLOPT_TLSAUTH_USERNAME, config->tls_username); @@ -2144,9 +2079,9 @@ static CURLcode single_transfer(struct GlobalConfig *global, my_setopt_str(curl, CURLOPT_DEFAULT_PROTOCOL, config->proto_default); /* new in 7.47.0 */ - if(config->expect100timeout_ms > 0) + if(config->expect100timeout > 0) my_setopt_str(curl, CURLOPT_EXPECT_100_TIMEOUT_MS, - config->expect100timeout_ms); + (long)(config->expect100timeout*1000)); /* new in 7.48.0 */ if(config->tftp_no_options && proto_tftp) @@ -2161,11 +2096,6 @@ static CURLcode single_transfer(struct GlobalConfig *global, if(config->haproxy_protocol) my_setopt(curl, CURLOPT_HAPROXYPROTOCOL, 1L); - /* new in 8.2.0 */ - if(config->haproxy_clientip) - my_setopt_str(curl, CURLOPT_HAPROXY_CLIENT_IP, - config->haproxy_clientip); - if(config->disallow_username_in_url) my_setopt(curl, CURLOPT_DISALLOW_USERNAME_IN_URL, 1L); @@ -2368,8 +2298,7 @@ static CURLcode parallel_transfers(struct GlobalConfig *global, curl_easy_getinfo(easy, CURLINFO_PRIVATE, (void *)&ended); curl_multi_remove_handle(multi, easy); - if(ended->abort && (tres == CURLE_ABORTED_BY_CALLBACK) && - ended->errorbuffer) { + if(ended->abort && tres == CURLE_ABORTED_BY_CALLBACK) { msnprintf(ended->errorbuffer, CURL_ERROR_SIZE, "Transfer aborted due to critical error " "in another transfer"); @@ -2450,7 +2379,7 @@ static CURLcode serial_transfers(struct GlobalConfig *global, if(result) return result; if(!added) { - errorf(global, "no transfer performed"); + errorf(global, "no transfer performed\n"); return CURLE_READ_ERROR; } for(per = transfers; per;) { @@ -2467,6 +2396,7 @@ static CURLcode serial_transfers(struct GlobalConfig *global, if(result) break; } + start = tvnow(); #ifdef CURLDEBUG if(global->test_event_based) @@ -2487,10 +2417,8 @@ static CURLcode serial_transfers(struct GlobalConfig *global, else { /* setup the next one just before we delete this */ result = create_transfer(global, share, &added); - if(result) { - returncode = result; + if(result) bailout = TRUE; - } } per = del_per_transfer(per); @@ -2503,7 +2431,7 @@ static CURLcode serial_transfers(struct GlobalConfig *global, milliseconds */ long milli = tvdiff(tvnow(), start); if(milli < global->ms_per_transfer) { - notef(global, "Transfer took %ld ms, waits %ldms as set by --rate", + notef(global, "Transfer took %ld ms, waits %ldms as set by --rate\n", milli, global->ms_per_transfer - milli); /* The transfer took less time than wanted. Wait a little. */ tool_go_sleep(global->ms_per_transfer - milli); @@ -2532,7 +2460,7 @@ static CURLcode transfer_per_config(struct GlobalConfig *global, /* Check we have a url */ if(!config->url_list || !config->url_list->url) { - helpf(stderr, "(%d) no URL specified", CURLE_FAILED_INIT); + helpf(global->errors, "no URL specified!\n"); return CURLE_FAILED_INIT; } @@ -2579,7 +2507,7 @@ static CURLcode transfer_per_config(struct GlobalConfig *global, if(!config->cacert) { curl_free(env); curl_easy_cleanup(curltls); - errorf(global, "out of memory"); + errorf(global, "out of memory\n"); return CURLE_OUT_OF_MEMORY; } } @@ -2590,7 +2518,7 @@ static CURLcode transfer_per_config(struct GlobalConfig *global, if(!config->capath) { curl_free(env); curl_easy_cleanup(curltls); - errorf(global, "out of memory"); + helpf(global->errors, "out of memory\n"); return CURLE_OUT_OF_MEMORY; } capath_from_env = true; @@ -2602,7 +2530,7 @@ static CURLcode transfer_per_config(struct GlobalConfig *global, if(!config->cacert) { curl_free(env); curl_easy_cleanup(curltls); - errorf(global, "out of memory"); + errorf(global, "out of memory\n"); return CURLE_OUT_OF_MEMORY; } } @@ -2694,10 +2622,9 @@ CURLcode operate(struct GlobalConfig *global, int argc, argv_item_t argv[]) CURLcode result = CURLE_OK; char *first_arg = argc > 1 ? curlx_convert_tchar_to_UTF8(argv[1]) : NULL; + /* Setup proper locale from environment */ #ifdef HAVE_SETLOCALE - /* Override locale for number parsing (only) */ setlocale(LC_ALL, ""); - setlocale(LC_NUMERIC, "C"); #endif /* Parse .curlrc if necessary */ @@ -2708,7 +2635,7 @@ CURLcode operate(struct GlobalConfig *global, int argc, argv_item_t argv[]) /* If we had no arguments then make sure a url was specified in .curlrc */ if((argc < 2) && (!global->first->url_list)) { - helpf(stderr, NULL); + helpf(global->errors, NULL); result = CURLE_FAILED_INIT; } } @@ -2764,7 +2691,6 @@ CURLcode operate(struct GlobalConfig *global, int argc, argv_item_t argv[]) curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION); curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT); curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_PSL); - curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_HSTS); /* Get the required arguments for each operation */ do { @@ -2789,7 +2715,7 @@ CURLcode operate(struct GlobalConfig *global, int argc, argv_item_t argv[]) } } else - errorf(global, "out of memory"); + errorf(global, "out of memory\n"); } } diff --git a/contrib/libs/curl/src/tool_operate.h b/contrib/libs/curl/src/tool_operate.h index 21a7f929d0..c714da1bc2 100644 --- a/contrib/libs/curl/src/tool_operate.h +++ b/contrib/libs/curl/src/tool_operate.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,12 +33,10 @@ struct per_transfer { struct per_transfer *next; struct per_transfer *prev; struct OperationConfig *config; /* for this transfer */ - struct curl_certinfo *certinfo; CURL *curl; long retry_numretries; long retry_sleep_default; long retry_sleep; - struct timeval start; /* start of this transfer */ struct timeval retrystart; char *this_url; unsigned int urlnum; /* the index of the given URL */ @@ -50,6 +48,7 @@ struct per_transfer { struct OutStruct outs; struct OutStruct heads; struct OutStruct etag_save; + struct InStruct input; struct HdrCbData hdrcbdata; long num_headers; bool was_last_header_empty; @@ -67,8 +66,6 @@ struct per_transfer { curl_off_t dlnow; curl_off_t ultotal; curl_off_t ulnow; - curl_off_t uploadfilesize; /* expected total amount */ - curl_off_t uploadedsofar; /* amount delivered from the callback */ bool dltotal_added; /* if the total has been added from this */ bool ultotal_added; diff --git a/contrib/libs/curl/src/tool_operhlp.c b/contrib/libs/curl/src/tool_operhlp.c index a964d796de..eb58772fb9 100644 --- a/contrib/libs/curl/src/tool_operhlp.c +++ b/contrib/libs/curl/src/tool_operhlp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -71,51 +71,23 @@ bool stdin_upload(const char *uploadfile) !strcmp(uploadfile, ".")) ? TRUE : FALSE; } -/* Convert a CURLUcode into a CURLcode */ -CURLcode urlerr_cvt(CURLUcode ucode) -{ - if(ucode == CURLUE_OUT_OF_MEMORY) - return CURLE_OUT_OF_MEMORY; - else if(ucode == CURLUE_UNSUPPORTED_SCHEME) - return CURLE_UNSUPPORTED_PROTOCOL; - else if(ucode == CURLUE_LACKS_IDN) - return CURLE_NOT_BUILT_IN; - else if(ucode == CURLUE_BAD_HANDLE) - return CURLE_BAD_FUNCTION_ARGUMENT; - return CURLE_URL_MALFORMAT; -} - /* * Adds the file name to the URL if it doesn't already have one. * url will be freed before return if the returned pointer is different */ CURLcode add_file_name_to_url(CURL *curl, char **inurlp, const char *filename) { - CURLcode result = CURLE_URL_MALFORMAT; - CURLUcode uerr; + CURLcode result = CURLE_OUT_OF_MEMORY; CURLU *uh = curl_url(); char *path = NULL; - char *query = NULL; if(uh) { char *ptr; - uerr = curl_url_set(uh, CURLUPART_URL, *inurlp, - CURLU_GUESS_SCHEME|CURLU_NON_SUPPORT_SCHEME); - if(uerr) { - result = urlerr_cvt(uerr); + if(curl_url_set(uh, CURLUPART_URL, *inurlp, + CURLU_GUESS_SCHEME|CURLU_NON_SUPPORT_SCHEME)) goto fail; - } - uerr = curl_url_get(uh, CURLUPART_PATH, &path, 0); - if(uerr) { - result = urlerr_cvt(uerr); + if(curl_url_get(uh, CURLUPART_PATH, &path, 0)) goto fail; - } - uerr = curl_url_get(uh, CURLUPART_QUERY, &query, 0); - if(!uerr && query) { - curl_free(query); - curl_free(path); - curl_url_cleanup(uh); - return CURLE_OK; - } + ptr = strrchr(path, '/'); if(!ptr || !*++ptr) { /* The URL path has no file name part, add the local file name. In order @@ -139,6 +111,7 @@ CURLcode add_file_name_to_url(CURL *curl, char **inurlp, const char *filename) if(encfile) { char *newpath; char *newurl; + CURLUcode uerr; if(ptr) /* there is a trailing slash on the path */ newpath = aprintf("%s%s", path, encfile); @@ -152,15 +125,10 @@ CURLcode add_file_name_to_url(CURL *curl, char **inurlp, const char *filename) goto fail; uerr = curl_url_set(uh, CURLUPART_PATH, newpath, 0); free(newpath); - if(uerr) { - result = urlerr_cvt(uerr); + if(uerr) goto fail; - } - uerr = curl_url_get(uh, CURLUPART_URL, &newurl, CURLU_DEFAULT_SCHEME); - if(uerr) { - result = urlerr_cvt(uerr); + if(curl_url_get(uh, CURLUPART_URL, &newurl, CURLU_DEFAULT_SCHEME)) goto fail; - } free(*inurlp); *inurlp = newurl; result = CURLE_OK; @@ -170,7 +138,7 @@ CURLcode add_file_name_to_url(CURL *curl, char **inurlp, const char *filename) /* nothing to do */ result = CURLE_OK; } -fail: + fail: curl_url_cleanup(uh); curl_free(path); return result; @@ -185,70 +153,63 @@ CURLcode get_url_file_name(char **filename, const char *url) const char *pc, *pc2; CURLU *uh = curl_url(); char *path = NULL; - CURLUcode uerr; if(!uh) return CURLE_OUT_OF_MEMORY; *filename = NULL; - uerr = curl_url_set(uh, CURLUPART_URL, url, CURLU_GUESS_SCHEME); - if(!uerr) { - uerr = curl_url_get(uh, CURLUPART_PATH, &path, 0); - if(!uerr) { - curl_url_cleanup(uh); + if(!curl_url_set(uh, CURLUPART_URL, url, CURLU_GUESS_SCHEME) && + !curl_url_get(uh, CURLUPART_PATH, &path, 0)) { + curl_url_cleanup(uh); - pc = strrchr(path, '/'); - pc2 = strrchr(pc ? pc + 1 : path, '\\'); - if(pc2) - pc = pc2; + pc = strrchr(path, '/'); + pc2 = strrchr(pc ? pc + 1 : path, '\\'); + if(pc2) + pc = pc2; - if(pc) - /* duplicate the string beyond the slash */ - pc++; - else - /* no slash => empty string */ - pc = ""; + if(pc) + /* duplicate the string beyond the slash */ + pc++; + else + /* no slash => empty string */ + pc = ""; - *filename = strdup(pc); - curl_free(path); - if(!*filename) - return CURLE_OUT_OF_MEMORY; + *filename = strdup(pc); + curl_free(path); + if(!*filename) + return CURLE_OUT_OF_MEMORY; #if defined(MSDOS) || defined(WIN32) - { - char *sanitized; - SANITIZEcode sc = sanitize_file_name(&sanitized, *filename, 0); - Curl_safefree(*filename); - if(sc) { - if(sc == SANITIZE_ERR_OUT_OF_MEMORY) - return CURLE_OUT_OF_MEMORY; - return CURLE_URL_MALFORMAT; - } - *filename = sanitized; - } + { + char *sanitized; + SANITIZEcode sc = sanitize_file_name(&sanitized, *filename, 0); + Curl_safefree(*filename); + if(sc) + return CURLE_URL_MALFORMAT; + *filename = sanitized; + } #endif /* MSDOS || WIN32 */ - /* in case we built debug enabled, we allow an environment variable - * named CURL_TESTDIR to prefix the given file name to put it into a - * specific directory - */ + /* in case we built debug enabled, we allow an environment variable + * named CURL_TESTDIR to prefix the given file name to put it into a + * specific directory + */ #ifdef DEBUGBUILD - { - char *tdir = curlx_getenv("CURL_TESTDIR"); - if(tdir) { - char *alt = aprintf("%s/%s", tdir, *filename); - Curl_safefree(*filename); - *filename = alt; - curl_free(tdir); - if(!*filename) - return CURLE_OUT_OF_MEMORY; - } + { + char *tdir = curlx_getenv("CURL_TESTDIR"); + if(tdir) { + char *alt = aprintf("%s/%s", tdir, *filename); + Curl_safefree(*filename); + *filename = alt; + curl_free(tdir); + if(!*filename) + return CURLE_OUT_OF_MEMORY; } -#endif - return CURLE_OK; } +#endif + return CURLE_OK; } curl_url_cleanup(uh); - return urlerr_cvt(uerr); + return CURLE_URL_MALFORMAT; } diff --git a/contrib/libs/curl/src/tool_operhlp.h b/contrib/libs/curl/src/tool_operhlp.h index 1d56fa0408..8018d1a67c 100644 --- a/contrib/libs/curl/src/tool_operhlp.h +++ b/contrib/libs/curl/src/tool_operhlp.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,4 @@ CURLcode add_file_name_to_url(CURL *curl, char **inurlp, const char *filename); CURLcode get_url_file_name(char **filename, const char *url); -CURLcode urlerr_cvt(CURLUcode ucode); - #endif /* HEADER_CURL_TOOL_OPERHLP_H */ diff --git a/contrib/libs/curl/lib/macos.h b/contrib/libs/curl/src/tool_panykey.c index 3388acd9fe..dce4f404c7 100644 --- a/contrib/libs/curl/lib/macos.h +++ b/contrib/libs/curl/src/tool_panykey.c @@ -1,5 +1,3 @@ -#ifndef HEADER_CURL_MACOS_H -#define HEADER_CURL_MACOS_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -23,16 +21,27 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "curl_setup.h" +#include "tool_setup.h" -#if defined(__APPLE__) && (!defined(TARGET_OS_OSX) || TARGET_OS_OSX) +#if defined(NETWARE) -CURLcode Curl_macos_init(void); +#ifdef NETWARE +# ifdef __NOVELL_LIBC__ +# include <screen.h> +# else +# error #include <nwconio.h> +# endif +#endif -#else +#include "tool_panykey.h" -#define Curl_macos_init() CURLE_OK +#include "memdebug.h" /* keep this as LAST include */ +void tool_pressanykey(void) +{ +#if defined(NETWARE) + pressanykey(); #endif +} -#endif /* HEADER_CURL_MACOS_H */ +#endif /* NETWARE */ diff --git a/contrib/libs/curl/src/tool_stderr.h b/contrib/libs/curl/src/tool_panykey.h index c887275fc8..c5cc322606 100644 --- a/contrib/libs/curl/src/tool_stderr.h +++ b/contrib/libs/curl/src/tool_panykey.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_TOOL_STDERR_H -#define HEADER_CURL_TOOL_STDERR_H +#ifndef HEADER_CURL_TOOL_PANYKEY_H +#define HEADER_CURL_TOOL_PANYKEY_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,9 +24,11 @@ * ***************************************************************************/ #include "tool_setup.h" -#include "tool_cfgable.h" -void tool_init_stderr(void); -void tool_set_stderr_file(struct GlobalConfig *global, char *filename); +#if defined(NETWARE) +void tool_pressanykey(void); +#else +#define tool_pressanykey() Curl_nop_stmt +#endif -#endif /* HEADER_CURL_TOOL_STDERR_H */ +#endif /* HEADER_CURL_TOOL_PANYKEY_H */ diff --git a/contrib/libs/curl/src/tool_paramhlp.c b/contrib/libs/curl/src/tool_paramhlp.c index d2248442b1..05afb8d3a4 100644 --- a/contrib/libs/curl/src/tool_paramhlp.c +++ b/contrib/libs/curl/src/tool_paramhlp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -240,9 +240,8 @@ static ParameterError str2double(double *val, const char *str, double max) } /* - * Parse the string as seconds with decimals, and write the number of - * milliseconds that corresponds in the given address. Return PARAM_OK on - * success, otherwise a parameter error enum. ONLY ACCEPTS POSITIVE NUMBERS! + * Parse the string and write the double in the given address. Return PARAM_OK + * on success, otherwise a parameter error enum. ONLY ACCEPTS POSITIVE NUMBERS! * * The 'max' argument is the maximum value allowed, as the numbers are often * multiplied when later used. @@ -252,16 +251,16 @@ static ParameterError str2double(double *val, const char *str, double max) * data. */ -ParameterError secs2ms(long *valp, const char *str) +ParameterError str2udouble(double *valp, const char *str, double max) { double value; - ParameterError result = str2double(&value, str, (double)LONG_MAX/1000); + ParameterError result = str2double(&value, str, max); if(result != PARAM_OK) return result; if(value < 0) return PARAM_NEGATIVE_NUMERIC; - *valp = (long)(value*1000); + *valp = value; return PARAM_OK; } @@ -369,7 +368,7 @@ ParameterError proto2num(struct OperationConfig *config, /* Process token modifiers */ while(!ISALNUM(*token)) { /* may be NULL if token is all modifiers */ - switch(*token++) { + switch (*token++) { case '=': action = set; break; @@ -418,7 +417,7 @@ ParameterError proto2num(struct OperationConfig *config, if no protocols are allowed */ if(action == set) protoset[0] = NULL; - warnf(config->global, "unrecognized protocol '%s'", token); + warnf(config->global, "unrecognized protocol '%s'\n", token); } } } @@ -433,7 +432,6 @@ ParameterError proto2num(struct OperationConfig *config, result = curlx_dyn_addf(&obuf, "%s,", protoset[proto]); free((char *) protoset); curlx_dyn_setlen(&obuf, curlx_dyn_len(&obuf) - 1); - free(*ostr); *ostr = curlx_dyn_ptr(&obuf); return *ostr ? PARAM_OK : PARAM_NO_MEM; @@ -474,7 +472,7 @@ ParameterError str2offset(curl_off_t *val, const char *str) #if(SIZEOF_CURL_OFF_T > SIZEOF_LONG) { - CURLofft offt = curlx_strtoofft(str, &endptr, 10, val); + CURLofft offt = curlx_strtoofft(str, &endptr, 0, val); if(CURL_OFFT_FLOW == offt) return PARAM_NUMBER_TOO_LARGE; else if(CURL_OFFT_INVAL == offt) @@ -566,7 +564,7 @@ int ftpfilemethod(struct OperationConfig *config, const char *str) if(curl_strequal("multicwd", str)) return CURLFTPMETHOD_MULTICWD; - warnf(config->global, "unrecognized ftp file method '%s', using default", + warnf(config->global, "unrecognized ftp file method '%s', using default\n", str); return CURLFTPMETHOD_MULTICWD; @@ -579,7 +577,7 @@ int ftpcccmethod(struct OperationConfig *config, const char *str) if(curl_strequal("active", str)) return CURLFTPSSL_CCC_ACTIVE; - warnf(config->global, "unrecognized ftp CCC method '%s', using default", + warnf(config->global, "unrecognized ftp CCC method '%s', using default\n", str); return CURLFTPSSL_CCC_PASSIVE; @@ -594,7 +592,7 @@ long delegation(struct OperationConfig *config, const char *str) if(curl_strequal("always", str)) return CURLGSSAPI_DELEGATION_FLAG; - warnf(config->global, "unrecognized delegation method '%s', using none", + warnf(config->global, "unrecognized delegation method '%s', using none\n", str); return CURLGSSAPI_DELEGATION_NONE; @@ -665,7 +663,7 @@ CURLcode get_args(struct OperationConfig *config, const size_t i) if(!config->useragent) { config->useragent = my_useragent(); if(!config->useragent) { - errorf(config->global, "out of memory"); + errorf(config->global, "out of memory\n"); result = CURLE_OUT_OF_MEMORY; } } diff --git a/contrib/libs/curl/src/tool_paramhlp.h b/contrib/libs/curl/src/tool_paramhlp.h index edb8781950..7d68583e67 100644 --- a/contrib/libs/curl/src/tool_paramhlp.h +++ b/contrib/libs/curl/src/tool_paramhlp.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -36,7 +36,7 @@ ParameterError str2num(long *val, const char *str); ParameterError str2unum(long *val, const char *str); ParameterError oct2nummax(long *val, const char *str, long max); ParameterError str2unummax(long *val, const char *str, long max); -ParameterError secs2ms(long *val, const char *str); +ParameterError str2udouble(double *val, const char *str, double max); ParameterError proto2num(struct OperationConfig *config, const char * const *val, char **obuf, diff --git a/contrib/libs/curl/src/tool_parsecfg.c b/contrib/libs/curl/src/tool_parsecfg.c index 0bc69646b0..a166757f86 100644 --- a/contrib/libs/curl/src/tool_parsecfg.c +++ b/contrib/libs/curl/src/tool_parsecfg.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -43,7 +43,7 @@ static const char *unslashquote(const char *line, char *param); -#define MAX_CONFIG_LINE_LENGTH (10*1024*1024) +#define MAX_CONFIG_LINE_LENGTH (100*1024) static bool my_get_line(FILE *fp, struct curlx_dynbuf *, bool *error); #ifdef WIN32 @@ -210,7 +210,7 @@ int parseconfig(const char *filename, struct GlobalConfig *global) break; default: warnf(operation->global, "%s:%d: warning: '%s' uses unquoted " - "whitespace in the line that may cause side-effects", + "whitespace in the line that may cause side-effects!\n", filename, lineno, option); } } @@ -223,7 +223,7 @@ int parseconfig(const char *filename, struct GlobalConfig *global) #ifdef DEBUG_CONFIG fprintf(stderr, "PARAM: \"%s\"\n",(param ? param : "(null)")); #endif - res = getparameter(option, param, NULL, &usedarg, global, operation); + res = getparameter(option, param, &usedarg, global, operation); operation = global->last; if(!res && param && *param && !usedarg) @@ -263,7 +263,7 @@ int parseconfig(const char *filename, struct GlobalConfig *global) res != PARAM_VERSION_INFO_REQUESTED && res != PARAM_ENGINES_REQUESTED) { const char *reason = param2text(res); - warnf(operation->global, "%s:%d: warning: '%s' %s", + warnf(operation->global, "%s:%d: warning: '%s' %s\n", filename, lineno, option, reason); } } diff --git a/contrib/libs/curl/src/tool_parsecfg.h b/contrib/libs/curl/src/tool_parsecfg.h index 75f9127ee7..90af619615 100644 --- a/contrib/libs/curl/src/tool_parsecfg.h +++ b/contrib/libs/curl/src/tool_parsecfg.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_progress.c b/contrib/libs/curl/src/tool_progress.c index 782ad3b790..266177b6ec 100644 --- a/contrib/libs/curl/src/tool_progress.c +++ b/contrib/libs/curl/src/tool_progress.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -53,6 +53,8 @@ static char *max5data(curl_off_t bytes, char *max5) CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE, (bytes%ONE_MEGABYTE) / (ONE_MEGABYTE/CURL_OFF_T_C(10)) ); +#if (SIZEOF_CURL_OFF_T > 4) + else if(bytes < CURL_OFF_T_C(10000) * ONE_MEGABYTE) /* 'XXXXM' is good until we're at 10000MB or above */ msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE); @@ -75,8 +77,16 @@ static char *max5data(curl_off_t bytes, char *max5) /* up to 10000PB, display without decimal: XXXXP */ msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "P", bytes/ONE_PETABYTE); - /* 16384 petabytes (16 exabytes) is the maximum a 64 bit unsigned number can - hold, but our data type is signed so 8192PB will be the maximum. */ + /* 16384 petabytes (16 exabytes) is the maximum a 64 bit unsigned number + can hold, but our data type is signed so 8192PB will be the maximum. */ + +#else + + else + msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE); + +#endif + return max5; } @@ -163,7 +173,7 @@ bool progress_meter(struct GlobalConfig *global, struct timeval now; long diff; - if(global->noprogress || global->silent) + if(global->noprogress) return FALSE; now = tvnow(); @@ -173,7 +183,7 @@ bool progress_meter(struct GlobalConfig *global, header = TRUE; fputs("DL% UL% Dled Uled Xfers Live " "Total Current Left Speed\n", - stderr); + global->errors); } if(final || (diff > 500)) { char time_left[10]; @@ -275,7 +285,7 @@ bool progress_meter(struct GlobalConfig *global, } time2str(time_spent, spent); - fprintf(stderr, + fprintf(global->errors, "\r" "%-3s " /* percent downloaded */ "%-3s " /* percent uploaded */ diff --git a/contrib/libs/curl/src/tool_progress.h b/contrib/libs/curl/src/tool_progress.h index 4c869a0ac3..5f68474ff3 100644 --- a/contrib/libs/curl/src/tool_progress.h +++ b/contrib/libs/curl/src/tool_progress.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_sdecls.h b/contrib/libs/curl/src/tool_sdecls.h index 7c03a04a57..70e44d4ed2 100644 --- a/contrib/libs/curl/src/tool_sdecls.h +++ b/contrib/libs/curl/src/tool_sdecls.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -70,6 +70,23 @@ struct OutStruct { curl_off_t init; }; + +/* + * InStruct variables keep track of information relative to curl's + * input reading, which may take place from stdin or from some file. + * + * 'fd' member is either 'stdin' file descriptor number STDIN_FILENO + * or a file descriptor as returned from an 'open' call for some file. + * + * 'config' member is a pointer to associated 'OperationConfig' struct. + */ + +struct InStruct { + int fd; + struct OperationConfig *config; +}; + + /* * A linked list of these 'getout' nodes contain URL's to fetch, * as well as information relative to where URL contents should diff --git a/contrib/libs/curl/src/tool_setopt.c b/contrib/libs/curl/src/tool_setopt.c index 08e8c2373b..3db2fe3c68 100644 --- a/contrib/libs/curl/src/tool_setopt.c +++ b/contrib/libs/curl/src/tool_setopt.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -91,7 +91,6 @@ const struct NameValue setopt_nv_CURL_HTTP_VERSION[] = { NV(CURL_HTTP_VERSION_2_0), NV(CURL_HTTP_VERSION_2TLS), NV(CURL_HTTP_VERSION_3), - NV(CURL_HTTP_VERSION_3ONLY), NVEND, }; @@ -294,9 +293,9 @@ CURLcode tool_setopt_enum(CURL *curl, struct GlobalConfig *config, #ifdef DEBUGBUILD if(ret) - warnf(config, "option %s returned error (%d)", name, (int)ret); + warnf(config, "option %s returned error (%d)\n", name, (int)ret); #endif -nomem: + nomem: return ret; } @@ -338,7 +337,7 @@ CURLcode tool_setopt_flags(CURL *curl, struct GlobalConfig *config, CODE2("%s%ldL);", preamble, rest); } -nomem: + nomem: return ret; } @@ -381,7 +380,7 @@ CURLcode tool_setopt_bitmask(CURL *curl, struct GlobalConfig *config, CODE2("%s%luUL);", preamble, rest); } -nomem: + nomem: return ret; } @@ -407,7 +406,7 @@ static CURLcode libcurl_generate_slist(struct curl_slist *slist, int *slistno) *slistno, *slistno, escaped); } -nomem: + nomem: Curl_safefree(escaped); return ret; } @@ -590,7 +589,7 @@ CURLcode tool_setopt_slist(CURL *curl, struct GlobalConfig *config, CODE2("curl_easy_setopt(hnd, %s, slist%d);", name, i); } -nomem: + nomem: return ret; } @@ -704,11 +703,14 @@ CURLcode tool_setopt(CURL *curl, bool str, struct GlobalConfig *global, } } -nomem: + nomem: Curl_safefree(escaped); return ret; } #else /* CURL_DISABLE_LIBCURL_OPTION */ +#include "tool_cfgable.h" +#include "tool_setopt.h" + #endif /* CURL_DISABLE_LIBCURL_OPTION */ diff --git a/contrib/libs/curl/src/tool_setopt.h b/contrib/libs/curl/src/tool_setopt.h index 4396c00916..bc5afe9bcb 100644 --- a/contrib/libs/curl/src/tool_setopt.h +++ b/contrib/libs/curl/src/tool_setopt.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_setup.h b/contrib/libs/curl/src/tool_setup.h index 1a723f88ba..16fa00e7d1 100644 --- a/contrib/libs/curl/src/tool_setup.h +++ b/contrib/libs/curl/src/tool_setup.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,15 +37,6 @@ #include "curl_setup.h" /* from the lib directory */ -extern FILE *tool_stderr; - -#if !defined(CURL_DO_NOT_OVERRIDE_STDERR) && !defined(UNITTESTS) -#ifdef stderr -#undef stderr -#endif -#define stderr tool_stderr -#endif - /* * curl tool certainly uses libcurl's external interface. */ @@ -56,7 +47,7 @@ extern FILE *tool_stderr; * Platform specific stuff. */ -#ifdef macintosh +#if defined(macintosh) && defined(__MRC__) # define main(x,y) curl_main(x,y) #endif @@ -73,10 +64,4 @@ extern FILE *tool_stderr; # include "tool_strdup.h" #endif -#if defined(WIN32) && !defined(MSDOS) -/* set in win32_init() */ -extern LARGE_INTEGER tool_freq; -extern bool tool_isVistaOrGreater; -#endif - #endif /* HEADER_CURL_TOOL_SETUP_H */ diff --git a/contrib/libs/curl/src/tool_sleep.c b/contrib/libs/curl/src/tool_sleep.c index 08d6f90288..c52425f9ab 100644 --- a/contrib/libs/curl/src/tool_sleep.c +++ b/contrib/libs/curl/src/tool_sleep.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_sleep.h b/contrib/libs/curl/src/tool_sleep.h index de1d7e256b..d84b467a42 100644 --- a/contrib/libs/curl/src/tool_sleep.h +++ b/contrib/libs/curl/src/tool_sleep.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_stderr.c b/contrib/libs/curl/src/tool_stderr.c deleted file mode 100644 index 23643df7cf..0000000000 --- a/contrib/libs/curl/src/tool_stderr.c +++ /dev/null @@ -1,72 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -/* In this file, stdio.h's stderr macro is not overridden. */ -#define CURL_DO_NOT_OVERRIDE_STDERR - -#include "tool_setup.h" -#include "tool_stderr.h" -#include "tool_msgs.h" - -#include "memdebug.h" /* keep this as LAST include */ - -/* In other tool files stderr is defined as tool_stderr by tool_setup.h */ -FILE *tool_stderr; - -void tool_init_stderr(void) -{ - tool_stderr = stderr; -} - -void tool_set_stderr_file(struct GlobalConfig *global, char *filename) -{ - FILE *fp; - - if(!filename) - return; - - if(!strcmp(filename, "-")) { - tool_stderr = stdout; - return; - } - - /* precheck that filename is accessible to lessen the chance that the - subsequent freopen will fail. */ - fp = fopen(filename, FOPEN_WRITETEXT); - if(!fp) { - warnf(global, "Warning: Failed to open %s", filename); - return; - } - fclose(fp); - - /* freopen the actual stderr (stdio.h stderr) instead of tool_stderr since - the latter may be set to stdout. */ - fp = freopen(filename, FOPEN_WRITETEXT, stderr); - if(!fp) { - /* stderr may have been closed by freopen. there is nothing to be done. */ - DEBUGASSERT(0); - return; - } - tool_stderr = stderr; -} diff --git a/contrib/libs/curl/src/tool_strdup.c b/contrib/libs/curl/src/tool_strdup.c index a5725d6d40..402f1c9827 100644 --- a/contrib/libs/curl/src/tool_strdup.c +++ b/contrib/libs/curl/src/tool_strdup.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_strdup.h b/contrib/libs/curl/src/tool_strdup.h index 9b21618962..c31c046522 100644 --- a/contrib/libs/curl/src/tool_strdup.h +++ b/contrib/libs/curl/src/tool_strdup.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_urlglob.c b/contrib/libs/curl/src/tool_urlglob.c index 2cccef25c6..fae8b131f8 100644 --- a/contrib/libs/curl/src/tool_urlglob.c +++ b/contrib/libs/curl/src/tool_urlglob.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -64,9 +64,9 @@ static CURLcode glob_fixed(struct URLGlob *glob, char *fixed, size_t len) * * Multiplies and checks for overflow. */ -static int multiply(curl_off_t *amount, curl_off_t with) +static int multiply(unsigned long *amount, long with) { - curl_off_t sum = *amount * with; + unsigned long sum = *amount * with; if(!with) { *amount = 0; return 0; @@ -78,7 +78,7 @@ static int multiply(curl_off_t *amount, curl_off_t with) } static CURLcode glob_set(struct URLGlob *glob, char **patternp, - size_t *posp, curl_off_t *amount, + size_t *posp, unsigned long *amount, int globindex) { /* processes a set expression with the point behind the opening '{' @@ -100,7 +100,7 @@ static CURLcode glob_set(struct URLGlob *glob, char **patternp, pat->globindex = globindex; while(!done) { - switch(*pattern) { + switch (*pattern) { case '\0': /* URL ended while set was still open */ return GLOBERROR("unmatched brace", opos, CURLE_URL_MALFORMAT); @@ -123,8 +123,7 @@ static CURLcode glob_set(struct URLGlob *glob, char **patternp, *buf = '\0'; if(pat->content.Set.elements) { char **new_arr = realloc(pat->content.Set.elements, - (size_t)(pat->content.Set.size + 1) * - sizeof(char *)); + (pat->content.Set.size + 1) * sizeof(char *)); if(!new_arr) return GLOBERROR("out of memory", 0, CURLE_OUT_OF_MEMORY); @@ -173,7 +172,7 @@ static CURLcode glob_set(struct URLGlob *glob, char **patternp, } static CURLcode glob_range(struct URLGlob *glob, char **patternp, - size_t *posp, curl_off_t *amount, + size_t *posp, unsigned long *amount, int globindex) { /* processes a range expression with the point behind the opening '[' @@ -296,7 +295,7 @@ static CURLcode glob_range(struct URLGlob *glob, char **patternp, } } -fail: + fail: *posp += (pattern - *patternp); if(!endp || !step_n || @@ -351,7 +350,7 @@ static bool peek_ipv6(const char *str, size_t *skip) memcpy(hostname, str, hlen); hostname[hlen] = 0; - /* ask to "guess scheme" as then it works without an https:// prefix */ + /* ask to "guess scheme" as then it works without a https:// prefix */ rc = curl_url_set(u, CURLUPART_URL, hostname, CURLU_GUESS_SCHEME); curl_url_cleanup(u); @@ -361,7 +360,7 @@ static bool peek_ipv6(const char *str, size_t *skip) } static CURLcode glob_parse(struct URLGlob *glob, char *pattern, - size_t pos, curl_off_t *amount) + size_t pos, unsigned long *amount) { /* processes a literal string component of a URL special characters '{' and '[' branch to set/range processing functions @@ -412,7 +411,7 @@ static CURLcode glob_parse(struct URLGlob *glob, char *pattern, res = glob_fixed(glob, glob->glob_buffer, sublen); } else { - switch(*pattern) { + switch (*pattern) { case '\0': /* done */ break; @@ -438,7 +437,7 @@ static CURLcode glob_parse(struct URLGlob *glob, char *pattern, return res; } -CURLcode glob_url(struct URLGlob **glob, char *url, curl_off_t *urlnum, +CURLcode glob_url(struct URLGlob **glob, char *url, unsigned long *urlnum, FILE *error) { /* @@ -446,7 +445,7 @@ CURLcode glob_url(struct URLGlob **glob, char *url, curl_off_t *urlnum, * as the specified URL! */ struct URLGlob *glob_expand; - curl_off_t amount = 0; + unsigned long amount = 0; char *glob_buffer; CURLcode res; @@ -497,7 +496,7 @@ CURLcode glob_url(struct URLGlob **glob, char *url, curl_off_t *urlnum, void glob_cleanup(struct URLGlob *glob) { size_t i; - curl_off_t elem; + int elem; if(!glob) return; diff --git a/contrib/libs/curl/src/tool_urlglob.h b/contrib/libs/curl/src/tool_urlglob.h index c38f9d92eb..80c1537f9d 100644 --- a/contrib/libs/curl/src/tool_urlglob.h +++ b/contrib/libs/curl/src/tool_urlglob.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -38,7 +38,7 @@ struct URLPattern { union { struct { char **elements; - curl_off_t size; + int size; int ptr_s; } Set; struct { @@ -48,11 +48,11 @@ struct URLPattern { int step; } CharRange; struct { - curl_off_t min_n; - curl_off_t max_n; + unsigned long min_n; + unsigned long max_n; int padlength; - curl_off_t ptr_n; - curl_off_t step; + unsigned long ptr_n; + unsigned long step; } NumRange; } content; }; @@ -70,7 +70,7 @@ struct URLGlob { size_t pos; /* column position of error or 0 */ }; -CURLcode glob_url(struct URLGlob**, char *, curl_off_t *, FILE *); +CURLcode glob_url(struct URLGlob**, char *, unsigned long *, FILE *); CURLcode glob_next_url(char **, struct URLGlob *); CURLcode glob_match_url(char **, char *, struct URLGlob *); void glob_cleanup(struct URLGlob *glob); diff --git a/contrib/libs/curl/src/tool_util.c b/contrib/libs/curl/src/tool_util.c index 7a1c03b20b..fb4829f84a 100644 --- a/contrib/libs/curl/src/tool_util.c +++ b/contrib/libs/curl/src/tool_util.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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,6 +33,10 @@ #if defined(WIN32) && !defined(MSDOS) +/* set in win32_init() */ +extern LARGE_INTEGER tool_freq; +extern bool tool_isVistaOrGreater; + /* In case of bug fix this function has a counterpart in timeval.c */ struct timeval tvnow(void) { diff --git a/contrib/libs/curl/src/tool_util.h b/contrib/libs/curl/src/tool_util.h index d68867265c..72b355e685 100644 --- a/contrib/libs/curl/src/tool_util.h +++ b/contrib/libs/curl/src/tool_util.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_version.h b/contrib/libs/curl/src/tool_version.h index 8266854c9a..0bbb9a5092 100644 --- a/contrib/libs/curl/src/tool_version.h +++ b/contrib/libs/curl/src/tool_version.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -27,7 +27,7 @@ #define CURL_NAME "curl" #define CURL_COPYRIGHT LIBCURL_COPYRIGHT -#define CURL_VERSION "8.2.1" +#define CURL_VERSION "7.86.0" #define CURL_VERSION_MAJOR LIBCURL_VERSION_MAJOR #define CURL_VERSION_MINOR LIBCURL_VERSION_MINOR #define CURL_VERSION_PATCH LIBCURL_VERSION_PATCH diff --git a/contrib/libs/curl/src/tool_vms.c b/contrib/libs/curl/src/tool_vms.c index 36d0ebe6ca..62a440f4e5 100644 --- a/contrib/libs/curl/src/tool_vms.c +++ b/contrib/libs/curl/src/tool_vms.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_vms.h b/contrib/libs/curl/src/tool_vms.h index cc2d31d053..949206dc58 100644 --- a/contrib/libs/curl/src/tool_vms.h +++ b/contrib/libs/curl/src/tool_vms.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_writeout.c b/contrib/libs/curl/src/tool_writeout.c index 2509d18b1a..2789ee20bf 100644 --- a/contrib/libs/curl/src/tool_writeout.c +++ b/contrib/libs/curl/src/tool_writeout.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -28,7 +28,6 @@ #include "tool_cfgable.h" #include "tool_writeout.h" #include "tool_writeout_json.h" -#include "dynbuf.h" #include "memdebug.h" /* keep this as LAST include */ @@ -73,9 +72,7 @@ static const struct httpmap http_version[] = { Variable names should be in alphabetical order. */ static const struct writeoutvar variables[] = { - {"certs", VAR_CERT, CURLINFO_NONE, writeString}, {"content_type", VAR_CONTENT_TYPE, CURLINFO_CONTENT_TYPE, writeString}, - {"conn_id", VAR_CONN_ID, CURLINFO_CONN_ID, writeOffset}, {"errormsg", VAR_ERRORMSG, CURLINFO_NONE, writeString}, {"exitcode", VAR_EXITCODE, CURLINFO_NONE, writeLong}, {"filename_effective", VAR_EFFECTIVE_FILENAME, CURLINFO_NONE, writeString}, @@ -88,7 +85,6 @@ static const struct writeoutvar variables[] = { {"local_ip", VAR_LOCAL_IP, CURLINFO_LOCAL_IP, writeString}, {"local_port", VAR_LOCAL_PORT, CURLINFO_LOCAL_PORT, writeLong}, {"method", VAR_EFFECTIVE_METHOD, CURLINFO_EFFECTIVE_METHOD, writeString}, - {"num_certs", VAR_NUM_CERTS, CURLINFO_NONE, writeLong}, {"num_connects", VAR_NUM_CONNECTS, CURLINFO_NUM_CONNECTS, writeLong}, {"num_headers", VAR_NUM_HEADERS, CURLINFO_NONE, writeLong}, {"num_redirects", VAR_REDIRECT_COUNT, CURLINFO_REDIRECT_COUNT, writeLong}, @@ -124,29 +120,8 @@ static const struct writeoutvar variables[] = { writeTime}, {"time_total", VAR_TOTAL_TIME, CURLINFO_TOTAL_TIME_T, writeTime}, {"url", VAR_INPUT_URL, CURLINFO_NONE, writeString}, - {"url.scheme", VAR_INPUT_URLSCHEME, CURLINFO_NONE, writeString}, - {"url.user", VAR_INPUT_URLUSER, CURLINFO_NONE, writeString}, - {"url.password", VAR_INPUT_URLPASSWORD, CURLINFO_NONE, writeString}, - {"url.options", VAR_INPUT_URLOPTIONS, CURLINFO_NONE, writeString}, - {"url.host", VAR_INPUT_URLHOST, CURLINFO_NONE, writeString}, - {"url.port", VAR_INPUT_URLPORT, CURLINFO_NONE, writeString}, - {"url.path", VAR_INPUT_URLPATH, CURLINFO_NONE, writeString}, - {"url.query", VAR_INPUT_URLQUERY, CURLINFO_NONE, writeString}, - {"url.fragment", VAR_INPUT_URLFRAGMENT, CURLINFO_NONE, writeString}, - {"url.zoneid", VAR_INPUT_URLZONEID, CURLINFO_NONE, writeString}, - {"urle.scheme", VAR_INPUT_URLESCHEME, CURLINFO_NONE, writeString}, - {"urle.user", VAR_INPUT_URLEUSER, CURLINFO_NONE, writeString}, - {"urle.password", VAR_INPUT_URLEPASSWORD, CURLINFO_NONE, writeString}, - {"urle.options", VAR_INPUT_URLEOPTIONS, CURLINFO_NONE, writeString}, - {"urle.host", VAR_INPUT_URLEHOST, CURLINFO_NONE, writeString}, - {"urle.port", VAR_INPUT_URLEPORT, CURLINFO_NONE, writeString}, - {"urle.path", VAR_INPUT_URLEPATH, CURLINFO_NONE, writeString}, - {"urle.query", VAR_INPUT_URLEQUERY, CURLINFO_NONE, writeString}, - {"urle.fragment", VAR_INPUT_URLEFRAGMENT, CURLINFO_NONE, writeString}, - {"urle.zoneid", VAR_INPUT_URLEZONEID, CURLINFO_NONE, writeString}, {"url_effective", VAR_EFFECTIVE_URL, CURLINFO_EFFECTIVE_URL, writeString}, {"urlnum", VAR_URLNUM, CURLINFO_NONE, writeLong}, - {"xfer_id", VAR_EASY_ID, CURLINFO_XFER_ID, writeOffset}, {NULL, VAR_NONE, CURLINFO_NONE, NULL} }; @@ -187,96 +162,12 @@ static int writeTime(FILE *stream, const struct writeoutvar *wovar, return 1; /* return 1 if anything was written */ } -static int urlpart(struct per_transfer *per, writeoutid vid, - const char **contentp) -{ - CURLU *uh = curl_url(); - int rc = 0; - if(uh) { - CURLUPart cpart = CURLUPART_HOST; - char *part = NULL; - const char *url = NULL; - - if(vid >= VAR_INPUT_URLEHOST) { - if(curl_easy_getinfo(per->curl, CURLINFO_EFFECTIVE_URL, &url)) - rc = 5; - } - else - url = per->this_url; - - if(!rc) { - switch(vid) { - case VAR_INPUT_URLSCHEME: - case VAR_INPUT_URLESCHEME: - cpart = CURLUPART_SCHEME; - break; - case VAR_INPUT_URLUSER: - case VAR_INPUT_URLEUSER: - cpart = CURLUPART_USER; - break; - case VAR_INPUT_URLPASSWORD: - case VAR_INPUT_URLEPASSWORD: - cpart = CURLUPART_PASSWORD; - break; - case VAR_INPUT_URLOPTIONS: - case VAR_INPUT_URLEOPTIONS: - cpart = CURLUPART_OPTIONS; - break; - case VAR_INPUT_URLHOST: - case VAR_INPUT_URLEHOST: - cpart = CURLUPART_HOST; - break; - case VAR_INPUT_URLPORT: - case VAR_INPUT_URLEPORT: - cpart = CURLUPART_PORT; - break; - case VAR_INPUT_URLPATH: - case VAR_INPUT_URLEPATH: - cpart = CURLUPART_PATH; - break; - case VAR_INPUT_URLQUERY: - case VAR_INPUT_URLEQUERY: - cpart = CURLUPART_QUERY; - break; - case VAR_INPUT_URLFRAGMENT: - case VAR_INPUT_URLEFRAGMENT: - cpart = CURLUPART_FRAGMENT; - break; - case VAR_INPUT_URLZONEID: - case VAR_INPUT_URLEZONEID: - cpart = CURLUPART_ZONEID; - break; - default: - /* not implemented */ - rc = 4; - break; - } - } - if(!rc && curl_url_set(uh, CURLUPART_URL, url, - CURLU_GUESS_SCHEME|CURLU_NON_SUPPORT_SCHEME)) - rc = 2; - - if(!rc && curl_url_get(uh, cpart, &part, CURLU_DEFAULT_PORT)) - rc = 3; - - if(!rc && part) - *contentp = part; - curl_url_cleanup(uh); - } - else - return 1; - return rc; -} - static int writeString(FILE *stream, const struct writeoutvar *wovar, struct per_transfer *per, CURLcode per_result, bool use_json) { bool valid = false; const char *strinfo = NULL; - const char *freestr = NULL; - struct dynbuf buf; - curlx_dyn_init(&buf, 256*1024); DEBUGASSERT(wovar->writefunc == writeString); @@ -302,51 +193,6 @@ static int writeString(FILE *stream, const struct writeoutvar *wovar, } else { switch(wovar->id) { - case VAR_CERT: - if(per->certinfo) { - int i; - bool error = FALSE; - for(i = 0; (i < per->certinfo->num_of_certs) && !error; i++) { - struct curl_slist *slist; - - for(slist = per->certinfo->certinfo[i]; slist; slist = slist->next) { - size_t len; - if(curl_strnequal(slist->data, "cert:", 5)) { - if(curlx_dyn_add(&buf, &slist->data[5])) { - error = TRUE; - break; - } - } - else { - if(curlx_dyn_add(&buf, slist->data)) { - error = TRUE; - break; - } - } - len = curlx_dyn_len(&buf); - if(len) { - char *ptr = curlx_dyn_ptr(&buf); - if(ptr[len -1] != '\n') { - /* add a newline to make things look better */ - if(curlx_dyn_addn(&buf, "\n", 1)) { - error = TRUE; - break; - } - } - } - } - } - if(!error) { - strinfo = curlx_dyn_ptr(&buf); - if(!strinfo) - /* maybe not a TLS protocol */ - strinfo = ""; - valid = true; - } - } - else - strinfo = ""; /* no cert info */ - break; case VAR_ERRORMSG: if(per_result) { strinfo = (per->errorbuffer && per->errorbuffer[0]) ? @@ -366,33 +212,6 @@ static int writeString(FILE *stream, const struct writeoutvar *wovar, valid = true; } break; - case VAR_INPUT_URLSCHEME: - case VAR_INPUT_URLUSER: - case VAR_INPUT_URLPASSWORD: - case VAR_INPUT_URLOPTIONS: - case VAR_INPUT_URLHOST: - case VAR_INPUT_URLPORT: - case VAR_INPUT_URLPATH: - case VAR_INPUT_URLQUERY: - case VAR_INPUT_URLFRAGMENT: - case VAR_INPUT_URLZONEID: - case VAR_INPUT_URLESCHEME: - case VAR_INPUT_URLEUSER: - case VAR_INPUT_URLEPASSWORD: - case VAR_INPUT_URLEOPTIONS: - case VAR_INPUT_URLEHOST: - case VAR_INPUT_URLEPORT: - case VAR_INPUT_URLEPATH: - case VAR_INPUT_URLEQUERY: - case VAR_INPUT_URLEFRAGMENT: - case VAR_INPUT_URLEZONEID: - if(per->this_url) { - if(!urlpart(per, wovar->id, &strinfo)) { - freestr = strinfo; - valid = true; - } - } - break; default: DEBUGASSERT(0); break; @@ -412,9 +231,7 @@ static int writeString(FILE *stream, const struct writeoutvar *wovar, if(use_json) fprintf(stream, "\"%s\":null", wovar->name); } - curl_free((char *)freestr); - curlx_dyn_free(&buf); return 1; /* return 1 if anything was written */ } @@ -433,10 +250,6 @@ static int writeLong(FILE *stream, const struct writeoutvar *wovar, } else { switch(wovar->id) { - case VAR_NUM_CERTS: - longinfo = per->certinfo ? per->certinfo->num_of_certs : 0; - valid = true; - break; case VAR_NUM_HEADERS: longinfo = per->num_headers; valid = true; @@ -508,21 +321,12 @@ static int writeOffset(FILE *stream, const struct writeoutvar *wovar, return 1; /* return 1 if anything was written */ } -void ourWriteOut(struct OperationConfig *config, struct per_transfer *per, +void ourWriteOut(const char *writeinfo, struct per_transfer *per, CURLcode per_result) { FILE *stream = stdout; - const char *writeinfo = config->writeout; const char *ptr = writeinfo; bool done = FALSE; - struct curl_certinfo *certinfo; - CURLcode res = curl_easy_getinfo(per->curl, CURLINFO_CERTINFO, &certinfo); - - if(!writeinfo) - return; - - if(!res && certinfo) - per->certinfo = certinfo; while(ptr && *ptr && !done) { if('%' == *ptr && ptr[1]) { diff --git a/contrib/libs/curl/src/tool_writeout.h b/contrib/libs/curl/src/tool_writeout.h index 4e690a65c9..c7cdb9771c 100644 --- a/contrib/libs/curl/src/tool_writeout.h +++ b/contrib/libs/curl/src/tool_writeout.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -29,11 +29,8 @@ typedef enum { VAR_NONE, /* must be the first */ VAR_APPCONNECT_TIME, - VAR_CERT, VAR_CONNECT_TIME, VAR_CONTENT_TYPE, - VAR_CONN_ID, - VAR_EASY_ID, VAR_EFFECTIVE_FILENAME, VAR_EFFECTIVE_METHOD, VAR_EFFECTIVE_URL, @@ -46,32 +43,10 @@ typedef enum { VAR_HTTP_CODE_PROXY, VAR_HTTP_VERSION, VAR_INPUT_URL, - VAR_INPUT_URLSCHEME, - VAR_INPUT_URLUSER, - VAR_INPUT_URLPASSWORD, - VAR_INPUT_URLOPTIONS, - VAR_INPUT_URLHOST, - VAR_INPUT_URLPORT, - VAR_INPUT_URLPATH, - VAR_INPUT_URLQUERY, - VAR_INPUT_URLFRAGMENT, - VAR_INPUT_URLZONEID, - /* the same ones again for url *effective* */ - VAR_INPUT_URLESCHEME, - VAR_INPUT_URLEUSER, - VAR_INPUT_URLEPASSWORD, - VAR_INPUT_URLEOPTIONS, - VAR_INPUT_URLEHOST, - VAR_INPUT_URLEPORT, - VAR_INPUT_URLEPATH, - VAR_INPUT_URLEQUERY, - VAR_INPUT_URLEFRAGMENT, - VAR_INPUT_URLEZONEID, VAR_JSON, VAR_LOCAL_IP, VAR_LOCAL_PORT, VAR_NAMELOOKUP_TIME, - VAR_NUM_CERTS, VAR_NUM_CONNECTS, VAR_NUM_HEADERS, VAR_ONERROR, @@ -107,7 +82,7 @@ struct writeoutvar { bool use_json); }; -void ourWriteOut(struct OperationConfig *config, struct per_transfer *per, +void ourWriteOut(const char *writeinfo, struct per_transfer *per, CURLcode per_result); #endif /* HEADER_CURL_TOOL_WRITEOUT_H */ diff --git a/contrib/libs/curl/src/tool_writeout_json.c b/contrib/libs/curl/src/tool_writeout_json.c index 0e4623fe86..a36d60c4f1 100644 --- a/contrib/libs/curl/src/tool_writeout_json.c +++ b/contrib/libs/curl/src/tool_writeout_json.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -61,8 +61,8 @@ void jsonWriteString(FILE *stream, const char *in, bool lowercase) fputs("\\t", stream); break; default: - if(*i < 32) { - fprintf(stream, "\\u%04x", *i); + if (*i < 32) { + fprintf(stream, "u%04x", *i); } else { char out = *i; @@ -110,6 +110,11 @@ void headerJSON(FILE *stream, struct per_transfer *per) fputc('{', stream); while((header = curl_easy_nextheader(per->curl, CURLH_HEADER, -1, prev))) { + if(prev) + fputs(",\n", stream); + jsonWriteString(stream, header->name, TRUE); + fputc(':', stream); + prev = header; if(header->amount > 1) { if(!header->index) { /* act on the 0-index entry and pull the others in, then output in a @@ -117,11 +122,6 @@ void headerJSON(FILE *stream, struct per_transfer *per) size_t a = header->amount; size_t i = 0; char *name = header->name; - if(prev) - fputs(",\n", stream); - jsonWriteString(stream, header->name, TRUE); - fputc(':', stream); - prev = header; fputc('[', stream); do { jsonWriteString(stream, header->value, FALSE); @@ -132,18 +132,13 @@ void headerJSON(FILE *stream, struct per_transfer *per) -1, &header)) break; } while(1); - fputc(']', stream); } + fputc(']', stream); } else { - if(prev) - fputs(",\n", stream); - jsonWriteString(stream, header->name, TRUE); - fputc(':', stream); fputc('[', stream); jsonWriteString(stream, header->value, FALSE); fputc(']', stream); - prev = header; } } fputs("\n}", stream); diff --git a/contrib/libs/curl/src/tool_writeout_json.h b/contrib/libs/curl/src/tool_writeout_json.h index 6d8f8d0dc0..d8466051c5 100644 --- a/contrib/libs/curl/src/tool_writeout_json.h +++ b/contrib/libs/curl/src/tool_writeout_json.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/src/tool_xattr.c b/contrib/libs/curl/src/tool_xattr.c index 968cf2f72d..bd05749966 100644 --- a/contrib/libs/curl/src/tool_xattr.c +++ b/contrib/libs/curl/src/tool_xattr.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 @@ -75,7 +75,7 @@ char *stripcredentials(const char *url) return nurl; } -error: + error: curl_url_cleanup(u); return NULL; } diff --git a/contrib/libs/curl/src/tool_xattr.h b/contrib/libs/curl/src/tool_xattr.h index 2ffffd275f..e85d4cded5 100644 --- a/contrib/libs/curl/src/tool_xattr.h +++ b/contrib/libs/curl/src/tool_xattr.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, 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 diff --git a/contrib/libs/curl/ya.make b/contrib/libs/curl/ya.make index a4a02b33c3..d9bc485f14 100644 --- a/contrib/libs/curl/ya.make +++ b/contrib/libs/curl/ya.make @@ -1,4 +1,4 @@ -# Generated by devtools/yamaker from nixpkgs 22.11. +# Generated by devtools/yamaker from nixpkgs 22.05. LIBRARY() @@ -11,9 +11,9 @@ LICENSE( LICENSE_TEXTS(.yandex_meta/licenses.list.txt) -VERSION(8.2.1) +VERSION(7.86.0) -ORIGINAL_SOURCE(https://github.com/curl/curl/releases/download/curl-8_2_1/curl-8.2.1.tar.bz2) +ORIGINAL_SOURCE(https://github.com/curl/curl/releases/download/curl-7_86_0/curl-7.86.0.tar.bz2) PEERDIR( contrib/libs/libc_compat @@ -65,15 +65,8 @@ SRCS( lib/asyn-ares.c lib/asyn-thread.c lib/base64.c - lib/bufq.c lib/bufref.c lib/c-hyper.c - lib/cf-h1-proxy.c - lib/cf-h2-proxy.c - lib/cf-haproxy.c - lib/cf-https-connect.c - lib/cf-socket.c - lib/cfilters.c lib/conncache.c lib/connect.c lib/content_encoding.c @@ -85,7 +78,6 @@ SRCS( lib/curl_get_line.c lib/curl_gethostname.c lib/curl_gssapi.c - lib/curl_log.c lib/curl_memrchr.c lib/curl_multibyte.c lib/curl_ntlm_core.c @@ -99,7 +91,6 @@ SRCS( lib/dict.c lib/doh.c lib/dynbuf.c - lib/dynhds.c lib/easy.c lib/easygetopt.c lib/easyoptions.c @@ -113,6 +104,7 @@ SRCS( lib/getenv.c lib/getinfo.c lib/gopher.c + lib/h2h3.c lib/hash.c lib/headers.c lib/hmac.c @@ -123,7 +115,6 @@ SRCS( lib/hostsyn.c lib/hsts.c lib/http.c - lib/http1.c lib/http2.c lib/http_aws_sigv4.c lib/http_chunks.c @@ -131,7 +122,7 @@ SRCS( lib/http_negotiate.c lib/http_ntlm.c lib/http_proxy.c - lib/idn.c + lib/idn_win32.c lib/if2ip.c lib/imap.c lib/inet_ntop.c @@ -139,7 +130,6 @@ SRCS( lib/krb5.c lib/ldap.c lib/llist.c - lib/macos.c lib/md4.c lib/md5.c lib/memdebug.c @@ -201,9 +191,9 @@ SRCS( lib/vauth/vauth.c lib/version.c lib/version_win32.c - lib/vquic/curl_msh3.c - lib/vquic/curl_ngtcp2.c - lib/vquic/curl_quiche.c + lib/vquic/msh3.c + lib/vquic/ngtcp2.c + lib/vquic/quiche.c lib/vquic/vquic.c lib/vssh/libssh.c lib/vssh/libssh2.c @@ -219,6 +209,7 @@ SRCS( lib/vtls/wolfssl.c lib/vtls/x509asn1.c lib/warnless.c + lib/wildcard.c lib/ws.c ) diff --git a/contrib/libs/rapidjson/include/rapidjson/internal/dtoa.h b/contrib/libs/rapidjson/include/rapidjson/internal/dtoa.h index bf2e9b2e59..4572645914 100644 --- a/contrib/libs/rapidjson/include/rapidjson/internal/dtoa.h +++ b/contrib/libs/rapidjson/include/rapidjson/internal/dtoa.h @@ -36,6 +36,7 @@ inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uin while (rest < wp_w && delta - rest >= ten_kappa && (rest + ten_kappa < wp_w || /// closer wp_w - rest > rest + ten_kappa - wp_w)) { + // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Assign) buffer[len - 1]--; rest += ten_kappa; } diff --git a/contrib/python/importlib-metadata/py3/.dist-info/METADATA b/contrib/python/importlib-metadata/py3/.dist-info/METADATA index 395be499f1..9e58c1d38e 100644 --- a/contrib/python/importlib-metadata/py3/.dist-info/METADATA +++ b/contrib/python/importlib-metadata/py3/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: importlib-metadata -Version: 6.8.0 +Version: 6.9.0 Summary: Read metadata from Python packages Home-page: https://github.com/python/importlib_metadata Author: Jason R. Coombs @@ -12,29 +12,30 @@ Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3 :: Only Requires-Python: >=3.8 License-File: LICENSE -Requires-Dist: typing-extensions (>=3.6.4) ; python_version < "3.8" +Requires-Dist: typing-extensions >=3.6.4 ; python_version < "3.8" Provides-Extra: docs -Requires-Dist: sphinx (>=3.5) ; extra == 'docs' -Requires-Dist: jaraco.packaging (>=9) ; extra == 'docs' -Requires-Dist: rst.linker (>=1.9) ; extra == 'docs' +Requires-Dist: sphinx >=3.5 ; extra == 'docs' +Requires-Dist: sphinx <7.2.5 ; extra == 'docs' +Requires-Dist: jaraco.packaging >=9.3 ; extra == 'docs' +Requires-Dist: rst.linker >=1.9 ; extra == 'docs' Requires-Dist: furo ; extra == 'docs' Requires-Dist: sphinx-lint ; extra == 'docs' -Requires-Dist: jaraco.tidelift (>=1.4) ; extra == 'docs' +Requires-Dist: jaraco.tidelift >=1.4 ; extra == 'docs' Provides-Extra: perf Requires-Dist: ipython ; extra == 'perf' Provides-Extra: testing -Requires-Dist: pytest (>=6) ; extra == 'testing' -Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing' +Requires-Dist: pytest >=6 ; extra == 'testing' +Requires-Dist: pytest-checkdocs >=2.4 ; extra == 'testing' Requires-Dist: pytest-cov ; extra == 'testing' -Requires-Dist: pytest-enabler (>=2.2) ; extra == 'testing' +Requires-Dist: pytest-enabler >=2.2 ; extra == 'testing' Requires-Dist: pytest-ruff ; extra == 'testing' Requires-Dist: packaging ; extra == 'testing' Requires-Dist: pyfakefs ; extra == 'testing' Requires-Dist: flufl.flake8 ; extra == 'testing' -Requires-Dist: pytest-perf (>=0.9.2) ; extra == 'testing' -Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing' -Requires-Dist: pytest-mypy (>=0.9.1) ; (platform_python_implementation != "PyPy") and extra == 'testing' -Requires-Dist: importlib-resources (>=1.3) ; (python_version < "3.9") and extra == 'testing' +Requires-Dist: pytest-perf >=0.9.2 ; extra == 'testing' +Requires-Dist: pytest-black >=0.3.7 ; (platform_python_implementation != "PyPy") and extra == 'testing' +Requires-Dist: pytest-mypy >=0.9.1 ; (platform_python_implementation != "PyPy") and extra == 'testing' +Requires-Dist: importlib-resources >=1.3 ; (python_version < "3.9") and extra == 'testing' .. image:: https://img.shields.io/pypi/v/importlib_metadata.svg :target: https://pypi.org/project/importlib_metadata @@ -128,10 +129,3 @@ Available as part of the Tidelift Subscription. This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use. `Learn more <https://tidelift.com/subscription/pkg/pypi-importlib-metadata?utm_source=pypi-importlib-metadata&utm_medium=referral&utm_campaign=github>`_. - -Security Contact -================ - -To report a security vulnerability, please use the -`Tidelift security contact <https://tidelift.com/security>`_. -Tidelift will coordinate the fix and disclosure. diff --git a/contrib/python/importlib-metadata/py3/README.rst b/contrib/python/importlib-metadata/py3/README.rst index f42e88db71..73ddc5fab4 100644 --- a/contrib/python/importlib-metadata/py3/README.rst +++ b/contrib/python/importlib-metadata/py3/README.rst @@ -90,10 +90,3 @@ Available as part of the Tidelift Subscription. This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use. `Learn more <https://tidelift.com/subscription/pkg/pypi-importlib-metadata?utm_source=pypi-importlib-metadata&utm_medium=referral&utm_campaign=github>`_. - -Security Contact -================ - -To report a security vulnerability, please use the -`Tidelift security contact <https://tidelift.com/security>`_. -Tidelift will coordinate the fix and disclosure. diff --git a/contrib/python/importlib-metadata/py3/importlib_metadata/__init__.py b/contrib/python/importlib-metadata/py3/importlib_metadata/__init__.py index 36fed6ecee..8416afdbd6 100644 --- a/contrib/python/importlib-metadata/py3/importlib_metadata/__init__.py +++ b/contrib/python/importlib-metadata/py3/importlib_metadata/__init__.py @@ -298,6 +298,13 @@ class EntryPoints(tuple): except StopIteration: raise KeyError(name) + def __repr__(self): + """ + Repr with classname and tuple constructor to + signal that we deviate from regular tuple behavior. + """ + return '%s(%r)' % (self.__class__.__name__, tuple(self)) + def select(self, **params): """ Select entry points from self that match the diff --git a/contrib/python/importlib-metadata/py3/importlib_metadata/_adapters.py b/contrib/python/importlib-metadata/py3/importlib_metadata/_adapters.py index e33cba5e44..120e43a048 100644 --- a/contrib/python/importlib-metadata/py3/importlib_metadata/_adapters.py +++ b/contrib/python/importlib-metadata/py3/importlib_metadata/_adapters.py @@ -54,7 +54,7 @@ class Message(email.message.Message): def __getitem__(self, item): """ Warn users that a ``KeyError`` can be expected when a - mising key is supplied. Ref python/importlib_metadata#371. + missing key is supplied. Ref python/importlib_metadata#371. """ res = super().__getitem__(item) if res is None: diff --git a/contrib/python/importlib-metadata/py3/ya.make b/contrib/python/importlib-metadata/py3/ya.make index baed051fc4..cb87df460a 100644 --- a/contrib/python/importlib-metadata/py3/ya.make +++ b/contrib/python/importlib-metadata/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(6.8.0) +VERSION(6.9.0) LICENSE(Apache-2.0) diff --git a/contrib/python/yarl/.dist-info/METADATA b/contrib/python/yarl/.dist-info/METADATA index 8585ab738d..548567bd12 100644 --- a/contrib/python/yarl/.dist-info/METADATA +++ b/contrib/python/yarl/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: yarl -Version: 1.9.3 +Version: 1.9.4 Summary: Yet another URL library Home-page: https://github.com/aio-libs/yarl Author: Andrew Svetlov @@ -116,9 +116,9 @@ automatically encoded giving canonical representation as result: .. code-block:: pycon - >>> url = URL('https://www.python.org/путь') + >>> url = URL('https://www.python.org/шлях') >>> url - URL('https://www.python.org/%D0%BF%D1%83%D1%82%D1%8C') + URL('https://www.python.org/%D1%88%D0%BB%D1%8F%D1%85') Regular properties are *percent-decoded*, use ``raw_`` versions for getting *encoded* strings: @@ -126,17 +126,17 @@ getting *encoded* strings: .. code-block:: pycon >>> url.path - '/путь' + '/шлях' >>> url.raw_path - '/%D0%BF%D1%83%D1%82%D1%8C' + '/%D1%88%D0%BB%D1%8F%D1%85' Human readable representation of URL is available as ``.human_repr()``: .. code-block:: pycon >>> url.human_repr() - 'https://www.python.org/путь' + 'https://www.python.org/шлях' For full documentation please read https://yarl.aio-libs.org. @@ -157,12 +157,12 @@ used with our wheels) the the tarball will be used to compile the library from the source code. It requires a C compiler and and Python headers installed. To skip the compilation you must explicitly opt-in by using a PEP 517 -configuration setting ``--pure-python``, or setting the ``YARL_NO_EXTENSIONS`` +configuration setting ``pure-python``, or setting the ``YARL_NO_EXTENSIONS`` environment variable to a non-empty value, e.g.: .. code-block:: console - $ pip install yarl --config-settings=--pure-python= + $ pip install yarl --config-settings=pure-python=false Please note that the pure-Python (uncompiled) version is much slower. However, PyPy always uses a pure-Python implementation, and, as such, it is unaffected @@ -262,13 +262,83 @@ It's *Apache 2* licensed and freely available. .. towncrier release notes start +1.9.4 (2023-12-06) +================== + +Bug fixes +--------- + +- Started raising ``TypeError`` when a string value is passed into + ``yarl.URL.build()`` as the ``port`` argument -- by `@commonism <https://github.com/sponsors/commonism>`__. + + Previously the empty string as port would create malformed URLs when rendered as string representations. (`#883 <https://github.com/aio-libs/yarl/issues/883>`__) + + +Packaging updates and notes for downstreams +------------------------------------------- + +- The leading ``--`` has been dropped from the `PEP 517 <https://peps.python.org/pep-517>`__ in-tree build + backend config setting names. ``--pure-python`` is now just ``pure-python`` + -- by `@webknjaz <https://github.com/sponsors/webknjaz>`__. + + The usage now looks as follows: + + .. code-block:: console + + $ python -m build \ + --config-setting=pure-python=true \ + --config-setting=with-cython-tracing=true + + (`#963 <https://github.com/aio-libs/yarl/issues/963>`__) + + +Contributor-facing changes +-------------------------- + +- A step-by-step ``Release Guide`` guide has + been added, describing how to release *yarl* -- by `@webknjaz <https://github.com/sponsors/webknjaz>`__. + + This is primarily targeting maintainers. (`#960 <https://github.com/aio-libs/yarl/issues/960>`__) +- Coverage collection has been implemented for the Cython modules + -- by `@webknjaz <https://github.com/sponsors/webknjaz>`__. + + It will also be reported to Codecov from any non-release CI jobs. + + To measure coverage in a development environment, *yarl* can be + installed in editable mode, which requires an environment variable + ``YARL_CYTHON_TRACING=1`` to be set: + + .. code-block:: console + + $ YARL_CYTHON_TRACING=1 python -Im pip install -e . + + Editable install produces C-files required for the Cython coverage + plugin to map the measurements back to the PYX-files. (`#961 <https://github.com/aio-libs/yarl/issues/961>`__) +- It is now possible to request line tracing in Cython builds using the + ``with-cython-tracing`` `PEP 517 <https://peps.python.org/pep-517>`__ config setting + -- `@webknjaz <https://github.com/sponsors/webknjaz>`__. + + This can be used in CI and development environment to measure coverage + on Cython modules, but is not normally useful to the end-users or + downstream packagers. + + Here's a usage example: + + .. code-block:: console + + $ python -Im pip install . --config-settings=with-cython-tracing=true + + For editable installs, this setting is on by default. Otherwise, it's + off unless requested explicitly. (`#962 <https://github.com/aio-libs/yarl/issues/962>`__) + + 1.9.3 (2023-11-20) ================== Bug fixes --------- -- Stopped dropping trailing slashes in ``yarl.URL.joinpath()`` -- by `@mjpieters <https://github.com/sponsors/mjpieters>`__. (`#862 <https://github.com/aio-libs/yarl/issues/862>`__, `#866 <https://github.com/aio-libs/yarl/issues/866>`__) +- Stopped dropping trailing slashes in ``yarl.URL.joinpath()`` -- by `@gmacon <https://github.com/sponsors/gmacon>`__. (`#862 <https://github.com/aio-libs/yarl/issues/862>`__, `#866 <https://github.com/aio-libs/yarl/issues/866>`__) - Started accepting string subclasses in ``__truediv__()`` operations (``URL / segment``) -- by `@mjpieters <https://github.com/sponsors/mjpieters>`__. (`#871 <https://github.com/aio-libs/yarl/issues/871>`__, `#884 <https://github.com/aio-libs/yarl/issues/884>`__) - Fixed the human representation of URLs with square brackets in usernames and passwords -- by `@mjpieters <https://github.com/sponsors/mjpieters>`__. (`#876 <https://github.com/aio-libs/yarl/issues/876>`__, `#882 <https://github.com/aio-libs/yarl/issues/882>`__) - Updated type hints to include ``URL.missing_port()``, ``URL.__bytes__()`` @@ -280,12 +350,12 @@ Packaging updates and notes for downstreams ------------------------------------------- - Integrated Cython 3 to enable building *yarl* under Python 3.12 -- by `@mjpieters <https://github.com/sponsors/mjpieters>`__. (`#829 <https://github.com/aio-libs/yarl/issues/829>`__, `#881 <https://github.com/aio-libs/yarl/issues/881>`__) -- Declared modern ``setuptools.build_meta`` as the ``517`` build +- Declared modern ``setuptools.build_meta`` as the `PEP 517 <https://peps.python.org/pep-517>`__ build backend in ``pyproject.toml`` explicitly -- by `@webknjaz <https://github.com/sponsors/webknjaz>`__. (`#886 <https://github.com/aio-libs/yarl/issues/886>`__) - Converted most of the packaging setup into a declarative ``setup.cfg`` config -- by `@webknjaz <https://github.com/sponsors/webknjaz>`__. (`#890 <https://github.com/aio-libs/yarl/issues/890>`__) -- Replaced the packaging is replaced from an old-fashioned ``setup.py`` to an - in-tree ``517`` build backend -- by `@webknjaz <https://github.com/sponsors/webknjaz>`__. +- The packaging is replaced from an old-fashioned ``setup.py`` to an + in-tree `PEP 517 <https://peps.python.org/pep-517>`__ build backend -- by `@webknjaz <https://github.com/sponsors/webknjaz>`__. Whenever the end-users or downstream packagers need to build ``yarl`` from source (a Git checkout or an sdist), they may pass a ``config_settings`` @@ -296,7 +366,7 @@ Packaging updates and notes for downstreams .. code-block:: console - $ python -m pip install . --config-settings=--pure-python= + $ python -m pip install . --config-settings=--pure-python=false This will also work with ``-e | --editable``. @@ -304,10 +374,16 @@ Packaging updates and notes for downstreams .. code-block:: console - $ python -m build --config-setting=--pure-python= + $ python -m build --config-setting=--pure-python=false Adding ``-w | --wheel`` can force ``pypa/build`` produce a wheel from source directly, as opposed to building an ``sdist`` and then building from it. (`#893 <https://github.com/aio-libs/yarl/issues/893>`__) + + .. attention:: + + v1.9.3 was the only version using the ``--pure-python`` setting name. + Later versions dropped the ``--`` prefix, making it just ``pure-python``. + - Declared Python 3.12 supported officially in the distribution package metadata -- by `@edgarrmondragon <https://github.com/sponsors/edgarrmondragon>`__. (`#942 <https://github.com/aio-libs/yarl/issues/942>`__) diff --git a/contrib/python/yarl/README.rst b/contrib/python/yarl/README.rst index a1032b206a..844ffff692 100644 --- a/contrib/python/yarl/README.rst +++ b/contrib/python/yarl/README.rst @@ -74,9 +74,9 @@ automatically encoded giving canonical representation as result: .. code-block:: pycon - >>> url = URL('https://www.python.org/путь') + >>> url = URL('https://www.python.org/шлях') >>> url - URL('https://www.python.org/%D0%BF%D1%83%D1%82%D1%8C') + URL('https://www.python.org/%D1%88%D0%BB%D1%8F%D1%85') Regular properties are *percent-decoded*, use ``raw_`` versions for getting *encoded* strings: @@ -84,17 +84,17 @@ getting *encoded* strings: .. code-block:: pycon >>> url.path - '/путь' + '/шлях' >>> url.raw_path - '/%D0%BF%D1%83%D1%82%D1%8C' + '/%D1%88%D0%BB%D1%8F%D1%85' Human readable representation of URL is available as ``.human_repr()``: .. code-block:: pycon >>> url.human_repr() - 'https://www.python.org/путь' + 'https://www.python.org/шлях' For full documentation please read https://yarl.aio-libs.org. @@ -115,12 +115,12 @@ used with our wheels) the the tarball will be used to compile the library from the source code. It requires a C compiler and and Python headers installed. To skip the compilation you must explicitly opt-in by using a PEP 517 -configuration setting ``--pure-python``, or setting the ``YARL_NO_EXTENSIONS`` +configuration setting ``pure-python``, or setting the ``YARL_NO_EXTENSIONS`` environment variable to a non-empty value, e.g.: .. code-block:: console - $ pip install yarl --config-settings=--pure-python= + $ pip install yarl --config-settings=pure-python=false Please note that the pure-Python (uncompiled) version is much slower. However, PyPy always uses a pure-Python implementation, and, as such, it is unaffected diff --git a/contrib/python/yarl/tests/test_cached_property.py b/contrib/python/yarl/tests/test_cached_property.py index 5dcb5ece23..834f6db437 100644 --- a/contrib/python/yarl/tests/test_cached_property.py +++ b/contrib/python/yarl/tests/test_cached_property.py @@ -3,42 +3,27 @@ import pytest from yarl._url import cached_property -def test_reify(): - class A: - def __init__(self): - self._cache = {} +class A: + def __init__(self): + self._cache = {} + + @cached_property + def prop(self): + """Docstring.""" + return 1 - @cached_property - def prop(self): - return 1 +def test_reify(): a = A() assert 1 == a.prop def test_reify_class(): - class A: - def __init__(self): - self._cache = {} - - @cached_property - def prop(self): - """Docstring.""" - return 1 - assert isinstance(A.prop, cached_property) assert "Docstring." == A.prop.__doc__ def test_reify_assignment(): - class A: - def __init__(self): - self._cache = {} - - @cached_property - def prop(self): - return 1 - a = A() with pytest.raises(AttributeError): diff --git a/contrib/python/yarl/tests/test_quoting.py b/contrib/python/yarl/tests/test_quoting.py index 7ebc0f9b04..d9b6ae8e4b 100644 --- a/contrib/python/yarl/tests/test_quoting.py +++ b/contrib/python/yarl/tests/test_quoting.py @@ -226,14 +226,21 @@ def test_unquoting_bad_percent_escapes(unquoter, input, expected): assert unquoter()(input) == expected -@pytest.mark.xfail -# FIXME: After conversion to bytes, should not cause UTF-8 decode fail. -# See https://url.spec.whatwg.org/#percent-encoded-bytes -def test_unquoting_invalid_utf8_sequence(unquoter): - with pytest.raises(ValueError): - unquoter()("%AB") +@pytest.mark.xfail( + reason=""" + FIXME: After conversion to bytes, should not cause UTF-8 decode fail. + See https://url.spec.whatwg.org/#percent-encoded-bytes + + Refs: + * https://github.com/aio-libs/yarl/pull/216 + * https://github.com/aio-libs/yarl/pull/214 + * https://github.com/aio-libs/yarl/pull/7 + """, +) +@pytest.mark.parametrize("urlencoded_string", ("%AB", "%AB%AB")) +def test_unquoting_invalid_utf8_sequence(unquoter, urlencoded_string): with pytest.raises(ValueError): - unquoter()("%AB%AB") + unquoter()(urlencoded_string) def test_unquoting_mixed_case_percent_escapes(unquoter): diff --git a/contrib/python/yarl/tests/test_update_query.py b/contrib/python/yarl/tests/test_update_query.py index e47c468341..176259d750 100644 --- a/contrib/python/yarl/tests/test_update_query.py +++ b/contrib/python/yarl/tests/test_update_query.py @@ -171,7 +171,7 @@ class _CStr(str): class _EmptyStrEr: def __str__(self): - return "" + return "" # pragma: no cover # <-- this should never happen class _CInt(int, _EmptyStrEr): diff --git a/contrib/python/yarl/tests/test_url.py b/contrib/python/yarl/tests/test_url.py index af13d0b5d5..59d543754d 100644 --- a/contrib/python/yarl/tests/test_url.py +++ b/contrib/python/yarl/tests/test_url.py @@ -64,8 +64,8 @@ def test_origin(): def test_origin_nonascii(): - url = URL("http://user:password@историк.рф:8888/path/to?a=1&b=2") - assert str(url.origin()) == "http://xn--h1aagokeh.xn--p1ai:8888" + url = URL("http://user:password@оун-упа.укр:8888/path/to?a=1&b=2") + assert str(url.origin()) == "http://xn----8sb1bdhvc.xn--j1amh:8888" def test_origin_ipv6(): @@ -117,8 +117,8 @@ def test_raw_user(): def test_raw_user_non_ascii(): - url = URL("http://вася@example.com") - assert "%D0%B2%D0%B0%D1%81%D1%8F" == url.raw_user + url = URL("http://бажан@example.com") + assert "%D0%B1%D0%B0%D0%B6%D0%B0%D0%BD" == url.raw_user def test_no_user(): @@ -127,8 +127,8 @@ def test_no_user(): def test_user_non_ascii(): - url = URL("http://вася@example.com") - assert "вася" == url.user + url = URL("http://бажан@example.com") + assert "бажан" == url.user def test_raw_password(): @@ -164,13 +164,13 @@ def test_raw_host(): def test_raw_host_non_ascii(): - url = URL("http://историк.рф") - assert "xn--h1aagokeh.xn--p1ai" == url.raw_host + url = URL("http://оун-упа.укр") + assert "xn----8sb1bdhvc.xn--j1amh" == url.raw_host def test_host_non_ascii(): - url = URL("http://историк.рф") - assert "историк.рф" == url.host + url = URL("http://оун-упа.укр") + assert "оун-упа.укр" == url.host def test_localhost(): @@ -210,12 +210,13 @@ def test_authority_short() -> None: def test_authority_full_nonasci() -> None: - url = URL("http://ваня:пароль@айдеко.рф:8080/path") + url = URL("http://степан:пароль@слава.укр:8080/path") assert url.raw_authority == ( - "%D0%B2%D0%B0%D0%BD%D1%8F:%D0%BF%D0%B0%D1%80%D0%BE%D0%BB%D1%8C@" - "xn--80aidohy.xn--p1ai:8080" + "%D1%81%D1%82%D0%B5%D0%BF%D0%B0%D0%BD:" + "%D0%BF%D0%B0%D1%80%D0%BE%D0%BB%D1%8C@" + "xn--80aaf8a3a.xn--j1amh:8080" ) - assert url.authority == "ваня:пароль@айдеко.рф:8080" + assert url.authority == "степан:пароль@слава.укр:8080" def test_lowercase(): @@ -225,9 +226,9 @@ def test_lowercase(): def test_lowercase_nonascii(): - url = URL("http://Айдеко.Рф") - assert url.raw_host == "xn--80aidohy.xn--p1ai" - assert url.host == "айдеко.рф" + url = URL("http://Слава.Укр") + assert url.raw_host == "xn--80aaf8a3a.xn--j1amh" + assert url.host == "слава.укр" def test_compressed_ipv6(): @@ -294,13 +295,13 @@ def test_raw_path(): def test_raw_path_non_ascii(): - url = URL("http://example.com/путь/сюда") - assert "/%D0%BF%D1%83%D1%82%D1%8C/%D1%81%D1%8E%D0%B4%D0%B0" == url.raw_path + url = URL("http://example.com/шлях/сюди") + assert "/%D1%88%D0%BB%D1%8F%D1%85/%D1%81%D1%8E%D0%B4%D0%B8" == url.raw_path def test_path_non_ascii(): - url = URL("http://example.com/путь/сюда") - assert "/путь/сюда" == url.path + url = URL("http://example.com/шлях/сюди") + assert "/шлях/сюди" == url.path def test_path_with_spaces(): @@ -352,8 +353,8 @@ def test_raw_path_qs(): assert url.raw_path_qs == "/?%D0%B1=%D0%B2&%D1%8E=%D0%BA" url = URL("http://example.com/path?б=в&ю=к") assert url.raw_path_qs == "/path?%D0%B1=%D0%B2&%D1%8E=%D0%BA" - url = URL("http://example.com/путь?a=1&b=2") - assert url.raw_path_qs == "/%D0%BF%D1%83%D1%82%D1%8C?a=1&b=2" + url = URL("http://example.com/шлях?a=1&b=2") + assert url.raw_path_qs == "/%D1%88%D0%BB%D1%8F%D1%85?a=1&b=2" def test_query_string_spaces(): @@ -375,8 +376,8 @@ def test_raw_fragment(): def test_raw_fragment_non_ascii(): - url = URL("http://example.com/path#якорь") - assert "%D1%8F%D0%BA%D0%BE%D1%80%D1%8C" == url.raw_fragment + url = URL("http://example.com/path#якір") + assert "%D1%8F%D0%BA%D1%96%D1%80" == url.raw_fragment def test_raw_fragment_safe(): @@ -385,8 +386,8 @@ def test_raw_fragment_safe(): def test_fragment_non_ascii(): - url = URL("http://example.com/path#якорь") - assert "якорь" == url.fragment + url = URL("http://example.com/path#якір") + assert "якір" == url.fragment def test_raw_parts_empty(): @@ -435,17 +436,17 @@ def test_parts_for_empty_url(): def test_raw_parts_non_ascii(): - url = URL("http://example.com/путь/сюда") + url = URL("http://example.com/шлях/сюди") assert ( "/", - "%D0%BF%D1%83%D1%82%D1%8C", - "%D1%81%D1%8E%D0%B4%D0%B0", + "%D1%88%D0%BB%D1%8F%D1%85", + "%D1%81%D1%8E%D0%B4%D0%B8", ) == url.raw_parts def test_parts_non_ascii(): - url = URL("http://example.com/путь/сюда") - assert ("/", "путь", "сюда") == url.parts + url = URL("http://example.com/шлях/сюди") + assert ("/", "шлях", "сюди") == url.parts def test_name_for_empty_url(): @@ -489,8 +490,8 @@ def test_relative_raw_name_slash(): def test_name_non_ascii(): - url = URL("http://example.com/путь") - assert url.name == "путь" + url = URL("http://example.com/шлях") + assert url.name == "шлях" def test_suffix_for_empty_url(): @@ -534,8 +535,8 @@ def test_relative_raw_suffix_dot(): def test_suffix_non_ascii(): - url = URL("http://example.com/путь.суффикс") - assert url.suffix == ".суффикс" + url = URL("http://example.com/шлях.суфікс") + assert url.suffix == ".суфікс" def test_suffix_with_empty_name(): @@ -594,8 +595,8 @@ def test_relative_raw_suffixes_dot(): def test_suffixes_non_ascii(): - url = URL("http://example.com/путь.суффикс") - assert url.suffixes == (".суффикс",) + url = URL("http://example.com/шлях.суфікс") + assert url.suffixes == (".суфікс",) def test_suffixes_with_empty_name(): @@ -753,15 +754,15 @@ def test_div_for_relative_url_started_with_slash(): def test_div_non_ascii(): - url = URL("http://example.com/сюда") - url2 = url / "туда" - assert url2.path == "/сюда/туда" - assert url2.raw_path == "/%D1%81%D1%8E%D0%B4%D0%B0/%D1%82%D1%83%D0%B4%D0%B0" - assert url2.parts == ("/", "сюда", "туда") + url = URL("http://example.com/сюди") + url2 = url / "туди" + assert url2.path == "/сюди/туди" + assert url2.raw_path == "/%D1%81%D1%8E%D0%B4%D0%B8/%D1%82%D1%83%D0%B4%D0%B8" + assert url2.parts == ("/", "сюди", "туди") assert url2.raw_parts == ( "/", - "%D1%81%D1%8E%D0%B4%D0%B0", - "%D1%82%D1%83%D0%B4%D0%B0", + "%D1%81%D1%8E%D0%B4%D0%B8", + "%D1%82%D1%83%D0%B4%D0%B8", ) @@ -846,13 +847,13 @@ def test_joinpath_relative(url, to_join, expected): "url,to_join,encoded,e_path,e_raw_path,e_parts,e_raw_parts", [ pytest.param( - "http://example.com/сюда", - ("туда",), + "http://example.com/сюди", + ("туди",), False, - "/сюда/туда", - "/%D1%81%D1%8E%D0%B4%D0%B0/%D1%82%D1%83%D0%B4%D0%B0", - ("/", "сюда", "туда"), - ("/", "%D1%81%D1%8E%D0%B4%D0%B0", "%D1%82%D1%83%D0%B4%D0%B0"), + "/сюди/туди", + "/%D1%81%D1%8E%D0%B4%D0%B8/%D1%82%D1%83%D0%B4%D0%B8", + ("/", "сюди", "туди"), + ("/", "%D1%81%D1%8E%D0%B4%D0%B8", "%D1%82%D1%83%D0%B4%D0%B8"), id="non-ascii", ), pytest.param( @@ -1093,11 +1094,11 @@ def test_with_name_empty(): def test_with_name_non_ascii(): - url = URL("http://example.com/path").with_name("путь") - assert url.path == "/путь" - assert url.raw_path == "/%D0%BF%D1%83%D1%82%D1%8C" - assert url.parts == ("/", "путь") - assert url.raw_parts == ("/", "%D0%BF%D1%83%D1%82%D1%8C") + url = URL("http://example.com/path").with_name("шлях") + assert url.path == "/шлях" + assert url.raw_path == "/%D1%88%D0%BB%D1%8F%D1%85" + assert url.parts == ("/", "шлях") + assert url.raw_parts == ("/", "%D1%88%D0%BB%D1%8F%D1%85") def test_with_name_percent_encoded(): @@ -1185,11 +1186,11 @@ def test_with_suffix_empty(): def test_with_suffix_non_ascii(): - url = URL("http://example.com/path").with_suffix(".путь") - assert url.path == "/path.путь" - assert url.raw_path == "/path.%D0%BF%D1%83%D1%82%D1%8C" - assert url.parts == ("/", "path.путь") - assert url.raw_parts == ("/", "path.%D0%BF%D1%83%D1%82%D1%8C") + url = URL("http://example.com/path").with_suffix(".шлях") + assert url.path == "/path.шлях" + assert url.raw_path == "/path.%D1%88%D0%BB%D1%8F%D1%85" + assert url.parts == ("/", "path.шлях") + assert url.raw_parts == ("/", "path.%D1%88%D0%BB%D1%8F%D1%85") def test_with_suffix_percent_encoded(): @@ -1340,8 +1341,8 @@ def test_from_ascii_login(): def test_from_non_ascii_login(): - url = URL("http://вася@host:1234/") - assert ("http://" "%D0%B2%D0%B0%D1%81%D1%8F" "@host:1234/") == str(url) + url = URL("http://бажан@host:1234/") + assert ("http://%D0%B1%D0%B0%D0%B6%D0%B0%D0%BD@host:1234/") == str(url) def test_from_ascii_login_and_password(): @@ -1360,10 +1361,10 @@ def test_from_ascii_login_and_password(): def test_from_non_ascii_login_and_password(): - url = URL("http://вася:пароль@host:1234/") + url = URL("http://бажан:пароль@host:1234/") assert ( "http://" - "%D0%B2%D0%B0%D1%81%D1%8F" + "%D0%B1%D0%B0%D0%B6%D0%B0%D0%BD" ":%D0%BF%D0%B0%D1%80%D0%BE%D0%BB%D1%8C" "@host:1234/" ) == str(url) @@ -1384,16 +1385,16 @@ def test_from_ascii_path_lower_case(): def test_from_non_ascii_path(): - url = URL("http://example.com/путь/туда") + url = URL("http://example.com/шлях/туди") assert ( - "http://example.com/" "%D0%BF%D1%83%D1%82%D1%8C/%D1%82%D1%83%D0%B4%D0%B0" + "http://example.com/%D1%88%D0%BB%D1%8F%D1%85/%D1%82%D1%83%D0%B4%D0%B8" ) == str(url) def test_bytes(): - url = URL("http://example.com/путь/туда") + url = URL("http://example.com/шлях/туди") assert ( - b"http://example.com/%D0%BF%D1%83%D1%82%D1%8C/%D1%82%D1%83%D0%B4%D0%B0" + b"http://example.com/%D1%88%D0%BB%D1%8F%D1%85/%D1%82%D1%83%D0%B4%D0%B8" == bytes(url) ) @@ -1610,23 +1611,23 @@ def test_split_result_non_decoded(): def test_human_repr(): - url = URL("http://вася:пароль@хост.домен:8080/путь/сюда?арг=вал#фраг") + url = URL("http://бажан:пароль@хост.домен:8080/шлях/сюди?арг=вал#фраг") s = url.human_repr() assert URL(s) == url - assert s == "http://вася:пароль@хост.домен:8080/путь/сюда?арг=вал#фраг" + assert s == "http://бажан:пароль@хост.домен:8080/шлях/сюди?арг=вал#фраг" def test_human_repr_defaults(): - url = URL("путь") + url = URL("шлях") s = url.human_repr() - assert s == "путь" + assert s == "шлях" def test_human_repr_default_port(): - url = URL("http://вася:пароль@хост.домен/путь/сюда?арг=вал#фраг") + url = URL("http://бажан:пароль@хост.домен/шлях/сюди?арг=вал#фраг") s = url.human_repr() assert URL(s) == url - assert s == "http://вася:пароль@хост.домен/путь/сюда?арг=вал#фраг" + assert s == "http://бажан:пароль@хост.домен/шлях/сюди?арг=вал#фраг" def test_human_repr_ipv6(): @@ -1667,20 +1668,20 @@ def test_human_repr_delimiters(): def test_human_repr_non_printable(): url = URL.build( scheme="http", - user="вася\n\xad\u200b", + user="бажан\n\xad\u200b", password="пароль\n\xad\u200b", host="хост.домен", port=8080, - path="/путь\n\xad\u200b", + path="/шлях\n\xad\u200b", query={"арг\n\xad\u200b": "вал\n\xad\u200b"}, fragment="фраг\n\xad\u200b", ) s = url.human_repr() assert URL(s) == url assert ( - s == "http://вася%0A%C2%AD%E2%80%8B:пароль%0A%C2%AD%E2%80%8B" + s == "http://бажан%0A%C2%AD%E2%80%8B:пароль%0A%C2%AD%E2%80%8B" "@хост.домен:8080" - "/путь%0A%C2%AD%E2%80%8B" + "/шлях%0A%C2%AD%E2%80%8B" "?арг%0A%C2%AD%E2%80%8B=вал%0A%C2%AD%E2%80%8B" "#фраг%0A%C2%AD%E2%80%8B" ) diff --git a/contrib/python/yarl/tests/test_url_build.py b/contrib/python/yarl/tests/test_url_build.py index 51969fa849..5aecbc5854 100644 --- a/contrib/python/yarl/tests/test_url_build.py +++ b/contrib/python/yarl/tests/test_url_build.py @@ -32,12 +32,28 @@ def test_build_with_scheme_and_host(): assert u == URL("http://127.0.0.1") -def test_build_with_port(): - with pytest.raises(ValueError): - URL.build(port=8000) - - u = URL.build(scheme="http", host="127.0.0.1", port=8000) - assert str(u) == "http://127.0.0.1:8000" +@pytest.mark.parametrize( + ("port", "exc", "match"), + [ + pytest.param( + 8000, + ValueError, + r"""(?x) + ^ + Can't\ build\ URL\ with\ "port"\ but\ without\ "host"\. + $ + """, + id="port-only", + ), + pytest.param( + "", TypeError, r"^The port is required to be int\.$", id="port-str" + ), + ], +) +def test_build_with_port(port, exc, match): + print(match) + with pytest.raises(exc, match=match): + URL.build(port=port) def test_build_with_user(): @@ -85,8 +101,10 @@ def test_build_with_authority_and_host(): def test_build_with_authority(): - url = URL.build(scheme="http", authority="ваня:bar@host.com:8000", path="path") - assert str(url) == "http://%D0%B2%D0%B0%D0%BD%D1%8F:bar@host.com:8000/path" + url = URL.build(scheme="http", authority="степан:bar@host.com:8000", path="path") + assert ( + str(url) == "http://%D1%81%D1%82%D0%B5%D0%BF%D0%B0%D0%BD:bar@host.com:8000/path" + ) def test_build_with_authority_without_encoding(): @@ -109,23 +127,33 @@ def test_query_dict(): def test_build_path_quoting(): u = URL.build( - scheme="http", host="127.0.0.1", path="/файл.jpg", query=dict(arg="Привет") + scheme="http", + host="127.0.0.1", + path="/фотографія.jpg", + query=dict(arg="Привіт"), ) - assert u == URL("http://127.0.0.1/файл.jpg?arg=Привет") + assert u == URL("http://127.0.0.1/фотографія.jpg?arg=Привіт") assert str(u) == ( - "http://127.0.0.1/%D1%84%D0%B0%D0%B9%D0%BB.jpg?" - "arg=%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82" + "http://127.0.0.1/" + "%D1%84%D0%BE%D1%82%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D1%96%D1%8F.jpg?" + "arg=%D0%9F%D1%80%D0%B8%D0%B2%D1%96%D1%82" ) def test_build_query_quoting(): - u = URL.build(scheme="http", host="127.0.0.1", path="/файл.jpg", query="arg=Привет") + u = URL.build( + scheme="http", + host="127.0.0.1", + path="/фотографія.jpg", + query="arg=Привіт", + ) - assert u == URL("http://127.0.0.1/файл.jpg?arg=Привет") + assert u == URL("http://127.0.0.1/фотографія.jpg?arg=Привіт") assert str(u) == ( - "http://127.0.0.1/%D1%84%D0%B0%D0%B9%D0%BB.jpg?" - "arg=%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82" + "http://127.0.0.1/" + "%D1%84%D0%BE%D1%82%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D1%96%D1%8F.jpg?" + "arg=%D0%9F%D1%80%D0%B8%D0%B2%D1%96%D1%82" ) @@ -143,14 +171,14 @@ def test_build_drop_dots(): def test_build_encode(): u = URL.build( scheme="http", - host="историк.рф", - path="/путь/файл", + host="оун-упа.укр", + path="/шлях/криївка", query_string="ключ=знач", fragment="фраг", ) expected = ( - "http://xn--h1aagokeh.xn--p1ai" - "/%D0%BF%D1%83%D1%82%D1%8C/%D1%84%D0%B0%D0%B9%D0%BB" + "http://xn----8sb1bdhvc.xn--j1amh" + "/%D1%88%D0%BB%D1%8F%D1%85/%D0%BA%D1%80%D0%B8%D1%97%D0%B2%D0%BA%D0%B0" "?%D0%BA%D0%BB%D1%8E%D1%87=%D0%B7%D0%BD%D0%B0%D1%87" "#%D1%84%D1%80%D0%B0%D0%B3" ) @@ -161,13 +189,13 @@ def test_build_already_encoded(): # resulting URL is invalid but not encoded u = URL.build( scheme="http", - host="историк.рф", - path="/путь/файл", + host="оун-упа.укр", + path="/шлях/криївка", query_string="ключ=знач", fragment="фраг", encoded=True, ) - assert str(u) == "http://историк.рф/путь/файл?ключ=знач#фраг" + assert str(u) == "http://оун-упа.укр/шлях/криївка?ключ=знач#фраг" def test_build_percent_encoded(): diff --git a/contrib/python/yarl/tests/test_url_parsing.py b/contrib/python/yarl/tests/test_url_parsing.py index cc753fcd0c..11aa8e92a4 100644 --- a/contrib/python/yarl/tests/test_url_parsing.py +++ b/contrib/python/yarl/tests/test_url_parsing.py @@ -214,15 +214,19 @@ class TestPort: assert u.query_string == "" assert u.fragment == "" - @pytest.mark.xfail(reason="https://github.com/aio-libs/yarl/issues/821") + @pytest.mark.xfail( + # FIXME: remove "no cover" pragmas upon xfail marker deletion + reason="https://github.com/aio-libs/yarl/issues/821", + raises=ValueError, + ) def test_no_host(self): u = URL("//:80") - assert u.scheme == "" - assert u.host == "" - assert u.port == 80 - assert u.path == "/" - assert u.query_string == "" - assert u.fragment == "" + assert u.scheme == "" # pragma: no cover + assert u.host == "" # pragma: no cover + assert u.port == 80 # pragma: no cover + assert u.path == "/" # pragma: no cover + assert u.query_string == "" # pragma: no cover + assert u.fragment == "" # pragma: no cover def test_double_port(self): with pytest.raises(ValueError): diff --git a/contrib/python/yarl/tests/test_url_update_netloc.py b/contrib/python/yarl/tests/test_url_update_netloc.py index cf0cc1c44c..47d13bcd60 100644 --- a/contrib/python/yarl/tests/test_url_update_netloc.py +++ b/contrib/python/yarl/tests/test_url_update_netloc.py @@ -33,11 +33,11 @@ def test_with_user(): def test_with_user_non_ascii(): url = URL("http://example.com") - url2 = url.with_user("вася") - assert url2.raw_user == "%D0%B2%D0%B0%D1%81%D1%8F" - assert url2.user == "вася" - assert url2.raw_authority == "%D0%B2%D0%B0%D1%81%D1%8F@example.com" - assert url2.authority == "вася@example.com:80" + url2 = url.with_user("бажан") + assert url2.raw_user == "%D0%B1%D0%B0%D0%B6%D0%B0%D0%BD" + assert url2.user == "бажан" + assert url2.raw_authority == "%D0%B1%D0%B0%D0%B6%D0%B0%D0%BD@example.com" + assert url2.authority == "бажан@example.com:80" def test_with_user_percent_encoded(): @@ -159,11 +159,11 @@ def test_with_host_empty(): def test_with_host_non_ascii(): url = URL("http://example.com:123") - url2 = url.with_host("историк.рф") - assert url2.raw_host == "xn--h1aagokeh.xn--p1ai" - assert url2.host == "историк.рф" - assert url2.raw_authority == "xn--h1aagokeh.xn--p1ai:123" - assert url2.authority == "историк.рф:123" + url2 = url.with_host("оун-упа.укр") + assert url2.raw_host == "xn----8sb1bdhvc.xn--j1amh" + assert url2.host == "оун-упа.укр" + assert url2.raw_authority == "xn----8sb1bdhvc.xn--j1amh:123" + assert url2.authority == "оун-упа.укр:123" def test_with_host_percent_encoded(): diff --git a/contrib/python/yarl/ya.make b/contrib/python/yarl/ya.make index 0c3d0ce434..e1a242b811 100644 --- a/contrib/python/yarl/ya.make +++ b/contrib/python/yarl/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(1.9.3) +VERSION(1.9.4) LICENSE(Apache-2.0) diff --git a/contrib/python/yarl/yarl/__init__.py b/contrib/python/yarl/yarl/__init__.py index f43aecbc92..127721ad09 100644 --- a/contrib/python/yarl/yarl/__init__.py +++ b/contrib/python/yarl/yarl/__init__.py @@ -1,5 +1,5 @@ from ._url import URL, cache_clear, cache_configure, cache_info -__version__ = "1.9.3" +__version__ = "1.9.4" __all__ = ("URL", "cache_clear", "cache_configure", "cache_info") diff --git a/contrib/python/yarl/yarl/_quoting_c.pyx b/contrib/python/yarl/yarl/_quoting_c.pyx index 5335d17365..96f69c14e2 100644 --- a/contrib/python/yarl/yarl/_quoting_c.pyx +++ b/contrib/python/yarl/yarl/_quoting_c.pyx @@ -145,7 +145,7 @@ cdef inline int _write_utf8(Writer* writer, Py_UCS4 symbol): if _write_pct(writer, <uint8_t>(0xe0 | (utf >> 12)), True) < 0: return -1 if _write_pct(writer, <uint8_t>(0x80 | ((utf >> 6) & 0x3f)), - True) < 0: + True) < 0: return -1 return _write_pct(writer, <uint8_t>(0x80 | (utf & 0x3f)), True) elif utf > 0x10FFFF: @@ -155,10 +155,10 @@ cdef inline int _write_utf8(Writer* writer, Py_UCS4 symbol): if _write_pct(writer, <uint8_t>(0xf0 | (utf >> 18)), True) < 0: return -1 if _write_pct(writer, <uint8_t>(0x80 | ((utf >> 12) & 0x3f)), - True) < 0: - return -1 + True) < 0: + return -1 if _write_pct(writer, <uint8_t>(0x80 | ((utf >> 6) & 0x3f)), - True) < 0: + True) < 0: return -1 return _write_pct(writer, <uint8_t>(0x80 | (utf & 0x3f)), True) diff --git a/contrib/python/yarl/yarl/_url.py b/contrib/python/yarl/yarl/_url.py index c8f2acb39b..9cca27ef86 100644 --- a/contrib/python/yarl/yarl/_url.py +++ b/contrib/python/yarl/yarl/_url.py @@ -233,6 +233,8 @@ class URL: raise ValueError( 'Can\'t mix "authority" with "user", "password", "host" or "port".' ) + if not isinstance(port, (int, type(None))): + raise TypeError("The port is required to be int.") if port and not host: raise ValueError('Can\'t build URL with "port" but without "host".') if query and query_string: @@ -507,7 +509,7 @@ class URL: return None if "%" in raw: # Hack for scoped IPv6 addresses like - # fe80::2%Проверка + # fe80::2%Перевірка # presence of '%' sign means only IPv6 address, so idna is useless. return raw return _idna_decode(raw) diff --git a/contrib/restricted/boost/assert/include/boost/assert/source_location.hpp b/contrib/restricted/boost/assert/include/boost/assert/source_location.hpp index 8b040c11a2..82e2d37854 100644 --- a/contrib/restricted/boost/assert/include/boost/assert/source_location.hpp +++ b/contrib/restricted/boost/assert/include/boost/assert/source_location.hpp @@ -7,9 +7,7 @@ // Distributed under the Boost Software License, Version 1.0. // http://www.boost.org/LICENSE_1_0.txt -#include <boost/current_function.hpp> #include <boost/config.hpp> -#include <boost/config/workaround.hpp> #include <boost/cstdint.hpp> #include <iosfwd> #include <string> diff --git a/contrib/restricted/boost/assert/ya.make b/contrib/restricted/boost/assert/ya.make index 7730822d2d..46ab40f809 100644 --- a/contrib/restricted/boost/assert/ya.make +++ b/contrib/restricted/boost/assert/ya.make @@ -6,9 +6,9 @@ LICENSE(BSL-1.0) LICENSE_TEXTS(.yandex_meta/licenses.list.txt) -VERSION(1.83.0) +VERSION(1.84.0) -ORIGINAL_SOURCE(https://github.com/boostorg/assert/archive/boost-1.83.0.tar.gz) +ORIGINAL_SOURCE(https://github.com/boostorg/assert/archive/boost-1.84.0.tar.gz) PEERDIR( contrib/restricted/boost/config diff --git a/contrib/restricted/boost/geometry/ya.make b/contrib/restricted/boost/geometry/ya.make index bc4a609832..aea4e4cd5e 100644 --- a/contrib/restricted/boost/geometry/ya.make +++ b/contrib/restricted/boost/geometry/ya.make @@ -49,7 +49,6 @@ PEERDIR( contrib/restricted/boost/type_traits contrib/restricted/boost/utility contrib/restricted/boost/variant - contrib/restricted/boost/variant2 ) ADDINCL( diff --git a/contrib/restricted/boost/program_options/include/boost/program_options/detail/config_file.hpp b/contrib/restricted/boost/program_options/include/boost/program_options/detail/config_file.hpp index be6bba1447..808bec7f1d 100644 --- a/contrib/restricted/boost/program_options/include/boost/program_options/detail/config_file.hpp +++ b/contrib/restricted/boost/program_options/include/boost/program_options/detail/config_file.hpp @@ -76,7 +76,7 @@ namespace boost { namespace program_options { namespace detail { const std::set<std::string>& allowed_options, bool allow_unregistered = false); - virtual ~common_config_file_iterator() {} + BOOST_DEFAULTED_FUNCTION(virtual ~common_config_file_iterator(), {}) public: // Method required by eof_iterator diff --git a/contrib/restricted/boost/program_options/include/boost/program_options/errors.hpp b/contrib/restricted/boost/program_options/include/boost/program_options/errors.hpp index 73d16884a2..0b33f80fa3 100644 --- a/contrib/restricted/boost/program_options/include/boost/program_options/errors.hpp +++ b/contrib/restricted/boost/program_options/include/boost/program_options/errors.hpp @@ -121,7 +121,7 @@ namespace boost { namespace program_options { /** gcc says that throw specification on dtor is loosened * without this line * */ - ~error_with_option_name() {} + BOOST_DEFAULTED_FUNCTION(~error_with_option_name(), {}) //void dump() const @@ -183,7 +183,7 @@ namespace boost { namespace program_options { /** Creates the error_message on the fly * Currently a thin wrapper for substitute_placeholders() */ - virtual const char* what() const noexcept; + virtual const char* what() const BOOST_NOEXCEPT_OR_NOTHROW; protected: /** Used to hold the error text returned by what() */ @@ -209,7 +209,7 @@ namespace boost { namespace program_options { multiple_values() : error_with_option_name("option '%canonical_option%' only takes a single argument"){} - ~multiple_values() {} + BOOST_DEFAULTED_FUNCTION(~multiple_values(), {}) }; /** Class thrown when there are several occurrences of an @@ -220,7 +220,7 @@ namespace boost { namespace program_options { multiple_occurrences() : error_with_option_name("option '%canonical_option%' cannot be specified more than once"){} - ~multiple_occurrences() {} + BOOST_DEFAULTED_FUNCTION(~multiple_occurrences(), {}) }; @@ -233,7 +233,7 @@ namespace boost { namespace program_options { { } - ~required_option() {} + BOOST_DEFAULTED_FUNCTION(~required_option(), {}) }; /** Base class of unparsable options, @@ -258,7 +258,7 @@ namespace boost { namespace program_options { /** Does NOT set option name, because no option name makes sense */ virtual void set_option_name(const std::string&) {} - ~error_with_no_option_name() {} + BOOST_DEFAULTED_FUNCTION(~error_with_no_option_name(), {}) }; @@ -270,7 +270,7 @@ namespace boost { namespace program_options { { } - ~unknown_option() {} + BOOST_DEFAULTED_FUNCTION(~unknown_option(), {}) }; @@ -283,9 +283,9 @@ namespace boost { namespace program_options { m_alternatives(xalternatives) {} - ~ambiguous_option() {} + BOOST_DEFAULTED_FUNCTION(~ambiguous_option(), {}) - const std::vector<std::string>& alternatives() const noexcept {return m_alternatives;} + const std::vector<std::string>& alternatives() const BOOST_NOEXCEPT_OR_NOTHROW {return m_alternatives;} protected: /** Makes all substitutions using the template */ @@ -320,7 +320,7 @@ namespace boost { namespace program_options { { } - ~invalid_syntax() {} + BOOST_DEFAULTED_FUNCTION(~invalid_syntax(), {}) kind_t kind() const {return m_kind;} @@ -340,7 +340,7 @@ namespace boost { namespace program_options { m_substitutions["invalid_line"] = invalid_line; } - ~invalid_config_file_syntax() {} + BOOST_DEFAULTED_FUNCTION(~invalid_config_file_syntax(), {}) /** Convenience functions for backwards compatibility */ virtual std::string tokens() const {return m_substitutions.find("invalid_line")->second; } @@ -355,7 +355,7 @@ namespace boost { namespace program_options { const std::string& original_token = "", int option_style = 0): invalid_syntax(kind, option_name, original_token, option_style) {} - ~invalid_command_line_syntax() {} + BOOST_DEFAULTED_FUNCTION(~invalid_command_line_syntax(), {}) }; @@ -380,7 +380,7 @@ namespace boost { namespace program_options { { } - ~validation_error() {} + BOOST_DEFAULTED_FUNCTION(~validation_error(), {}) kind_t kind() const { return m_kind; } diff --git a/contrib/restricted/boost/program_options/src/cmdline.cpp b/contrib/restricted/boost/program_options/src/cmdline.cpp index a768fa2be7..8f4ae64e4a 100644 --- a/contrib/restricted/boost/program_options/src/cmdline.cpp +++ b/contrib/restricted/boost/program_options/src/cmdline.cpp @@ -3,7 +3,9 @@ // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) -#define BOOST_PROGRAM_OPTIONS_SOURCE +#ifndef BOOST_PROGRAM_OPTIONS_SOURCE +# define BOOST_PROGRAM_OPTIONS_SOURCE +#endif #include <boost/program_options/config.hpp> #include <boost/config.hpp> diff --git a/contrib/restricted/boost/program_options/src/config_file.cpp b/contrib/restricted/boost/program_options/src/config_file.cpp index f2a57b4b82..d586523703 100644 --- a/contrib/restricted/boost/program_options/src/config_file.cpp +++ b/contrib/restricted/boost/program_options/src/config_file.cpp @@ -3,8 +3,9 @@ // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) - -#define BOOST_PROGRAM_OPTIONS_SOURCE +#ifndef BOOST_PROGRAM_OPTIONS_SOURCE +# define BOOST_PROGRAM_OPTIONS_SOURCE +#endif #include <boost/program_options/config.hpp> #include <boost/program_options/detail/config_file.hpp> diff --git a/contrib/restricted/boost/program_options/src/convert.cpp b/contrib/restricted/boost/program_options/src/convert.cpp index 692fcde203..fbe2758b12 100644 --- a/contrib/restricted/boost/program_options/src/convert.cpp +++ b/contrib/restricted/boost/program_options/src/convert.cpp @@ -13,7 +13,9 @@ #include <boost/config.hpp> -#define BOOST_PROGRAM_OPTIONS_SOURCE +#ifndef BOOST_PROGRAM_OPTIONS_SOURCE +# define BOOST_PROGRAM_OPTIONS_SOURCE +#endif #include <boost/program_options/config.hpp> #include <boost/program_options/detail/convert.hpp> #include <boost/program_options/detail/utf8_codecvt_facet.hpp> diff --git a/contrib/restricted/boost/program_options/src/options_description.cpp b/contrib/restricted/boost/program_options/src/options_description.cpp index dc0eae8744..0efdc33882 100644 --- a/contrib/restricted/boost/program_options/src/options_description.cpp +++ b/contrib/restricted/boost/program_options/src/options_description.cpp @@ -4,8 +4,9 @@ // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) - -#define BOOST_PROGRAM_OPTIONS_SOURCE +#ifndef BOOST_PROGRAM_OPTIONS_SOURCE +# define BOOST_PROGRAM_OPTIONS_SOURCE +#endif #include <boost/program_options/config.hpp> #include <boost/program_options/options_description.hpp> // FIXME: this is only to get multiple_occurrences class diff --git a/contrib/restricted/boost/program_options/src/parsers.cpp b/contrib/restricted/boost/program_options/src/parsers.cpp index f37953e0cb..4dac608d3e 100644 --- a/contrib/restricted/boost/program_options/src/parsers.cpp +++ b/contrib/restricted/boost/program_options/src/parsers.cpp @@ -6,7 +6,9 @@ #include <boost/config.hpp> -#define BOOST_PROGRAM_OPTIONS_SOURCE +#ifndef BOOST_PROGRAM_OPTIONS_SOURCE +# define BOOST_PROGRAM_OPTIONS_SOURCE +#endif #include <boost/program_options/config.hpp> #include <boost/program_options/parsers.hpp> #include <boost/program_options/options_description.hpp> diff --git a/contrib/restricted/boost/program_options/src/positional_options.cpp b/contrib/restricted/boost/program_options/src/positional_options.cpp index 72dc0d6b0d..cb6a145240 100644 --- a/contrib/restricted/boost/program_options/src/positional_options.cpp +++ b/contrib/restricted/boost/program_options/src/positional_options.cpp @@ -3,7 +3,9 @@ // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) -#define BOOST_PROGRAM_OPTIONS_SOURCE +#ifndef BOOST_PROGRAM_OPTIONS_SOURCE +# define BOOST_PROGRAM_OPTIONS_SOURCE +#endif #include <boost/program_options/config.hpp> #include <boost/program_options/positional_options.hpp> diff --git a/contrib/restricted/boost/program_options/src/split.cpp b/contrib/restricted/boost/program_options/src/split.cpp index 96da068b82..c674403ff2 100644 --- a/contrib/restricted/boost/program_options/src/split.cpp +++ b/contrib/restricted/boost/program_options/src/split.cpp @@ -3,7 +3,9 @@ // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) -#define BOOST_PROGRAM_OPTIONS_SOURCE +#ifndef BOOST_PROGRAM_OPTIONS_SOURCE +# define BOOST_PROGRAM_OPTIONS_SOURCE +#endif #include <boost/program_options/parsers.hpp> #include <boost/tokenizer.hpp> diff --git a/contrib/restricted/boost/program_options/src/utf8_codecvt_facet.cpp b/contrib/restricted/boost/program_options/src/utf8_codecvt_facet.cpp index 2e4c532c35..075e4aebb9 100644 --- a/contrib/restricted/boost/program_options/src/utf8_codecvt_facet.cpp +++ b/contrib/restricted/boost/program_options/src/utf8_codecvt_facet.cpp @@ -3,7 +3,9 @@ // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) -#define BOOST_PROGRAM_OPTIONS_SOURCE +#ifndef BOOST_PROGRAM_OPTIONS_SOURCE +# define BOOST_PROGRAM_OPTIONS_SOURCE +#endif #include <boost/program_options/config.hpp> #define BOOST_UTF8_BEGIN_NAMESPACE \ diff --git a/contrib/restricted/boost/program_options/src/value_semantic.cpp b/contrib/restricted/boost/program_options/src/value_semantic.cpp index a7366d4386..de7b2ace84 100644 --- a/contrib/restricted/boost/program_options/src/value_semantic.cpp +++ b/contrib/restricted/boost/program_options/src/value_semantic.cpp @@ -3,7 +3,9 @@ // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) -#define BOOST_PROGRAM_OPTIONS_SOURCE +#ifndef BOOST_PROGRAM_OPTIONS_SOURCE +# define BOOST_PROGRAM_OPTIONS_SOURCE +#endif #include <boost/program_options/config.hpp> #include <boost/program_options/value_semantic.hpp> #include <boost/program_options/detail/convert.hpp> @@ -258,7 +260,7 @@ namespace boost { namespace program_options { } - const char* error_with_option_name::what() const throw() + const char* error_with_option_name::what() const BOOST_NOEXCEPT_OR_NOTHROW { // will substitute tokens each time what is run() substitute_placeholders(m_error_template); diff --git a/contrib/restricted/boost/program_options/src/variables_map.cpp b/contrib/restricted/boost/program_options/src/variables_map.cpp index bc85f7e34b..94ac6148c4 100644 --- a/contrib/restricted/boost/program_options/src/variables_map.cpp +++ b/contrib/restricted/boost/program_options/src/variables_map.cpp @@ -3,8 +3,9 @@ // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) - -#define BOOST_PROGRAM_OPTIONS_SOURCE +#ifndef BOOST_PROGRAM_OPTIONS_SOURCE +# define BOOST_PROGRAM_OPTIONS_SOURCE +#endif #include <boost/program_options/config.hpp> #include <boost/program_options/parsers.hpp> #include <boost/program_options/options_description.hpp> diff --git a/contrib/restricted/boost/program_options/src/winmain.cpp b/contrib/restricted/boost/program_options/src/winmain.cpp index 6220043f6c..31c8adcb7f 100644 --- a/contrib/restricted/boost/program_options/src/winmain.cpp +++ b/contrib/restricted/boost/program_options/src/winmain.cpp @@ -3,7 +3,9 @@ // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) -#define BOOST_PROGRAM_OPTIONS_SOURCE +#ifndef BOOST_PROGRAM_OPTIONS_SOURCE +# define BOOST_PROGRAM_OPTIONS_SOURCE +#endif #include <boost/program_options/parsers.hpp> #include <cctype> diff --git a/contrib/restricted/boost/program_options/ya.make b/contrib/restricted/boost/program_options/ya.make index 73d8bfc35e..b7864afb64 100644 --- a/contrib/restricted/boost/program_options/ya.make +++ b/contrib/restricted/boost/program_options/ya.make @@ -6,9 +6,9 @@ LICENSE(BSL-1.0) LICENSE_TEXTS(.yandex_meta/licenses.list.txt) -VERSION(1.83.0) +VERSION(1.84.0) -ORIGINAL_SOURCE(https://github.com/boostorg/program_options/archive/boost-1.83.0.tar.gz) +ORIGINAL_SOURCE(https://github.com/boostorg/program_options/archive/boost-1.84.0.tar.gz) PEERDIR( contrib/restricted/boost/any diff --git a/contrib/tools/cython/.dist-info/METADATA b/contrib/tools/cython/.dist-info/METADATA index 10f4b69cd5..78e3d977d9 100644 --- a/contrib/tools/cython/.dist-info/METADATA +++ b/contrib/tools/cython/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: Cython -Version: 0.29.36 +Version: 0.29.37 Summary: The Cython compiler for writing C extensions for the Python language. Home-page: http://cython.org/ Author: Robert Bradshaw, Stefan Behnel, Dag Seljebotn, Greg Ewing, et al. diff --git a/contrib/tools/cython/CHANGES.rst b/contrib/tools/cython/CHANGES.rst index 829bfdcaff..ddb4d52816 100644 --- a/contrib/tools/cython/CHANGES.rst +++ b/contrib/tools/cython/CHANGES.rst @@ -2,6 +2,25 @@ Cython Changelog ================ +0.29.37 (2023-12-18) +==================== + +Bugs fixed +---------- + +* Fix a potential crash while cleaning up subtypes of externally imported extension + types when terminating Python. This was introduced in Cython 0.29.35. + +* Fix a ``complex`` related compile error on Windows. + (Github issue :issue:`5512`) + +* Compiling fused types used in pxd files could crash Cython in Python 3.11+. + (Github issues :issue:`5894`, :issue:`5588`) + +* ``cythonize`` failed to consider the ``CYTHON_FORCE_REGEN`` env variable. + Patch by Harmen Stoppels. (Github issue :issue:`5712`) + + 0.29.36 (2023-07-04) ==================== diff --git a/contrib/tools/cython/Cython/Build/Dependencies.py b/contrib/tools/cython/Cython/Build/Dependencies.py index 28c48ed8c3..92fa96aa01 100644 --- a/contrib/tools/cython/Cython/Build/Dependencies.py +++ b/contrib/tools/cython/Cython/Build/Dependencies.py @@ -878,7 +878,7 @@ def create_extension_list(patterns, exclude=None, ctx=None, aliases=None, quiet= # This is the user-exposed entry point. -def cythonize(module_list, exclude=None, nthreads=0, aliases=None, quiet=False, force=False, language=None, +def cythonize(module_list, exclude=None, nthreads=0, aliases=None, quiet=False, force=None, language=None, exclude_failures=False, **options): """ Compile a set of source modules into C/C++ files and return a list of distutils diff --git a/contrib/tools/cython/Cython/Compiler/FusedNode.py b/contrib/tools/cython/Cython/Compiler/FusedNode.py index 26d6ffd3d6..c32b02d941 100644 --- a/contrib/tools/cython/Cython/Compiler/FusedNode.py +++ b/contrib/tools/cython/Cython/Compiler/FusedNode.py @@ -190,12 +190,11 @@ class FusedCFuncDefNode(StatListNode): break # replace old entry with new entries - try: + if self.node.entry in env.cfunc_entries: cindex = env.cfunc_entries.index(self.node.entry) - except ValueError: - env.cfunc_entries.extend(new_cfunc_entries) - else: env.cfunc_entries[cindex:cindex+1] = new_cfunc_entries + else: + env.cfunc_entries.extend(new_cfunc_entries) if orig_py_func: self.py_func = self.make_fused_cpdef(orig_py_func, env, diff --git a/contrib/tools/cython/Cython/Compiler/ModuleNode.py b/contrib/tools/cython/Cython/Compiler/ModuleNode.py index fd998d419f..ff217bb4e7 100644 --- a/contrib/tools/cython/Cython/Compiler/ModuleNode.py +++ b/contrib/tools/cython/Cython/Compiler/ModuleNode.py @@ -1515,7 +1515,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): if base_type.scope and base_type.scope.needs_gc(): code.putln("PyObject_GC_Track(o);") else: - code.putln("if (PyType_IS_GC(%s)) PyObject_GC_Track(o);" % base_cname) + # Need to check type pointer, see comment on dealloc call below. + code.putln("if (%sPyType_IS_GC(%s)) PyObject_GC_Track(o);" % ( + "" if base_type.is_builtin_type else "likely(%s) && " % base_cname, + base_cname)) tp_dealloc = TypeSlots.get_base_slot_function(scope, tp_slot) if tp_dealloc is not None: diff --git a/contrib/tools/cython/Cython/Compiler/PyrexTypes.py b/contrib/tools/cython/Cython/Compiler/PyrexTypes.py index b2c92989ff..807c766c7f 100644 --- a/contrib/tools/cython/Cython/Compiler/PyrexTypes.py +++ b/contrib/tools/cython/Cython/Compiler/PyrexTypes.py @@ -2889,7 +2889,12 @@ class CFuncType(CType): if self.is_overridable: arg_decl_list.append("int %s" % Naming.skip_dispatch_cname) if self.optional_arg_count: - arg_decl_list.append(self.op_arg_struct.declaration_code(Naming.optional_args_cname)) + if self.op_arg_struct: + arg_decl_list.append(self.op_arg_struct.declaration_code(Naming.optional_args_cname)) + else: + # op_arg_struct may not be initialized at this point if this class is being used + # to prepare a Python error message or similar. In this case, just omit the args. + assert for_display if self.has_varargs: arg_decl_list.append("...") arg_decl_code = ", ".join(arg_decl_list) diff --git a/contrib/tools/cython/Cython/Shadow.py b/contrib/tools/cython/Cython/Shadow.py index a3991f0494..1ffb93e9dc 100644 --- a/contrib/tools/cython/Cython/Shadow.py +++ b/contrib/tools/cython/Cython/Shadow.py @@ -1,7 +1,7 @@ # cython.* namespace for pure mode. from __future__ import absolute_import -__version__ = "0.29.36" +__version__ = "0.29.37" try: from __builtin__ import basestring diff --git a/contrib/tools/cython/Cython/Shadow.pyi b/contrib/tools/cython/Cython/Shadow.pyi new file mode 100644 index 0000000000..42827a3ac1 --- /dev/null +++ b/contrib/tools/cython/Cython/Shadow.pyi @@ -0,0 +1,102 @@ +from builtins import (int as py_int, float as py_float, + bool as py_bool, str as py_str, complex as py_complex) +from typing import (Union, Dict, Any, Sequence, Optional, + List, TypeVar, Type, Generic) + +int = py_int +long = py_int +longlong = py_int +short = py_int +char = py_int +sint = py_int +slong = py_int +slonglong = py_int +sshort = py_int +schar = py_int +uint = py_int +ulong = py_int +ulonglong = py_int +ushort = py_int +uchar = py_int +size_t = py_int +Py_ssize_t = py_int +Py_UCS4 = Union[py_int, str] +Py_UNICODE = Union[py_int, str] +float = py_float +double = py_float +longdouble = py_float +complex = py_complex +floatcomplex = py_complex +doublecomplex = py_complex +longdoublecomplex = py_complex +bint = py_bool +void = Union[None] +basestring = py_str +unicode = py_str + +gs: Dict[str, Any] # Should match the return type of globals() + +_T = TypeVar('_T') + +class _ArrayType(object, Generic[_T]): + is_array: bool + subtypes: Sequence[str] + dtype: _T + ndim: int + is_c_contig: bool + is_f_contig: bool + inner_contig: bool + broadcasting: Any + + # broadcasting is not used, so it's not clear about its type + def __init__(self, dtype: _T, ndim: int, is_c_contig: bool = ..., + is_f_contig: bool = ..., inner_contig: bool = ..., + broadcasting: Any = ...) -> None: ... + def __repr__(self) -> str: ... + +class CythonTypeObject(object): + ... + +class CythonType(CythonTypeObject): + ... + +class PointerType(CythonType, Generic[_T]): + def __init__( + self, + value: Optional[Union[ArrayType[_T], PointerType[_T], List[_T], int]] = ... + ) -> None: ... + def __getitem__(self, ix: int) -> _T: ... + def __setitem__(self, ix: int, value: _T) -> None: ... + def __eq__(self, value: object) -> bool: ... + def __repr__(self) -> str: ... + +class ArrayType(PointerType[_T]): + def __init__(self) -> None: ... + +#class StructType(CythonType, Generic[_T]): +# def __init__( +# self, +# value: List[Type[_T]] = ... +# ) -> None: ... + +def index_type( + base_type: _T, item: Union[tuple, slice, int]) -> _ArrayType[_T]: ... + +def pointer(basetype: _T) -> Type[PointerType[_T]]: ... + +def array(basetype: _T, n: int) -> Type[ArrayType[_T]]: ... + +#def struct(basetype: _T) -> Type[StructType[_T]]: ... + +class typedef(CythonType, Generic[_T]): + name: str + + def __init__(self, type: _T, name: Optional[str] = ...) -> None: ... + def __call__(self, *arg: Any) -> _T: ... + def __repr__(self) -> str: ... + __getitem__ = index_type + +#class _FusedType(CythonType, Generic[_T]): +# def __init__(self) -> None: ... + +#def fused_type(*args: Tuple[_T]) -> Type[FusedType[_T]]: ... diff --git a/contrib/tools/cython/Cython/Utility/Complex.c b/contrib/tools/cython/Cython/Utility/Complex.c index 15d5f544dd..099b3c4651 100644 --- a/contrib/tools/cython/Cython/Utility/Complex.c +++ b/contrib/tools/cython/Cython/Utility/Complex.c @@ -4,7 +4,8 @@ #if !defined(CYTHON_CCOMPLEX) #if defined(__cplusplus) #define CYTHON_CCOMPLEX 1 - #elif defined(_Complex_I) + #elif (defined(_Complex_I) && !defined(_MSC_VER)) + // MSVC defines "_Complex_I" but not "_Complex". See https://github.com/cython/cython/issues/5512 #define CYTHON_CCOMPLEX 1 #else #define CYTHON_CCOMPLEX 0 diff --git a/contrib/tools/cython/cython.py b/contrib/tools/cython/cython.py index 30315749d3..554ba1b4ff 100755 --- a/contrib/tools/cython/cython.py +++ b/contrib/tools/cython/cython.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# Change content of this file to change uids for cython programs - cython 0.29.36 r0 +# Change content of this file to change uids for cython programs - cython 0.29.37 r0 # # Cython -- Main Program, generic diff --git a/contrib/tools/cython/ya.make b/contrib/tools/cython/ya.make index fddaea9c02..4546638bd5 100644 --- a/contrib/tools/cython/ya.make +++ b/contrib/tools/cython/ya.make @@ -11,9 +11,9 @@ LICENSE_TEXTS(.yandex_meta/licenses.list.txt) OWNER(g:python-contrib) -VERSION(0.29.36) +VERSION(0.29.37) -ORIGINAL_SOURCE(mirror://pypi/C/Cython/Cython-0.29.36.tar.gz) +ORIGINAL_SOURCE(mirror://pypi/C/Cython/Cython-0.29.37.tar.gz) NO_LINT() diff --git a/contrib/tools/python/src/Lib/unittest/case.py b/contrib/tools/python/src/Lib/unittest/case.py index 3f19d29697..9befb36f33 100644 --- a/contrib/tools/python/src/Lib/unittest/case.py +++ b/contrib/tools/python/src/Lib/unittest/case.py @@ -993,9 +993,6 @@ class TestCase(object): with context: callable_obj(*args, **kwargs) - # XXX Для более простой миграции существующих тестов на Python 3 - assertRaisesRegex = assertRaisesRegexp - def assertRegexpMatches(self, text, expected_regexp, msg=None): """Fail the test unless the text matches the regular expression.""" if isinstance(expected_regexp, basestring): @@ -1018,6 +1015,11 @@ class TestCase(object): text) raise self.failureException(msg) + # XXX Для более простой миграции существующих тестов на Python 3 + assertRaisesRegex = assertRaisesRegexp + assertRegex = assertRegexpMatches + + class FunctionTestCase(TestCase): """A test case that wraps a test function. diff --git a/util/generic/singleton.h b/util/generic/singleton.h index 4cc31de39c..ed01efb213 100644 --- a/util/generic/singleton.h +++ b/util/generic/singleton.h @@ -1,6 +1,7 @@ #pragma once #include <util/system/atexit.h> +#include <util/system/compiler.h> #include <atomic> #include <new> @@ -111,22 +112,22 @@ namespace NPrivate { friend T* ::NPrivate::SingletonBase(std::atomic<T*>&, TArgs&&...); template <class T, class... TArgs> -T* Singleton(TArgs&&... args) { +Y_RETURNS_NONNULL T* Singleton(TArgs&&... args) { return ::NPrivate::SingletonInt<T, TSingletonTraits<T>::Priority>(std::forward<TArgs>(args)...); } template <class T, class... TArgs> -T* HugeSingleton(TArgs&&... args) { +Y_RETURNS_NONNULL T* HugeSingleton(TArgs&&... args) { return Singleton<::NPrivate::THeapStore<T>>(std::forward<TArgs>(args)...)->D; } template <class T, size_t P, class... TArgs> -T* SingletonWithPriority(TArgs&&... args) { +Y_RETURNS_NONNULL T* SingletonWithPriority(TArgs&&... args) { return ::NPrivate::SingletonInt<T, P>(std::forward<TArgs>(args)...); } template <class T, size_t P, class... TArgs> -T* HugeSingletonWithPriority(TArgs&&... args) { +Y_RETURNS_NONNULL T* HugeSingletonWithPriority(TArgs&&... args) { return SingletonWithPriority<::NPrivate::THeapStore<T>, P>(std::forward<TArgs>(args)...)->D; } diff --git a/util/system/compiler.h b/util/system/compiler.h index 95cb61dd6b..259232b67a 100644 --- a/util/system/compiler.h +++ b/util/system/compiler.h @@ -670,3 +670,43 @@ Y_FORCE_INLINE void DoNotOptimizeAway(const T&) = delete; #else #define Y_LIFETIME_BOUND #endif + +/** + * @def Y_HAVE_ATTRIBUTE + * + * A function-like feature checking macro that is a wrapper around + * `__has_attribute`, which is defined by GCC 5+ and Clang and evaluates to a + * nonzero constant integer if the attribute is supported or 0 if not. + * + * It evaluates to zero if `__has_attribute` is not defined by the compiler. + * + * @see + * GCC: https://gcc.gnu.org/gcc-5/changes.html + * Clang: https://clang.llvm.org/docs/LanguageExtensions.html + */ +#ifdef __has_attribute + #define Y_HAVE_ATTRIBUTE(x) __has_attribute(x) +#else + #define Y_HAVE_ATTRIBUTE(x) 0 +#endif + +/** + * @def Y_RETURNS_NONNULL + * + * The returns_nonnull attribute specifies that the function return value should + * be a non-null pointer. It lets the compiler optimize callers based on + * the knowledge that the return value will never be null. + * + * @see + * GCC: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-returns_005fnonnull-function-attribute + * Clang: https://clang.llvm.org/docs/AttributeReference.html#returns-nonnull + * + * @code + * Y_RETURNS_NONNULL extern void* mymalloc(size_t len); + * @endcode + */ +#if Y_HAVE_ATTRIBUTE(returns_nonnull) + #define Y_RETURNS_NONNULL __attribute__((returns_nonnull)) +#else + #define Y_RETURNS_NONNULL +#endif diff --git a/util/thread/singleton.h b/util/thread/singleton.h index 4a1e05aea0..dc9ee5a4eb 100644 --- a/util/thread/singleton.h +++ b/util/thread/singleton.h @@ -28,7 +28,7 @@ namespace NPrivate { } template <class T, size_t Priority> -static inline T* FastTlsSingletonWithPriority() { +Y_RETURNS_NONNULL static inline T* FastTlsSingletonWithPriority() { return ::NPrivate::TFastThreadSingletonHelper<T, Priority>::Get(); } @@ -36,6 +36,6 @@ static inline T* FastTlsSingletonWithPriority() { // FastTlsSingleton with the same type parameter. If unique singleton // required, use unique types. template <class T> -static inline T* FastTlsSingleton() { +Y_RETURNS_NONNULL static inline T* FastTlsSingleton() { return FastTlsSingletonWithPriority<T, TSingletonTraits<T>::Priority>(); } diff --git a/yt/cpp/mapreduce/interface/operation.cpp b/yt/cpp/mapreduce/interface/operation.cpp index 0bb490b1ac..b3229117d9 100644 --- a/yt/cpp/mapreduce/interface/operation.cpp +++ b/yt/cpp/mapreduce/interface/operation.cpp @@ -337,11 +337,7 @@ void TJobOperationPreparer::FinallyValidate() const TApiUsageError error; error << "Output table schemas are missing: "; for (auto i : illegallyMissingSchemaIndices) { - error << "no. " << i; - if (auto path = Context_.GetInputPath(i)) { - error << "(" << *path << ")"; - } - error << "; "; + error << "no. " << i << " (" << Context_.GetOutputPath(i).GetOrElse("<unknown path>") << "); "; } ythrow std::move(error); } diff --git a/yt/yt/client/api/public.h b/yt/yt/client/api/public.h index 82a2adbe74..c671dea611 100644 --- a/yt/yt/client/api/public.h +++ b/yt/yt/client/api/public.h @@ -6,6 +6,8 @@ #include <yt/yt/client/transaction_client/public.h> +#include <yt/yt/client/bundle_controller_client/public.h> + #include <yt/yt/library/auth/authentication_options.h> #include <yt/yt/core/misc/public.h> diff --git a/yt/yt/client/api/queue_client.h b/yt/yt/client/api/queue_client.h index a19aef2b90..c665386fcd 100644 --- a/yt/yt/client/api/queue_client.h +++ b/yt/yt/client/api/queue_client.h @@ -39,6 +39,10 @@ struct TPullRowsResult bool Versioned = true; }; +struct TAdvanceConsumerOptions + : public TTimeoutOptions +{ }; + struct TPullQueueOptions : public TSelectRowsOptions , public TFallbackReplicaOptions diff --git a/yt/yt/client/api/rpc_proxy/api_service_proxy.h b/yt/yt/client/api/rpc_proxy/api_service_proxy.h index 354c883ef5..3a28b830ca 100644 --- a/yt/yt/client/api/rpc_proxy/api_service_proxy.h +++ b/yt/yt/client/api/rpc_proxy/api_service_proxy.h @@ -84,6 +84,7 @@ public: DEFINE_RPC_PROXY_METHOD(NRpcProxy::NProto, AlterReplicationCard); // Queues + DEFINE_RPC_PROXY_METHOD(NRpcProxy::NProto, AdvanceConsumer); DEFINE_RPC_PROXY_METHOD(NRpcProxy::NProto, PullQueue); DEFINE_RPC_PROXY_METHOD(NRpcProxy::NProto, PullConsumer); DEFINE_RPC_PROXY_METHOD(NRpcProxy::NProto, RegisterQueueConsumer); diff --git a/yt/yt/client/api/rpc_proxy/transaction_impl.cpp b/yt/yt/client/api/rpc_proxy/transaction_impl.cpp index 6777b7109f..e3c621c8f1 100644 --- a/yt/yt/client/api/rpc_proxy/transaction_impl.cpp +++ b/yt/yt/client/api/rpc_proxy/transaction_impl.cpp @@ -485,6 +485,39 @@ void TTransaction::ModifyRows( } } +TFuture<void> TTransaction::AdvanceConsumer( + const NYPath::TRichYPath& consumerPath, + const NYPath::TRichYPath& queuePath, + int partitionIndex, + std::optional<i64> oldOffset, + i64 newOffset, + const TAdvanceConsumerOptions& options) +{ + ValidateTabletTransactionId(GetId()); + + THROW_ERROR_EXCEPTION_IF(newOffset < 0, "Queue consumer offset %v cannot be negative", newOffset); + + auto req = Proxy_.AdvanceConsumer(); + SetTimeoutOptions(*req, options); + + if (NTracing::IsCurrentTraceContextRecorded()) { + req->TracingTags().emplace_back("yt.consumer_path", ToString(consumerPath)); + req->TracingTags().emplace_back("yt.queue_path", ToString(queuePath)); + } + + ToProto(req->mutable_transaction_id(), GetId()); + + ToProto(req->mutable_consumer_path(), consumerPath); + ToProto(req->mutable_queue_path(), queuePath); + req->set_partition_index(partitionIndex); + if (oldOffset) { + req->set_old_offset(*oldOffset); + } + req->set_new_offset(newOffset); + + return req->Invoke().As<void>(); +} + TFuture<ITransactionPtr> TTransaction::StartTransaction( ETransactionType type, const TTransactionStartOptions& options) diff --git a/yt/yt/client/api/rpc_proxy/transaction_impl.h b/yt/yt/client/api/rpc_proxy/transaction_impl.h index 200e9e7539..5286ea931b 100644 --- a/yt/yt/client/api/rpc_proxy/transaction_impl.h +++ b/yt/yt/client/api/rpc_proxy/transaction_impl.h @@ -73,6 +73,14 @@ public: TSharedRange<NApi::TRowModification> modifications, const NApi::TModifyRowsOptions& options) override; + TFuture<void> AdvanceConsumer( + const NYPath::TRichYPath& consumerPath, + const NYPath::TRichYPath& queuePath, + int partitionIndex, + std::optional<i64> oldOffset, + i64 newOffset, + const TAdvanceConsumerOptions& options) override; + // IClientBase implementation. TFuture<NApi::ITransactionPtr> StartTransaction( NTransactionClient::ETransactionType type, diff --git a/yt/yt/client/api/security_client.h b/yt/yt/client/api/security_client.h index ed76f72d26..2c979a147d 100644 --- a/yt/yt/client/api/security_client.h +++ b/yt/yt/client/api/security_client.h @@ -76,11 +76,21 @@ struct TIssueTokenOptions : public TTimeoutOptions { }; +struct TIssueTemporaryTokenOptions + : public TIssueTokenOptions +{ + TDuration ExpirationTimeout; +}; + struct TIssueTokenResult { TString Token; }; +struct TRefreshTemporaryTokenOptions + : public TTimeoutOptions +{ }; + struct TRevokeTokenOptions : public TTimeoutOptions { }; diff --git a/yt/yt/client/api/transaction.h b/yt/yt/client/api/transaction.h index 4677db4864..3fb39fa740 100644 --- a/yt/yt/client/api/transaction.h +++ b/yt/yt/client/api/transaction.h @@ -240,22 +240,32 @@ struct ITransaction // Consumers. - //! Advance the consumer's offset for the given partition, setting it to a new value. - //! - //! If oldOffset is specified, the current offset is read inside this transaction and compared with oldOffset. - //! If they are equal, the new offset is written, otherwise an exception is thrown. + // TODO(nadya73): Remove it: YT-20712 void AdvanceConsumer( const NYPath::TYPath& path, int partitionIndex, std::optional<i64> oldOffset, i64 newOffset); + // TODO(nadya73): Remove it: YT-20712 void AdvanceConsumer( const NYPath::TRichYPath& consumerPath, const NYPath::TRichYPath& queuePath, int partitionIndex, std::optional<i64> oldOffset, i64 newOffset); + + //! Advance the consumer's offset for the given partition with index #partitionIndex, setting it to #newOffset. + //! + //! If #oldOffset is specified, the current offset is read inside this transaction and compared with #oldOffset. + //! If they are equal, the new offset is written, otherwise an exception is thrown. + virtual TFuture<void> AdvanceConsumer( + const NYPath::TRichYPath& consumerPath, + const NYPath::TRichYPath& queuePath, + int partitionIndex, + std::optional<i64> oldOffset, + i64 newOffset, + const TAdvanceConsumerOptions& options) = 0; }; DEFINE_REFCOUNTED_TYPE(ITransaction) diff --git a/yt/yt/client/bundle_controller_client/bundle_controller_client.h b/yt/yt/client/bundle_controller_client/bundle_controller_client.h index 0341458955..30cf31c72e 100644 --- a/yt/yt/client/bundle_controller_client/bundle_controller_client.h +++ b/yt/yt/client/bundle_controller_client/bundle_controller_client.h @@ -1,5 +1,6 @@ #pragma once +#include "public.h" #include "bundle_controller_settings.h" #include <yt/yt/client/api/client_common.h> @@ -8,10 +9,6 @@ namespace NYT::NApi { //////////////////////////////////////////////////////////////////////////////// -DECLARE_REFCOUNTED_STRUCT(TBundleConfigDescriptor) - -//////////////////////////////////////////////////////////////////////////////// - struct TGetBundleConfigOptions : public TTimeoutOptions { }; diff --git a/yt/yt/client/bundle_controller_client/bundle_controller_settings.cpp b/yt/yt/client/bundle_controller_client/bundle_controller_settings.cpp index 194b020c1d..12f044ca9c 100644 --- a/yt/yt/client/bundle_controller_client/bundle_controller_settings.cpp +++ b/yt/yt/client/bundle_controller_client/bundle_controller_settings.cpp @@ -64,10 +64,15 @@ bool TInstanceResources::operator==(const TInstanceResources& other) const namespace NProto { -// TODO(alexmipt): make ToProto for TCpuLimits, TMemoryLimits, TInstanceResources - //////////////////////////////////////////////////////////////////////////////// +void ToProto(NBundleController::NProto::TCpuLimits* protoCpuLimits, const NBundleControllerClient::TCpuLimitsPtr cpuLimits) +{ + protoCpuLimits->set_lookup_thread_pool_size(cpuLimits->LookupThreadPoolSize); + protoCpuLimits->set_query_thread_pool_size(cpuLimits->QueryThreadPoolSize); + protoCpuLimits->set_write_thread_pool_size(cpuLimits->WriteThreadPoolSize); +} + void FromProto(NBundleControllerClient::TCpuLimitsPtr cpuLimits, const NBundleController::NProto::TCpuLimits* protoCpuLimits) { cpuLimits->LookupThreadPoolSize = protoCpuLimits->lookup_thread_pool_size(); @@ -77,6 +82,20 @@ void FromProto(NBundleControllerClient::TCpuLimitsPtr cpuLimits, const NBundleCo //////////////////////////////////////////////////////////////////////////////// +void ToProto(NBundleController::NProto::TMemoryLimits* protoMemoryLimits, const NBundleControllerClient::TMemoryLimitsPtr memoryLimits) +{ + protoMemoryLimits->set_compressed_block_cache(memoryLimits->CompressedBlockCache.value_or(0)); + protoMemoryLimits->set_key_filter_block_cache(memoryLimits->KeyFilterBlockCache.value_or(0)); + protoMemoryLimits->set_lookup_row_cache(memoryLimits->LookupRowCache.value_or(0)); + + protoMemoryLimits->set_tablet_dynamic(memoryLimits->TabletDynamic.value_or(0)); + protoMemoryLimits->set_tablet_static(memoryLimits->TabletStatic.value_or(0)); + + protoMemoryLimits->set_uncompressed_block_cache(memoryLimits->UncompressedBlockCache.value_or(0)); + + protoMemoryLimits->set_versioned_chunk_meta(memoryLimits->VersionedChunkMeta.value_or(0)); +} + void FromProto(NBundleControllerClient::TMemoryLimitsPtr memoryLimits, const NBundleController::NProto::TMemoryLimits* protoMemoryLimits) { memoryLimits->CompressedBlockCache = protoMemoryLimits->compressed_block_cache(); @@ -93,6 +112,14 @@ void FromProto(NBundleControllerClient::TMemoryLimitsPtr memoryLimits, const NBu //////////////////////////////////////////////////////////////////////////////// +void ToProto(NBundleController::NProto::TInstanceResources* protoInstanceResources, const NBundleControllerClient::TInstanceResourcesPtr instanceResources) +{ + protoInstanceResources->set_memory(instanceResources->Memory); + protoInstanceResources->set_net(instanceResources->Net.value_or(0)); + protoInstanceResources->set_type(instanceResources->Type); + protoInstanceResources->set_vcpu(instanceResources->Vcpu); +} + void FromProto(NBundleControllerClient::TInstanceResourcesPtr instanceResources, const NBundleController::NProto::TInstanceResources* protoInstanceResources) { instanceResources->Memory = protoInstanceResources->memory(); diff --git a/yt/yt/client/bundle_controller_client/bundle_controller_settings.h b/yt/yt/client/bundle_controller_client/bundle_controller_settings.h index 8dcf241ae1..da57ead80a 100644 --- a/yt/yt/client/bundle_controller_client/bundle_controller_settings.h +++ b/yt/yt/client/bundle_controller_client/bundle_controller_settings.h @@ -1,5 +1,7 @@ #pragma once +#include "public.h" + #include <optional> #include <yt/yt/client/tablet_client/public.h> @@ -13,12 +15,6 @@ namespace NYT::NBundleControllerClient { //////////////////////////////////////////////////////////////////////////////// -DECLARE_REFCOUNTED_STRUCT(TCpuLimits) -DECLARE_REFCOUNTED_STRUCT(TMemoryLimits) -DECLARE_REFCOUNTED_STRUCT(TInstanceResources) - -//////////////////////////////////////////////////////////////////////////////// - struct TCpuLimits : public NYTree::TYsonStruct { diff --git a/yt/yt/client/bundle_controller_client/public.h b/yt/yt/client/bundle_controller_client/public.h new file mode 100644 index 0000000000..d73eac1539 --- /dev/null +++ b/yt/yt/client/bundle_controller_client/public.h @@ -0,0 +1,35 @@ +#pragma once + +#include <yt/yt/core/misc/public.h> + +#include <yt/yt/client/hydra/public.h> + +#include <yt/yt/client/object_client/public.h> + +namespace NYT::NApi { + +//////////////////////////////////////////////////////////////////////////////// + +DECLARE_REFCOUNTED_STRUCT(TBundleConfigDescriptor) + +struct TBundleConfigDescriptor; + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT::NApi + +namespace NYT::NBundleControllerClient { + +//////////////////////////////////////////////////////////////////////////////// + +DECLARE_REFCOUNTED_STRUCT(TCpuLimits) +DECLARE_REFCOUNTED_STRUCT(TMemoryLimits) +DECLARE_REFCOUNTED_STRUCT(TInstanceResources) + +struct TCpuLimits; +struct TMemoryLimits; +struct TInstanceResources; + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT::NBundleControllerClient diff --git a/yt/yt/client/chunk_client/config.h b/yt/yt/client/chunk_client/config.h index 5a28cb0750..9f3488b003 100644 --- a/yt/yt/client/chunk_client/config.h +++ b/yt/yt/client/chunk_client/config.h @@ -4,7 +4,7 @@ #include <yt/yt/client/misc/config.h> -#include <yt/yt/core/ytree/yson_serializable.h> +#include <yt/yt/core/ytree/yson_struct.h> namespace NYT::NChunkClient { diff --git a/yt/yt/client/chunk_client/public.h b/yt/yt/client/chunk_client/public.h index 6cf877026e..1482ff2a58 100644 --- a/yt/yt/client/chunk_client/public.h +++ b/yt/yt/client/chunk_client/public.h @@ -79,6 +79,7 @@ YT_DEFINE_ERROR_ENUM( ((LocationCrashed) (750)) ((LocationDiskWaitingReplacement) (751)) ((ChunkMetaCacheFetchFailed) (752)) + ((LocationMediumIsMisconfigured) (753)) ); using TChunkId = NObjectClient::TObjectId; diff --git a/yt/yt/client/driver/chaos_commands.h b/yt/yt/client/driver/chaos_commands.h index 831991ec08..ac50bcf643 100644 --- a/yt/yt/client/driver/chaos_commands.h +++ b/yt/yt/client/driver/chaos_commands.h @@ -4,7 +4,7 @@ #include <yt/yt/client/chaos_client/replication_card.h> -#include <yt/yt/core/ytree/yson_serializable.h> +#include <yt/yt/core/ytree/yson_struct.h> namespace NYT::NDriver { diff --git a/yt/yt/client/driver/queue_commands.cpp b/yt/yt/client/driver/queue_commands.cpp index e2aaf35e7c..2d82e0d22c 100644 --- a/yt/yt/client/driver/queue_commands.cpp +++ b/yt/yt/client/driver/queue_commands.cpp @@ -233,7 +233,8 @@ void TAdvanceConsumerCommand::DoExecute(ICommandContextPtr context) { auto transaction = GetTransaction(context); - transaction->AdvanceConsumer(ConsumerPath, QueuePath, PartitionIndex, OldOffset, NewOffset); + WaitFor(transaction->AdvanceConsumer(ConsumerPath, QueuePath, PartitionIndex, OldOffset, NewOffset, /*options*/ {})) + .ThrowOnError(); if (ShouldCommitTransaction()) { WaitFor(transaction->Commit()) diff --git a/yt/yt/client/federated/client.cpp b/yt/yt/client/federated/client.cpp index f2606da4d6..3e2bf635ad 100644 --- a/yt/yt/client/federated/client.cpp +++ b/yt/yt/client/federated/client.cpp @@ -77,6 +77,14 @@ public: TSharedRange<TRowModification> modifications, const TModifyRowsOptions& options) override; + TFuture<void> AdvanceConsumer( + const NYPath::TRichYPath& consumerPath, + const NYPath::TRichYPath& queuePath, + int partitionIndex, + std::optional<i64> oldOffset, + i64 newOffset, + const TAdvanceConsumerOptions& options) override; + TFuture<TTransactionFlushResult> Flush() override; TFuture<void> Ping(const NApi::TTransactionPingOptions& options = {}) override; @@ -478,6 +486,7 @@ TRANSACTION_METHOD_IMPL(void, Abort, (const TTransactionAbortOptions&)); TRANSACTION_METHOD_IMPL(TVersionedLookupRowsResult, VersionedLookupRows, (const NYPath::TYPath&, NTableClient::TNameTablePtr, const TSharedRange<NTableClient::TUnversionedRow>&, const TVersionedLookupRowsOptions&)); TRANSACTION_METHOD_IMPL(std::vector<TUnversionedLookupRowsResult>, MultiLookup, (const std::vector<TMultiLookupSubrequest>&, const TMultiLookupOptions&)); TRANSACTION_METHOD_IMPL(TPullRowsResult, PullRows, (const NYPath::TYPath&, const TPullRowsOptions&)); +TRANSACTION_METHOD_IMPL(void, AdvanceConsumer, (const NYPath::TRichYPath&, const NYPath::TRichYPath&, int, std::optional<i64>, i64, const TAdvanceConsumerOptions&)); TRANSACTION_METHOD_IMPL(NYson::TYsonString, ExplainQuery, (const TString&, const TExplainQueryOptions&)); TRANSACTION_METHOD_IMPL(NYson::TYsonString, GetNode, (const NYPath::TYPath&, const TGetNodeOptions&)); TRANSACTION_METHOD_IMPL(NYson::TYsonString, ListNode, (const NYPath::TYPath&, const TListNodeOptions&)); diff --git a/yt/yt/client/journal_client/config.h b/yt/yt/client/journal_client/config.h index b89a248d2b..23e2d7150c 100644 --- a/yt/yt/client/journal_client/config.h +++ b/yt/yt/client/journal_client/config.h @@ -2,10 +2,10 @@ #include "public.h" -#include <yt/yt/core/ytree/yson_serializable.h> - #include <yt/yt/client/chunk_client/config.h> +#include <yt/yt/core/ytree/yson_struct.h> + namespace NYT::NJournalClient { //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/client/queue_client/consumer_client.cpp b/yt/yt/client/queue_client/consumer_client.cpp index 2089ba21aa..8690cb6433 100644 --- a/yt/yt/client/queue_client/consumer_client.cpp +++ b/yt/yt/client/queue_client/consumer_client.cpp @@ -575,6 +575,11 @@ ISubConsumerClientPtr CreateSubConsumerClient( return CreateConsumerClient(client, consumerPath)->GetSubConsumerClient(queueRef); } +const TTableSchemaPtr& GetConsumerSchema() +{ + return YTConsumerTableSchema; +} + //////////////////////////////////////////////////////////////////////////////// } // namespace NYT::NQueueClient diff --git a/yt/yt/client/queue_client/consumer_client.h b/yt/yt/client/queue_client/consumer_client.h index 5c239828fa..80bfce0435 100644 --- a/yt/yt/client/queue_client/consumer_client.h +++ b/yt/yt/client/queue_client/consumer_client.h @@ -118,6 +118,8 @@ ISubConsumerClientPtr CreateSubConsumerClient( const NYPath::TYPath& consumerPath, NYPath::TRichYPath queuePath); +const NTableClient::TTableSchemaPtr& GetConsumerSchema(); + //////////////////////////////////////////////////////////////////////////////// } // namespace NYT::NQueueClient diff --git a/yt/yt/client/table_client/helpers.cpp b/yt/yt/client/table_client/helpers.cpp index 66343bc32b..60767d1a68 100644 --- a/yt/yt/client/table_client/helpers.cpp +++ b/yt/yt/client/table_client/helpers.cpp @@ -5,6 +5,7 @@ #include <yt/yt_proto/yt/client/table_chunk_format/proto/chunk_meta.pb.h> +#include <yt/yt/core/ytree/attributes.h> #include <yt/yt/core/ytree/convert.h> #include <yt/yt/core/net/address.h> diff --git a/yt/yt/client/table_client/unversioned_row.cpp b/yt/yt/client/table_client/unversioned_row.cpp index 7a28b1333f..cff031f054 100644 --- a/yt/yt/client/table_client/unversioned_row.cpp +++ b/yt/yt/client/table_client/unversioned_row.cpp @@ -16,7 +16,7 @@ #include <yt/yt/core/yson/consumer.h> -#include <yt/yt/core/ytree/helpers.h> +#include <yt/yt/core/ytree/attributes.h> #include <yt/yt/core/ytree/node.h> #include <yt/yt/core/ytree/convert.h> diff --git a/yt/yt/client/transaction_client/config.h b/yt/yt/client/transaction_client/config.h index c4114b72c5..9c8c69d6a6 100644 --- a/yt/yt/client/transaction_client/config.h +++ b/yt/yt/client/transaction_client/config.h @@ -4,7 +4,7 @@ #include <yt/yt/core/rpc/config.h> -#include <yt/yt/core/ytree/yson_serializable.h> +#include <yt/yt/core/ytree/yson_struct.h> namespace NYT::NTransactionClient { diff --git a/yt/yt/client/unittests/mock/transaction.h b/yt/yt/client/unittests/mock/transaction.h index 06c9c65148..d8380bdd61 100644 --- a/yt/yt/client/unittests/mock/transaction.h +++ b/yt/yt/client/unittests/mock/transaction.h @@ -195,6 +195,14 @@ public: MOCK_METHOD(void, SubscribeAborted, (const TAbortedHandler& callback), (override)); MOCK_METHOD(void, UnsubscribeAborted, (const TAbortedHandler& callback), (override)); + MOCK_METHOD(TFuture<void>, AdvanceConsumer, ( + const NYPath::TRichYPath& consumerPath, + const NYPath::TRichYPath& queuePath, + int partitionIndex, + std::optional<i64> oldOffset, + i64 newOffset, + const TAdvanceConsumerOptions& options), (override)); + MOCK_METHOD(void, ModifyRows, ( const NYPath::TYPath& path, NTableClient::TNameTablePtr nameTable, diff --git a/yt/yt/core/actions/unittests/future_ut.cpp b/yt/yt/core/actions/unittests/future_ut.cpp index a979e8477a..9b564e7481 100644 --- a/yt/yt/core/actions/unittests/future_ut.cpp +++ b/yt/yt/core/actions/unittests/future_ut.cpp @@ -7,6 +7,8 @@ #include <yt/yt/core/misc/ref_counted_tracker.h> #include <yt/yt/core/misc/mpsc_stack.h> +#include <yt/yt/core/ytree/attributes.h> + #include <util/system/thread.h> #include <thread> diff --git a/yt/yt/core/actions/unittests/new_with_offloaded_dtor_ut.cpp b/yt/yt/core/actions/unittests/new_with_offloaded_dtor_ut.cpp index 5c9a9ffab9..be7482808a 100644 --- a/yt/yt/core/actions/unittests/new_with_offloaded_dtor_ut.cpp +++ b/yt/yt/core/actions/unittests/new_with_offloaded_dtor_ut.cpp @@ -7,6 +7,8 @@ #include <yt/yt/core/misc/ref_counted_tracker.h> #include <yt/yt/core/misc/proc.h> +#include <yt/yt/core/logging/log.h> + namespace NYT { namespace { diff --git a/yt/yt/core/compression/unittests/dictionary_compression_ut.cpp b/yt/yt/core/compression/unittests/dictionary_compression_ut.cpp index c96d1bc40c..5eb10dee42 100644 --- a/yt/yt/core/compression/unittests/dictionary_compression_ut.cpp +++ b/yt/yt/core/compression/unittests/dictionary_compression_ut.cpp @@ -6,6 +6,8 @@ #include <yt/yt/core/misc/error.h> #include <yt/yt/core/misc/serialize.h> +#include <yt/yt/core/ytree/attributes.h> + #include <library/cpp/yt/memory/chunked_memory_pool.h> #include <util/random/fast.h> diff --git a/yt/yt/core/concurrency/thread_pool_poller.cpp b/yt/yt/core/concurrency/thread_pool_poller.cpp index 0b45173871..f486003cc1 100644 --- a/yt/yt/core/concurrency/thread_pool_poller.cpp +++ b/yt/yt/core/concurrency/thread_pool_poller.cpp @@ -7,6 +7,7 @@ #include "two_level_fair_share_thread_pool.h" #include "new_fair_share_thread_pool.h" +#include <yt/yt/core/misc/collection_helpers.h> #include <yt/yt/core/misc/proc.h> #include <yt/yt/core/misc/mpsc_stack.h> #include <yt/yt/core/misc/ref_tracked.h> diff --git a/yt/yt/core/http/server.cpp b/yt/yt/core/http/server.cpp index b2c95d1dbd..132e0b1fbd 100644 --- a/yt/yt/core/http/server.cpp +++ b/yt/yt/core/http/server.cpp @@ -262,6 +262,11 @@ private: } })); + auto finally = Finally([&] { + auto count = ActiveConnections_.fetch_sub(1) - 1; + ConnectionsActive_.Update(count); + }); + if (Config_->NoDelay) { connection->SetNoDelay(); } @@ -274,11 +279,6 @@ private: void DoHandleConnection(const IConnectionPtr& connection, TGuid connectionId) { - auto finally = Finally([&] { - auto count = ActiveConnections_.fetch_sub(1) - 1; - ConnectionsActive_.Update(count); - }); - auto request = New<THttpInput>( connection, connection->RemoteAddress(), diff --git a/yt/yt/core/logging/log_manager.cpp b/yt/yt/core/logging/log_manager.cpp index 62dba0253d..b95beb20fb 100644 --- a/yt/yt/core/logging/log_manager.cpp +++ b/yt/yt/core/logging/log_manager.cpp @@ -16,6 +16,7 @@ #include <yt/yt/core/concurrency/invoker_queue.h> #include <yt/yt/core/concurrency/thread_pool.h> +#include <yt/yt/core/misc/collection_helpers.h> #include <yt/yt/core/misc/fs.h> #include <yt/yt/core/misc/spsc_queue.h> #include <yt/yt/core/misc/mpsc_stack.h> @@ -32,7 +33,7 @@ #include <yt/yt/core/ytree/ypath_client.h> #include <yt/yt/core/ytree/ypath_service.h> -#include <yt/yt/core/ytree/yson_serializable.h> +#include <yt/yt/core/ytree/yson_struct.h> #include <yt/yt/core/ytree/convert.h> #include <yt/yt/library/profiling/producer.h> diff --git a/yt/yt/core/misc/cache_config.h b/yt/yt/core/misc/cache_config.h index 9ac7c9063c..24e6b2c946 100644 --- a/yt/yt/core/misc/cache_config.h +++ b/yt/yt/core/misc/cache_config.h @@ -2,7 +2,6 @@ #include "public.h" -#include <yt/yt/core/ytree/yson_serializable.h> #include <yt/yt/core/ytree/yson_struct.h> namespace NYT { diff --git a/yt/yt/core/misc/error.cpp b/yt/yt/core/misc/error.cpp index ce9a942841..511a9fb26c 100644 --- a/yt/yt/core/misc/error.cpp +++ b/yt/yt/core/misc/error.cpp @@ -16,6 +16,7 @@ #include <yt/yt/core/yson/tokenizer.h> +#include <yt/yt/core/ytree/attributes.h> #include <yt/yt/core/ytree/convert.h> #include <yt/yt/core/ytree/fluent.h> diff --git a/yt/yt/core/misc/error.h b/yt/yt/core/misc/error.h index be3a5d9ec7..9dba023408 100644 --- a/yt/yt/core/misc/error.h +++ b/yt/yt/core/misc/error.h @@ -6,7 +6,7 @@ #include <yt/yt/core/yson/string.h> -#include <yt/yt/core/ytree/attributes.h> +#include <yt/yt/core/ytree/public.h> #include <yt/yt/core/tracing/public.h> diff --git a/yt/yt/core/misc/proc.cpp b/yt/yt/core/misc/proc.cpp index 1b7aefe466..2fbd8ba1d6 100644 --- a/yt/yt/core/misc/proc.cpp +++ b/yt/yt/core/misc/proc.cpp @@ -314,6 +314,24 @@ std::vector<size_t> GetCurrentProcessThreadIds() #endif } +bool IsUserspaceThread(size_t tid) +{ +#ifdef __linux__ + TFileInput file(Format("/proc/%v/stat", tid)); + auto statFields = SplitString(file.ReadLine(), " "); + constexpr int StartStackIndex = 27; + if (statFields.size() < StartStackIndex) { + return false; + } + // This is just a heuristic. + auto startStack = FromString<ui64>(statFields[StartStackIndex]); + return startStack != 0; +#else + Y_UNUSED(tid); + return false; +#endif +} + void ChownChmodDirectory(const TString& path, const std::optional<uid_t>& userId, const std::optional<int>& permissions) { #ifdef _unix_ diff --git a/yt/yt/core/misc/proc.h b/yt/yt/core/misc/proc.h index 2d52d0d209..823b64397e 100644 --- a/yt/yt/core/misc/proc.h +++ b/yt/yt/core/misc/proc.h @@ -4,7 +4,7 @@ #include <yt/yt/core/misc/error.h> -#include <yt/yt/core/ytree/yson_serializable.h> +#include <util/system/file.h> #include <errno.h> @@ -110,6 +110,7 @@ ui64 GetProcessCumulativeMajorPageFaults(int pid = -1); size_t GetCurrentProcessId(); size_t GetCurrentThreadId(); std::vector<size_t> GetCurrentProcessThreadIds(); +bool IsUserspaceThread(size_t tid); void ChownChmodDirectory( const TString& path, diff --git a/yt/yt/core/misc/shutdown.cpp b/yt/yt/core/misc/shutdown.cpp index 3646df7150..25a772a2d6 100644 --- a/yt/yt/core/misc/shutdown.cpp +++ b/yt/yt/core/misc/shutdown.cpp @@ -4,6 +4,8 @@ #include <yt/yt/core/misc/proc.h> #include <yt/yt/core/misc/singleton.h> +#include <library/cpp/yt/cpu_clock/clock.h> + #include <library/cpp/yt/threading/fork_aware_spin_lock.h> #include <library/cpp/yt/threading/event_count.h> diff --git a/yt/yt/core/misc/unittests/error_ut.cpp b/yt/yt/core/misc/unittests/error_ut.cpp index f8dad10b3c..f9cbbe9e9e 100644 --- a/yt/yt/core/misc/unittests/error_ut.cpp +++ b/yt/yt/core/misc/unittests/error_ut.cpp @@ -5,6 +5,7 @@ #include <yt/yt/core/yson/string.h> +#include <yt/yt/core/ytree/attributes.h> #include <yt/yt/core/ytree/convert.h> #include <util/stream/str.h> diff --git a/yt/yt/core/net/connection.cpp b/yt/yt/core/net/connection.cpp index 38427b05b9..d0018dbbe8 100644 --- a/yt/yt/core/net/connection.cpp +++ b/yt/yt/core/net/connection.cpp @@ -33,7 +33,7 @@ namespace NYT::NNet { using namespace NConcurrency; -using namespace NProfiling; +// using namespace NProfiling; #ifdef _unix_ using TIOVecBasePtr = void*; diff --git a/yt/yt/core/rpc/config.cpp b/yt/yt/core/rpc/config.cpp index 14143a6585..ec29c3b88c 100644 --- a/yt/yt/core/rpc/config.cpp +++ b/yt/yt/core/rpc/config.cpp @@ -38,6 +38,20 @@ void TServiceCommonConfig::Register(TRegistrar registrar) //////////////////////////////////////////////////////////////////////////////// +void TServiceCommonDynamicConfig::Register(TRegistrar registrar) +{ + registrar.Parameter("enable_per_user_profiling", &TThis::EnablePerUserProfiling) + .Default(); + registrar.Parameter("histogram_timer_profiling", &TThis::HistogramTimerProfiling) + .Default(); + registrar.Parameter("code_counting", &TThis::EnableErrorCodeCounting) + .Default(); + registrar.Parameter("tracing_mode", &TThis::TracingMode) + .Default(); +} + +//////////////////////////////////////////////////////////////////////////////// + void TServerConfig::Register(TRegistrar registrar) { registrar.Parameter("services", &TThis::Services) @@ -46,6 +60,14 @@ void TServerConfig::Register(TRegistrar registrar) //////////////////////////////////////////////////////////////////////////////// +void TServerDynamicConfig::Register(TRegistrar registrar) +{ + registrar.Parameter("services", &TThis::Services) + .Default(); +} + +//////////////////////////////////////////////////////////////////////////////// + void TServiceConfig::Register(TRegistrar registrar) { registrar.Parameter("enable_per_user_profiling", &TThis::EnablePerUserProfiling) diff --git a/yt/yt/core/rpc/config.h b/yt/yt/core/rpc/config.h index be8c1fdec5..9cdf971d18 100644 --- a/yt/yt/core/rpc/config.h +++ b/yt/yt/core/rpc/config.h @@ -90,6 +90,40 @@ DEFINE_REFCOUNTED_TYPE(TServerConfig) //////////////////////////////////////////////////////////////////////////////// +// Common options shared between all services in one server. +class TServiceCommonDynamicConfig + : public NYTree::TYsonStruct +{ +public: + std::optional<bool> EnablePerUserProfiling; + std::optional<THistogramConfigPtr> HistogramTimerProfiling; + std::optional<bool> EnableErrorCodeCounting; + std::optional<ERequestTracingMode> TracingMode; + + REGISTER_YSON_STRUCT(TServiceCommonDynamicConfig); + + static void Register(TRegistrar registrar); +}; + +DEFINE_REFCOUNTED_TYPE(TServiceCommonDynamicConfig) + +//////////////////////////////////////////////////////////////////////////////// + +class TServerDynamicConfig + : public TServiceCommonDynamicConfig +{ +public: + THashMap<TString, NYTree::INodePtr> Services; + + REGISTER_YSON_STRUCT(TServerDynamicConfig); + + static void Register(TRegistrar registrar); +}; + +DEFINE_REFCOUNTED_TYPE(TServerDynamicConfig) + +//////////////////////////////////////////////////////////////////////////////// + class TServiceConfig : public NYTree::TYsonStruct { diff --git a/yt/yt/core/rpc/public.h b/yt/yt/core/rpc/public.h index af88870ad9..5548fa0f22 100644 --- a/yt/yt/core/rpc/public.h +++ b/yt/yt/core/rpc/public.h @@ -107,6 +107,8 @@ DECLARE_REFCOUNTED_CLASS(THistogramExponentialBounds) DECLARE_REFCOUNTED_CLASS(THistogramConfig) DECLARE_REFCOUNTED_CLASS(TServerConfig) DECLARE_REFCOUNTED_CLASS(TServiceCommonConfig) +DECLARE_REFCOUNTED_CLASS(TServerDynamicConfig) +DECLARE_REFCOUNTED_CLASS(TServiceCommonDynamicConfig) DECLARE_REFCOUNTED_CLASS(TServiceConfig) DECLARE_REFCOUNTED_CLASS(TMethodConfig) DECLARE_REFCOUNTED_CLASS(TRetryingChannelConfig) diff --git a/yt/yt/core/rpc/retrying_channel.h b/yt/yt/core/rpc/retrying_channel.h index d8d25fb922..7de5f5f8ec 100644 --- a/yt/yt/core/rpc/retrying_channel.h +++ b/yt/yt/core/rpc/retrying_channel.h @@ -2,8 +2,6 @@ #include "public.h" -#include <yt/yt/core/ytree/yson_serializable.h> - namespace NYT::NRpc { //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/core/rpc/server.h b/yt/yt/core/rpc/server.h index fe4c88a478..e48bf2ef5c 100644 --- a/yt/yt/core/rpc/server.h +++ b/yt/yt/core/rpc/server.h @@ -31,7 +31,9 @@ struct IServer virtual IServicePtr GetServiceOrThrow(const TServiceId& serviceId) const = 0; //! Reconfigures the server on-the-fly. - virtual void Configure(TServerConfigPtr config) = 0; + virtual void Configure(const TServerConfigPtr& config) = 0; + + virtual void OnDynamicConfigChanged(const TServerDynamicConfigPtr& config) = 0; //! Starts the server. /*! diff --git a/yt/yt/core/rpc/server_detail.cpp b/yt/yt/core/rpc/server_detail.cpp index 0f80a07b6b..82ba43afe4 100644 --- a/yt/yt/core/rpc/server_detail.cpp +++ b/yt/yt/core/rpc/server_detail.cpp @@ -768,12 +768,12 @@ void TServerBase::RegisterService(IServicePtr service) auto guard = WriterGuard(ServicesLock_); auto& serviceMap = RealmIdToServiceMap_[serviceId.RealmId]; YT_VERIFY(serviceMap.emplace(serviceId.ServiceName, service).second); - if (Config_) { - auto it = Config_->Services.find(serviceId.ServiceName); - if (it != Config_->Services.end()) { - service->Configure(Config_, it->second); + if (AppliedConfig_) { + auto it = AppliedConfig_->Services.find(serviceId.ServiceName); + if (it != AppliedConfig_->Services.end()) { + service->Configure(AppliedConfig_, it->second); } else { - service->Configure(Config_, nullptr); + service->Configure(AppliedConfig_, nullptr); } } DoRegisterService(service); @@ -866,26 +866,54 @@ IServicePtr TServerBase::GetServiceOrThrow(const TServiceId& serviceId) const return serviceIt->second; } -void TServerBase::Configure(TServerConfigPtr config) +void TServerBase::ApplyConfig() { - auto guard = WriterGuard(ServicesLock_); + VERIFY_SPINLOCK_AFFINITY(ServicesLock_); - // Future services will be configured appropriately. - Config_ = config; + auto newAppliedConfig = New<TServerConfig>(); + newAppliedConfig->EnableErrorCodeCounting = DynamicConfig_->EnableErrorCodeCounting.value_or(StaticConfig_->EnableErrorCodeCounting); + newAppliedConfig->EnablePerUserProfiling = DynamicConfig_->EnablePerUserProfiling.value_or(StaticConfig_->EnablePerUserProfiling); + newAppliedConfig->HistogramTimerProfiling = DynamicConfig_->HistogramTimerProfiling.value_or(StaticConfig_->HistogramTimerProfiling); + newAppliedConfig->Services = StaticConfig_->Services; + + for (const auto& [name, node] : DynamicConfig_->Services) { + newAppliedConfig->Services[name] = node; + } + + AppliedConfig_ = newAppliedConfig; // Apply configuration to all existing services. for (const auto& [realmId, serviceMap] : RealmIdToServiceMap_) { for (const auto& [serviceName, service] : serviceMap) { - auto it = config->Services.find(serviceName); - if (it != config->Services.end()) { - service->Configure(config, it->second); + auto it = AppliedConfig_->Services.find(serviceName); + if (it != AppliedConfig_->Services.end()) { + service->Configure(AppliedConfig_, it->second); } else { - service->Configure(config, nullptr); + service->Configure(AppliedConfig_, nullptr); } } } } +void TServerBase::Configure(const TServerConfigPtr& config) +{ + auto guard = WriterGuard(ServicesLock_); + + // Future services will be configured appropriately. + StaticConfig_ = config; + + ApplyConfig(); +} + +void TServerBase::OnDynamicConfigChanged(const TServerDynamicConfigPtr& config) +{ + auto guard = WriterGuard(ServicesLock_); + + DynamicConfig_ = config; + + ApplyConfig(); +} + void TServerBase::Start() { YT_VERIFY(!Started_); diff --git a/yt/yt/core/rpc/server_detail.h b/yt/yt/core/rpc/server_detail.h index 0a2647b643..c05e19216e 100644 --- a/yt/yt/core/rpc/server_detail.h +++ b/yt/yt/core/rpc/server_detail.h @@ -269,7 +269,8 @@ public: IServicePtr FindService(const TServiceId& serviceId) const override; IServicePtr GetServiceOrThrow(const TServiceId& serviceId) const override; - void Configure(TServerConfigPtr config) override; + void Configure(const TServerConfigPtr& config) override; + void OnDynamicConfigChanged(const TServerDynamicConfigPtr& config) override; void Start() override; TFuture<void> Stop(bool graceful) override; @@ -280,7 +281,9 @@ protected: std::atomic<bool> Started_ = false; YT_DECLARE_SPIN_LOCK(NThreading::TReaderWriterSpinLock, ServicesLock_); - TServerConfigPtr Config_; + TServerConfigPtr StaticConfig_; + TServerDynamicConfigPtr DynamicConfig_ = New<TServerDynamicConfig>(); + TServerConfigPtr AppliedConfig_; //! Service name to service. using TServiceMap = THashMap<TString, IServicePtr>; @@ -288,6 +291,8 @@ protected: explicit TServerBase(NLogging::TLogger logger); + void ApplyConfig(); + virtual void DoStart(); virtual TFuture<void> DoStop(bool graceful); diff --git a/yt/yt/core/rpc/service_detail.cpp b/yt/yt/core/rpc/service_detail.cpp index 2fd6f483ad..46567c3fcd 100644 --- a/yt/yt/core/rpc/service_detail.cpp +++ b/yt/yt/core/rpc/service_detail.cpp @@ -2451,6 +2451,7 @@ void TServiceBase::DoConfigure( auto methodConfig = methodIt ? methodIt->second : New<TMethodConfig>(); const auto& descriptor = runtimeInfo->Descriptor; + runtimeInfo->Heavy.store(methodConfig->Heavy.value_or(descriptor.Options.Heavy)); runtimeInfo->QueueSizeLimit.store(methodConfig->QueueSizeLimit.value_or(descriptor.QueueSizeLimit)); runtimeInfo->ConcurrencyLimit.Reconfigure(methodConfig->ConcurrencyLimit.value_or(descriptor.ConcurrencyLimit)); diff --git a/yt/yt/core/yson/protobuf_interop.cpp b/yt/yt/core/yson/protobuf_interop.cpp index 030cc763fc..60f4ee6e1a 100644 --- a/yt/yt/core/yson/protobuf_interop.cpp +++ b/yt/yt/core/yson/protobuf_interop.cpp @@ -182,7 +182,7 @@ void SetProtobufInteropConfig(TProtobufInteropDynamicConfigPtr config) GlobalProtobufInteropConfig()->Config.Store(std::move(config)); } -void GetSchema(const TProtobufEnumType* enumType, IYsonConsumer* consumer); +void WriteSchema(const TProtobufEnumType* enumType, IYsonConsumer* consumer); //////////////////////////////////////////////////////////////////////////////// @@ -501,17 +501,18 @@ public: return EnumYsonStorageType_; } - void GetSchema(IYsonConsumer* consumer) const + void WriteSchema(IYsonConsumer* consumer) const { if (IsYsonMap()) { - BuildYsonFluently(consumer).BeginMap() - .Item("type_name").Value("dict") - .Item("key").Do([this] (auto&& fluent) { - GetYsonMapKeyField()->GetSchema(fluent.GetConsumer()); - }) - .Item("value").Do([this] (auto&& fluent) { - GetYsonMapValueField()->GetSchema(fluent.GetConsumer()); - }) + BuildYsonFluently(consumer) + .BeginMap() + .Item("type_name").Value("dict") + .Item("key").Do([&] (auto fluent) { + GetYsonMapKeyField()->WriteSchema(fluent.GetConsumer()); + }) + .Item("value").Do([&] (auto fluent) { + GetYsonMapValueField()->WriteSchema(fluent.GetConsumer()); + }) .EndMap(); return; @@ -558,10 +559,10 @@ public: consumer->OnStringScalar("string"); break; case FieldDescriptor::TYPE_ENUM: - NYson::GetSchema(GetEnumType(), consumer); + NYson::WriteSchema(GetEnumType(), consumer); break; case FieldDescriptor::TYPE_MESSAGE: - NYson::GetSchema(GetMessageType(), consumer); + NYson::WriteSchema(GetMessageType(), consumer); break; default: break; @@ -690,20 +691,21 @@ public: } } - void GetSchema(IYsonConsumer* consumer) const + void WriteSchema(IYsonConsumer* consumer) const { BuildYsonFluently(consumer).BeginMap() .Item("type_name").Value("struct") - .Item("members").DoListFor(0, Underlying_->field_count(), [this] (auto&& fluent, int index) { + .Item("members").DoListFor(0, Underlying_->field_count(), [&] (auto fluent, int index) { auto* field = GetFieldByNumber(Underlying_->field(index)->number()); - fluent.Item().BeginMap() - .Item("name").Value(field->GetYsonName()) - .Item("type").Do([&] (auto&& fluent) { - field->GetSchema(fluent.GetConsumer()); - }) - .DoIf(!field->IsYsonMap() && !field->IsRepeated() && !field->IsOptional(), [&] (auto && fluent) { - fluent.Item("required").Value(true); - }) + fluent.Item() + .BeginMap() + .Item("name").Value(field->GetYsonName()) + .Item("type").Do([&] (auto fluent) { + field->WriteSchema(fluent.GetConsumer()); + }) + .DoIf(!field->IsYsonMap() && !field->IsRepeated() && !field->IsOptional(), [] (auto fluent) { + fluent.Item("required").Value(true); + }) .EndMap(); }) .EndMap(); @@ -821,14 +823,15 @@ public: return it == ValueToLiteral_.end() ? TStringBuf() : it->second; } - void GetSchema(IYsonConsumer* consumer) const + void WriteSchema(IYsonConsumer* consumer) const { - BuildYsonFluently(consumer).BeginMap() - .Item("type_name").Value("enum") - .Item("enum_name").Value(Underlying_->name()) - .Item("values").DoListFor(0, Underlying_->value_count(), [this] (auto&& fluent, int index) { - fluent.Item().Value(FindLiteralByValue(Underlying_->value(index)->number())); - }) + BuildYsonFluently(consumer) + .BeginMap() + .Item("type_name").Value("enum") + .Item("enum_name").Value(Underlying_->name()) + .Item("values").DoListFor(0, Underlying_->value_count(), [&] (auto fluent, int index) { + fluent.Item().Value(FindLiteralByValue(Underlying_->value(index)->number())); + }) .EndMap(); } @@ -3140,14 +3143,14 @@ TString YsonStringToProto( return serializedProto; } -void GetSchema(const TProtobufEnumType* type, IYsonConsumer* consumer) +void WriteSchema(const TProtobufEnumType* type, IYsonConsumer* consumer) { - type->GetSchema(consumer); + type->WriteSchema(consumer); } -void GetSchema(const TProtobufMessageType* type, IYsonConsumer* consumer) +void WriteSchema(const TProtobufMessageType* type, IYsonConsumer* consumer) { - type->GetSchema(consumer); + type->WriteSchema(consumer); } //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/core/yson/protobuf_interop.h b/yt/yt/core/yson/protobuf_interop.h index 5daf062698..5bfe3fd93b 100644 --- a/yt/yt/core/yson/protobuf_interop.h +++ b/yt/yt/core/yson/protobuf_interop.h @@ -280,9 +280,11 @@ void SetProtobufInteropConfig(TProtobufInteropDynamicConfigPtr config); //////////////////////////////////////////////////////////////////////////////// -//! Return type v3 schema for protobuf message type. +//! Returns type v3 schema for protobuf message type. //! Note: Recursive types (message has field with self type) are not supported. -void GetSchema(const TProtobufMessageType* type, IYsonConsumer* consumer); +void WriteSchema(const TProtobufMessageType* type, IYsonConsumer* consumer); + +//////////////////////////////////////////////////////////////////////////////// } // namespace NYT::NYson diff --git a/yt/yt/core/yson/unittests/protobuf_yson_schema_ut.cpp b/yt/yt/core/yson/unittests/protobuf_yson_schema_ut.cpp index 103f00b01d..af58fb320a 100644 --- a/yt/yt/core/yson/unittests/protobuf_yson_schema_ut.cpp +++ b/yt/yt/core/yson/unittests/protobuf_yson_schema_ut.cpp @@ -14,13 +14,14 @@ namespace NYT::NYson { namespace { +//////////////////////////////////////////////////////////////////////////////// TEST(TProtobufYsonSchemaTest, GetMessageSchema) { TStringStream outputStream; TYsonWriter ysonWriter(&outputStream, EYsonFormat::Text); - GetSchema(ReflectProtobufMessageType<NProto::TTestSchemaMessage>(), &ysonWriter); + WriteSchema(ReflectProtobufMessageType<NProto::TTestSchemaMessage>(), &ysonWriter); TStringBuf expected = R"({ type_name="struct"; members=[ @@ -54,5 +55,7 @@ TEST(TProtobufYsonSchemaTest, GetMessageSchema) << "Actual: " << ConvertToYsonString(actualNode, EYsonFormat::Text, 4).AsStringBuf() << "\n\n"; } +//////////////////////////////////////////////////////////////////////////////// + } // namespace } // namespace NYT::NYson diff --git a/yt/yt/core/ytalloc/bindings.cpp b/yt/yt/core/ytalloc/bindings.cpp index 1f47fa7982..33eb077a5b 100644 --- a/yt/yt/core/ytalloc/bindings.cpp +++ b/yt/yt/core/ytalloc/bindings.cpp @@ -9,10 +9,10 @@ #include <yt/yt/core/misc/singleton.h> #include <yt/yt/core/misc/string_builder.h> -#include <yt/yt/core/ytree/yson_serializable.h> - #include <library/cpp/ytalloc/api/ytalloc.h> +#include <library/cpp/yt/yson_string/string.h> + #include <util/system/env.h> #include <cstdio> diff --git a/yt/yt/core/ytree/attributes-inl.h b/yt/yt/core/ytree/attributes-inl.h new file mode 100644 index 0000000000..dd367f5d24 --- /dev/null +++ b/yt/yt/core/ytree/attributes-inl.h @@ -0,0 +1,89 @@ +#ifndef ATTRIBUTES_INL_H_ +#error "Direct inclusion of this file is not allowed, include helpers.h" +// For the sake of sane code completion. +#include "attributes.h" +#endif + +// #include "attribute_consumer.h" +// #include "serialize.h" +#include "convert.h" + +namespace NYT::NYTree { + +//////////////////////////////////////////////////////////////////////////////// + +template <class T> +T IAttributeDictionary::Get(TStringBuf key) const +{ + auto yson = GetYson(key); + try { + return ConvertTo<T>(yson); + } catch (const std::exception& ex) { + THROW_ERROR_EXCEPTION("Error parsing attribute %Qv", + key) + << ex; + } +} + +template <class T> +T IAttributeDictionary::GetAndRemove(const TString& key) +{ + auto result = Get<T>(key); + Remove(key); + return result; +} + +template <class T> +T IAttributeDictionary::Get(TStringBuf key, const T& defaultValue) const +{ + return Find<T>(key).value_or(defaultValue); +} + +template <class T> +T IAttributeDictionary::GetAndRemove(const TString& key, const T& defaultValue) +{ + auto result = Find<T>(key); + if (result) { + Remove(key); + return *result; + } else { + return defaultValue; + } +} + +template <class T> +typename TOptionalTraits<T>::TOptional IAttributeDictionary::Find(TStringBuf key) const +{ + auto yson = FindYson(key); + if (!yson) { + return typename TOptionalTraits<T>::TOptional(); + } + try { + return ConvertTo<T>(yson); + } catch (const std::exception& ex) { + THROW_ERROR_EXCEPTION("Error parsing attribute %Qv", + key) + << ex; + } +} + +template <class T> +typename TOptionalTraits<T>::TOptional IAttributeDictionary::FindAndRemove(const TString& key) +{ + auto result = Find<T>(key); + if (result) { + Remove(key); + } + return result; +} + +template <class T> +void IAttributeDictionary::Set(const TString& key, const T& value) +{ + auto yson = ConvertToYsonString(value, NYson::EYsonFormat::Binary); + SetYson(key, yson); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT::NYTree diff --git a/yt/yt/core/ytree/attributes.h b/yt/yt/core/ytree/attributes.h index bff7c8fd55..c703bfa529 100644 --- a/yt/yt/core/ytree/attributes.h +++ b/yt/yt/core/ytree/attributes.h @@ -100,3 +100,7 @@ DEFINE_REFCOUNTED_TYPE(IAttributeDictionary) } // namespace NYT::NYTree + +#define ATTRIBUTES_INL_H_ +#include "attributes-inl.h" +#undef ATTRIBUTES_INL_H_ diff --git a/yt/yt/core/ytree/convert-inl.h b/yt/yt/core/ytree/convert-inl.h index 0e0e1c925b..f32c92ea0b 100644 --- a/yt/yt/core/ytree/convert-inl.h +++ b/yt/yt/core/ytree/convert-inl.h @@ -4,6 +4,7 @@ #include "convert.h" #endif +#include "attribute_consumer.h" #include "default_building_consumer.h" #include "serialize.h" #include "tree_builder.h" diff --git a/yt/yt/core/ytree/exception_helpers.cpp b/yt/yt/core/ytree/exception_helpers.cpp index 8e27ceb8e0..5260ff91e5 100644 --- a/yt/yt/core/ytree/exception_helpers.cpp +++ b/yt/yt/core/ytree/exception_helpers.cpp @@ -1,4 +1,5 @@ #include "exception_helpers.h" +#include "attributes.h" #include "node.h" #include <yt/yt/core/misc/error.h> diff --git a/yt/yt/core/ytree/fluent.h b/yt/yt/core/ytree/fluent.h index f423fb17f5..6ef40dfc10 100644 --- a/yt/yt/core/ytree/fluent.h +++ b/yt/yt/core/ytree/fluent.h @@ -4,6 +4,7 @@ #include "tree_visitor.h" #include "tree_builder.h" #include "convert.h" +#include "attributes.h" #include "attribute_consumer.h" #include "helpers.h" diff --git a/yt/yt/core/ytree/helpers-inl.h b/yt/yt/core/ytree/helpers-inl.h index ea204ab82b..4f19dc1814 100644 --- a/yt/yt/core/ytree/helpers-inl.h +++ b/yt/yt/core/ytree/helpers-inl.h @@ -12,80 +12,6 @@ namespace NYT::NYTree { //////////////////////////////////////////////////////////////////////////////// -template <class T> -T IAttributeDictionary::Get(TStringBuf key) const -{ - auto yson = GetYson(key); - try { - return ConvertTo<T>(yson); - } catch (const std::exception& ex) { - THROW_ERROR_EXCEPTION("Error parsing attribute %Qv", - key) - << ex; - } -} - -template <class T> -T IAttributeDictionary::GetAndRemove(const TString& key) -{ - auto result = Get<T>(key); - Remove(key); - return result; -} - -template <class T> -T IAttributeDictionary::Get(TStringBuf key, const T& defaultValue) const -{ - return Find<T>(key).value_or(defaultValue); -} - -template <class T> -T IAttributeDictionary::GetAndRemove(const TString& key, const T& defaultValue) -{ - auto result = Find<T>(key); - if (result) { - Remove(key); - return *result; - } else { - return defaultValue; - } -} - -template <class T> -typename TOptionalTraits<T>::TOptional IAttributeDictionary::Find(TStringBuf key) const -{ - auto yson = FindYson(key); - if (!yson) { - return typename TOptionalTraits<T>::TOptional(); - } - try { - return ConvertTo<T>(yson); - } catch (const std::exception& ex) { - THROW_ERROR_EXCEPTION("Error parsing attribute %Qv", - key) - << ex; - } -} - -template <class T> -typename TOptionalTraits<T>::TOptional IAttributeDictionary::FindAndRemove(const TString& key) -{ - auto result = Find<T>(key); - if (result) { - Remove(key); - } - return result; -} - -template <class T> -void IAttributeDictionary::Set(const TString& key, const T& value) -{ - auto yson = ConvertToYsonString(value, NYson::EYsonFormat::Binary); - SetYson(key, yson); -} - -//////////////////////////////////////////////////////////////////////////////// - template <class T, class R> IYPathServicePtr IYPathService::FromMethod( R (std::remove_cv_t<T>::*method) () const, diff --git a/yt/yt/core/ytree/helpers.cpp b/yt/yt/core/ytree/helpers.cpp index cad1e1367f..cd3bb2ce7f 100644 --- a/yt/yt/core/ytree/helpers.cpp +++ b/yt/yt/core/ytree/helpers.cpp @@ -1,4 +1,5 @@ #include "helpers.h" +#include "attributes.h" #include "ypath_client.h" #include <yt/yt/core/misc/error.h> diff --git a/yt/yt/core/ytree/helpers.h b/yt/yt/core/ytree/helpers.h index 489785ea75..3320456f78 100644 --- a/yt/yt/core/ytree/helpers.h +++ b/yt/yt/core/ytree/helpers.h @@ -54,7 +54,7 @@ void ValidateYPathResolutionDepth(const NYPath::TYPath& path, int depth); //! Helps implementing IAttributeDictionary::ListPairs by delegating to //! IAttributeDictionary::ListKeys and IAttributeDictionary::FindYson for those not capable //! of providing a custom efficient implementation. -std::vector<IAttributeDictionary::TKeyValuePair> ListAttributesPairs(const IAttributeDictionary& attributes); +std::vector<std::pair<TString, NYson::TYsonString>> ListAttributesPairs(const IAttributeDictionary& attributes); //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/core/ytree/unittests/yson_schema_ut.cpp b/yt/yt/core/ytree/unittests/yson_schema_ut.cpp index 3a9548eca6..65060e25e3 100644 --- a/yt/yt/core/ytree/unittests/yson_schema_ut.cpp +++ b/yt/yt/core/ytree/unittests/yson_schema_ut.cpp @@ -169,7 +169,7 @@ void CheckSchema(const TYsonStructPtr& ysonStruct, TStringBuf expected) auto* factory = GetEphemeralNodeFactory(); auto builder = CreateBuilderFromFactory(factory); builder->BeginTree(); - ysonStruct->GetSchema(builder.get()); + ysonStruct->WriteSchema(builder.get()); auto actualNode = builder->EndTree(); auto expectedNode = ConvertToNode(TYsonStringBuf(expected), factory); EXPECT_TRUE(AreNodesEqual(expectedNode, actualNode)) diff --git a/yt/yt/core/ytree/yson_schema-inl.h b/yt/yt/core/ytree/yson_schema-inl.h index 0aefa82d02..2b4b0e5a33 100644 --- a/yt/yt/core/ytree/yson_schema-inl.h +++ b/yt/yt/core/ytree/yson_schema-inl.h @@ -46,7 +46,7 @@ concept CIsAssociativeArray = CIsArray<T> && CIsMapping<T>; //////////////////////////////////////////////////////////////////////////////// #define DEFINE_SCHEMA_FOR_SIMPLE_TYPE(type, name) \ -inline void GetSchema(type, NYson::IYsonConsumer* consumer) \ +inline void WriteSchema(type, NYson::IYsonConsumer* consumer) \ { \ BuildYsonFluently(consumer) \ .Value(#name); \ @@ -79,83 +79,88 @@ DEFINE_SCHEMA_FOR_SIMPLE_TYPE(TGuid, guid) #undef DEFINE_SCHEMA_FOR_SIMPLE_TYPE template <CIsEnum T> -void GetSchema(const T&, NYson::IYsonConsumer* consumer) +void WriteSchema(const T&, NYson::IYsonConsumer* consumer) { - BuildYsonFluently(consumer).BeginMap() - .Item("type_name").Value("enum") - .Item("enum_name").Value(TEnumTraits<T>::GetTypeName()) - .Item("values").DoListFor( - TEnumTraits<T>::GetDomainNames(), [] (auto&& fluent, TStringBuf name) { - fluent.Item().Value(EncodeEnumValue(name)); - }) + BuildYsonFluently(consumer) + .BeginMap() + .Item("type_name").Value("enum") + .Item("enum_name").Value(TEnumTraits<T>::GetTypeName()) + .Item("values").DoListFor( + TEnumTraits<T>::GetDomainNames(), [] (auto fluent, TStringBuf name) { + fluent.Item().Value(EncodeEnumValue(name)); + }) .EndMap(); } template <CIsYsonStruct T> -void GetSchema(const NYT::TIntrusivePtr<T>& value, NYson::IYsonConsumer* consumer) +void WriteSchema(const NYT::TIntrusivePtr<T>& value, NYson::IYsonConsumer* consumer) { - BuildYsonFluently(consumer) .BeginMap() - .Item("type_name").Value("optional") - .Item("item").Do([&] (auto&& fluent) { - (value ? value : New<T>())->GetSchema(fluent.GetConsumer()); - }) + BuildYsonFluently(consumer) + .BeginMap() + .Item("type_name").Value("optional") + .Item("item").Do([&] (auto fluent) { + (value ? value : New<T>())->WriteSchema(fluent.GetConsumer()); + }) .EndMap(); } template <CIsYsonStruct T> -void GetSchema(const T& value, NYson::IYsonConsumer* consumer) +void WriteSchema(const T& value, NYson::IYsonConsumer* consumer) { - return value.GetSchema(consumer); + return value.WriteSchema(consumer); } template <CIsProtobufMessage T> -void GetSchema(const T&, NYson::IYsonConsumer* consumer) +void WriteSchema(const T&, NYson::IYsonConsumer* consumer) { - return NYson::GetSchema(NYson::ReflectProtobufMessageType<T>(), consumer); + return NYson::WriteSchema(NYson::ReflectProtobufMessageType<T>(), consumer); } template <CIsArray T> -void GetSchema(const T& value, NYson::IYsonConsumer* consumer) +void WriteSchema(const T& value, NYson::IYsonConsumer* consumer) { - BuildYsonFluently(consumer).BeginMap() - .Item("type_name").Value("list") - .Item("item").Do([&] (auto&& fluent) { - GetSchema( - std::begin(value) != std::end(value) - ? *std::begin(value) - : std::decay_t<decltype(*std::begin(value))>{}, - fluent.GetConsumer()); - }) + BuildYsonFluently(consumer) + .BeginMap() + .Item("type_name").Value("list") + .Item("item").Do([&] (auto fluent) { + WriteSchema( + std::begin(value) != std::end(value) + ? *std::begin(value) + : std::decay_t<decltype(*std::begin(value))>{}, + fluent.GetConsumer()); + }) .EndMap(); } template <CIsAssociativeArray T> -void GetSchema(const T& value, NYson::IYsonConsumer* consumer) +void WriteSchema(const T& value, NYson::IYsonConsumer* consumer) { - BuildYsonFluently(consumer).BeginMap() - .Item("type_name").Value("dict") - .Item("key").Do([&] (auto&& fluent) { - GetSchema(value.empty() ? typename T::key_type{} : value.begin()->first, fluent.GetConsumer()); - }) - .Item("value").Do([&] (auto&& fluent) { - GetSchema(value.empty() ? typename T::mapped_type{} : value.begin()->second, fluent.GetConsumer()); - }) + BuildYsonFluently(consumer) + .BeginMap() + .Item("type_name").Value("dict") + .Item("key").Do([&] (auto fluent) { + WriteSchema(value.empty() ? typename T::key_type{} : value.begin()->first, fluent.GetConsumer()); + }) + .Item("value").Do([&] (auto fluent) { + WriteSchema(value.empty() ? typename T::mapped_type{} : value.begin()->second, fluent.GetConsumer()); + }) .EndMap(); } template <CIsNullable T> -void GetSchema(const T& value, NYson::IYsonConsumer* consumer) +void WriteSchema(const T& value, NYson::IYsonConsumer* consumer) { - BuildYsonFluently(consumer) .BeginMap() - .Item("type_name").Value("optional") - .Item("item").Do([&] (auto&& fluent) { - GetSchema(value ? *value : std::decay_t<decltype(*value)>{}, fluent.GetConsumer()); - }) + BuildYsonFluently(consumer) + .BeginMap() + .Item("type_name").Value("optional") + .Item("item").Do([&] (auto fluent) { + WriteSchema(value ? *value : std::decay_t<decltype(*value)>{}, fluent.GetConsumer()); + }) .EndMap(); } template <class T> -void GetSchema(const T& value, NYson::IYsonConsumer* consumer) +void WriteSchema(const T& value, NYson::IYsonConsumer* consumer) { auto node = ConvertToNode(value); BuildYsonFluently(consumer).Value(FormatEnum(node->GetType())); diff --git a/yt/yt/core/ytree/yson_schema.h b/yt/yt/core/ytree/yson_schema.h index 2b1f3a38e6..a2e9f02b26 100644 --- a/yt/yt/core/ytree/yson_schema.h +++ b/yt/yt/core/ytree/yson_schema.h @@ -7,7 +7,7 @@ namespace NYT::NYTree::NPrivate { //////////////////////////////////////////////////////////////////////////////// template <typename T> -void GetSchema(const T& value, NYson::IYsonConsumer* consumer); +void WriteSchema(const T& value, NYson::IYsonConsumer* consumer); //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/core/ytree/yson_struct.cpp b/yt/yt/core/ytree/yson_struct.cpp index 881414d40b..09b98aebf3 100644 --- a/yt/yt/core/ytree/yson_struct.cpp +++ b/yt/yt/core/ytree/yson_struct.cpp @@ -135,9 +135,9 @@ std::vector<TString> TYsonStructBase::GetAllParameterAliases(const TString& key) return result; } -void TYsonStructBase::GetSchema(IYsonConsumer* consumer) const +void TYsonStructBase::WriteSchema(IYsonConsumer* consumer) const { - return Meta_->GetSchema(this, consumer); + return Meta_->WriteSchema(this, consumer); } //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/core/ytree/yson_struct.h b/yt/yt/core/ytree/yson_struct.h index 18013c71fe..15acd2fa15 100644 --- a/yt/yt/core/ytree/yson_struct.h +++ b/yt/yt/core/ytree/yson_struct.h @@ -93,7 +93,7 @@ public: std::vector<TString> GetAllParameterAliases(const TString& key) const; - void GetSchema(NYson::IYsonConsumer* consumer) const; + void WriteSchema(NYson::IYsonConsumer* consumer) const; private: template <class TValue> diff --git a/yt/yt/core/ytree/yson_struct_detail-inl.h b/yt/yt/core/ytree/yson_struct_detail-inl.h index d6f507d37d..ac714065af 100644 --- a/yt/yt/core/ytree/yson_struct_detail-inl.h +++ b/yt/yt/core/ytree/yson_struct_detail-inl.h @@ -869,10 +869,10 @@ IMapNodePtr TYsonStructParameter<TValue>::GetRecursiveUnrecognized(const TYsonSt } template <class TValue> -void TYsonStructParameter<TValue>::GetSchema(const TYsonStructBase* self, NYson::IYsonConsumer* consumer) const +void TYsonStructParameter<TValue>::WriteSchema(const TYsonStructBase* self, NYson::IYsonConsumer* consumer) const { // TODO(bulatman) What about constraints: minimum, maximum, default and etc? - NPrivate::GetSchema(FieldAccessor_->GetValue(self), consumer); + NPrivate::WriteSchema(FieldAccessor_->GetValue(self), consumer); } //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/core/ytree/yson_struct_detail.cpp b/yt/yt/core/ytree/yson_struct_detail.cpp index e730ce9b09..c396e4d621 100644 --- a/yt/yt/core/ytree/yson_struct_detail.cpp +++ b/yt/yt/core/ytree/yson_struct_detail.cpp @@ -315,20 +315,21 @@ void TYsonStructMeta::SetUnrecognizedStrategy(EUnrecognizedStrategy strategy) MetaUnrecognizedStrategy_ = strategy; } -void TYsonStructMeta::GetSchema(const TYsonStructBase* target, NYson::IYsonConsumer* consumer) const +void TYsonStructMeta::WriteSchema(const TYsonStructBase* target, NYson::IYsonConsumer* consumer) const { BuildYsonFluently(consumer) .BeginMap() .Item("type_name").Value("struct") - .Item("members").DoListFor(Parameters_, [&] (auto&& fluent, auto&& pair) { - fluent.Item().BeginMap() - .Item("name").Value(pair.first) - .Item("type").Do([&] (auto&& fluent) { - pair.second->GetSchema(target, fluent.GetConsumer()); - }) - .DoIf(pair.second->IsRequired(), [] (auto&& fluent) { - fluent.Item("required").Value(true); - }) + .Item("members").DoListFor(Parameters_, [&] (auto fluent, const auto& pair) { + fluent.Item() + .BeginMap() + .Item("name").Value(pair.first) + .Item("type").Do([&] (auto fluent) { + pair.second->WriteSchema(target, fluent.GetConsumer()); + }) + .DoIf(pair.second->IsRequired(), [] (auto fluent) { + fluent.Item("required").Value(true); + }) .EndMap(); }) .EndMap(); diff --git a/yt/yt/core/ytree/yson_struct_detail.h b/yt/yt/core/ytree/yson_struct_detail.h index 27b9a6ba18..5a3effd704 100644 --- a/yt/yt/core/ytree/yson_struct_detail.h +++ b/yt/yt/core/ytree/yson_struct_detail.h @@ -61,7 +61,7 @@ struct IYsonStructParameter virtual const std::vector<TString>& GetAliases() const = 0; virtual IMapNodePtr GetRecursiveUnrecognized(const TYsonStructBase* self) const = 0; - virtual void GetSchema(const TYsonStructBase* self, NYson::IYsonConsumer* consumer) const = 0; + virtual void WriteSchema(const TYsonStructBase* self, NYson::IYsonConsumer* consumer) const = 0; }; //using IYsonStructParameterPtr = TIntrusivePtr<IYsonStructParameter>; @@ -101,7 +101,7 @@ struct IYsonStructMeta virtual void RegisterPostprocessor(std::function<void(TYsonStructBase*)> postprocessor) = 0; virtual void SetUnrecognizedStrategy(EUnrecognizedStrategy strategy) = 0; - virtual void GetSchema(const TYsonStructBase* target, NYson::IYsonConsumer* consumer) const = 0; + virtual void WriteSchema(const TYsonStructBase* target, NYson::IYsonConsumer* consumer) const = 0; virtual ~IYsonStructMeta() = default; }; @@ -144,7 +144,7 @@ public: void RegisterPostprocessor(std::function<void(TYsonStructBase*)> postprocessor) override; void SetUnrecognizedStrategy(EUnrecognizedStrategy strategy) override; - void GetSchema(const TYsonStructBase* target, NYson::IYsonConsumer* consumer) const override; + void WriteSchema(const TYsonStructBase* target, NYson::IYsonConsumer* consumer) const override; void FinishInitialization(const std::type_info& structType); @@ -250,7 +250,7 @@ public: const std::vector<TString>& GetAliases() const override; IMapNodePtr GetRecursiveUnrecognized(const TYsonStructBase* self) const override; - void GetSchema(const TYsonStructBase* self, NYson::IYsonConsumer* consumer) const override; + void WriteSchema(const TYsonStructBase* self, NYson::IYsonConsumer* consumer) const override; // Mark as optional. Field will be default-initialized if `init` is true, initialization is skipped otherwise. TYsonStructParameter& Optional(bool init = true); diff --git a/yt/yt/library/backtrace_introspector/introspect.cpp b/yt/yt/library/backtrace_introspector/introspect.cpp index 592c232f0f..a555a8a548 100644 --- a/yt/yt/library/backtrace_introspector/introspect.cpp +++ b/yt/yt/library/backtrace_introspector/introspect.cpp @@ -2,6 +2,7 @@ #include "private.h" +#include <yt/yt/core/misc/collection_helpers.h> #include <yt/yt/core/misc/finally.h> #include <yt/yt/core/misc/proc.h> diff --git a/yt/yt/library/backtrace_introspector/introspect_linux.cpp b/yt/yt/library/backtrace_introspector/introspect_linux.cpp index 3fc1a077f6..2bee8c0bd3 100644 --- a/yt/yt/library/backtrace_introspector/introspect_linux.cpp +++ b/yt/yt/library/backtrace_introspector/introspect_linux.cpp @@ -163,6 +163,12 @@ std::vector<TThreadIntrospectionInfo> IntrospectThreads() std::vector<TThreadIntrospectionInfo> infos; for (auto threadId : GetCurrentProcessThreadIds()) { + if (!IsUserspaceThread(threadId)) { + YT_LOG_DEBUG("Skipping a non-userspace thread (ThreadId: %v)", + threadId); + continue; + } + TSignalHandlerContext signalHandlerContext; if (::syscall(SYS_tkill, threadId, SIGUSR1) != 0) { YT_LOG_DEBUG(TError::FromSystem(), "Failed to signal to thread (ThreadId: %v)", diff --git a/yt/yt/library/formats/arrow_writer.cpp b/yt/yt/library/formats/arrow_writer.cpp index 6874a45ff5..d83cb97358 100644 --- a/yt/yt/library/formats/arrow_writer.cpp +++ b/yt/yt/library/formats/arrow_writer.cpp @@ -45,6 +45,7 @@ struct TTypedBatchColumn //////////////////////////////////////////////////////////////////////////////// constexpr i64 ArrowAlignment = 8; +const TString AlignmentString(ArrowAlignment, 0); flatbuffers::Offset<flatbuffers::String> SerializeString( flatbuffers::FlatBufferBuilder* flatbufBuilder, @@ -1287,18 +1288,19 @@ private: auto metadataPtr = message.FlatbufBuilder->GetBufferPointer(); - ui32 metadataSz = AlignUp<i64>(metadataSize, ArrowAlignment); + ui32 metadataAlignSize = AlignUp<i64>(metadataSize, ArrowAlignment); - output->Write(&metadataSz, sizeof(ui32)); + output->Write(&metadataAlignSize, sizeof(ui32)); output->Write(metadataPtr, metadataSize); + output->Write(AlignmentString.Data(), metadataAlignSize - metadataSize); + // Body if (message.BodyWriter) { - TString current; - current.resize(message.BodySize); + TString current(AlignUp<i64>(message.BodySize, ArrowAlignment), 0); // Double copying. - message.BodyWriter(TMutableRef::FromString(current)); - output->Write(current.data(), message.BodySize); + message.BodyWriter(TMutableRef(current.begin(), current.begin() + message.BodySize)); + output->Write(current.data(), current.Size()); } else { YT_VERIFY(message.BodySize == 0); } diff --git a/yt/yt/library/profiling/solomon/exporter.h b/yt/yt/library/profiling/solomon/exporter.h index 6ec0ce7dc1..8aad2abcb1 100644 --- a/yt/yt/library/profiling/solomon/exporter.h +++ b/yt/yt/library/profiling/solomon/exporter.h @@ -4,20 +4,20 @@ #include "registry.h" #include "remote.h" -#include <yt/yt/core/concurrency/thread_pool.h> -#include <yt/yt/core/concurrency/async_rw_lock.h> - #include <yt/yt/core/actions/public.h> +#include <yt/yt/core/concurrency/async_rw_lock.h> +#include <yt/yt/core/concurrency/thread_pool.h> + #include <yt/yt/core/http/public.h> -#include <yt/yt/core/ytree/yson_serializable.h> #include <yt/yt/core/ytree/ypath_detail.h> +#include <yt/yt/core/ytree/yson_struct.h> -#include <library/cpp/monlib/encode/format.h> - -#include <yt/yt/library/profiling/sensor.h> #include <yt/yt/library/profiling/producer.h> +#include <yt/yt/library/profiling/sensor.h> + +#include <library/cpp/monlib/encode/format.h> namespace NYT::NProfiling { diff --git a/yt/yt/library/program/config.cpp b/yt/yt/library/program/config.cpp index 288a90a3b1..304f9c761c 100644 --- a/yt/yt/library/program/config.cpp +++ b/yt/yt/library/program/config.cpp @@ -169,13 +169,6 @@ void WarnForUnrecognizedOptions( WarnForUnrecognizedOptionsImpl(logger, config->GetRecursiveUnrecognized()); } -void WarnForUnrecognizedOptions( - const NLogging::TLogger& logger, - const NYTree::TYsonSerializablePtr& config) -{ - WarnForUnrecognizedOptionsImpl(logger, config->GetUnrecognizedRecursively()); -} - void AbortOnUnrecognizedOptionsImpl( const NLogging::TLogger& logger, const IMapNodePtr& unrecognized) @@ -195,13 +188,6 @@ void AbortOnUnrecognizedOptions( AbortOnUnrecognizedOptionsImpl(logger, config->GetRecursiveUnrecognized()); } -void AbortOnUnrecognizedOptions( - const NLogging::TLogger& logger, - const NYTree::TYsonSerializablePtr& config) -{ - AbortOnUnrecognizedOptionsImpl(logger, config->GetUnrecognizedRecursively()); -} - //////////////////////////////////////////////////////////////////////////////// } // namespace NYT diff --git a/yt/yt/library/program/config.h b/yt/yt/library/program/config.h index b840950418..d4420b7278 100644 --- a/yt/yt/library/program/config.h +++ b/yt/yt/library/program/config.h @@ -2,7 +2,6 @@ #include "public.h" -#include <yt/yt/core/ytree/yson_serializable.h> #include <yt/yt/core/ytree/yson_struct.h> #include <yt/yt/core/ytalloc/config.h> diff --git a/yt/yt/library/program/program_config_mixin.h b/yt/yt/library/program/program_config_mixin.h index 80f681d06e..784c422477 100644 --- a/yt/yt/library/program/program_config_mixin.h +++ b/yt/yt/library/program/program_config_mixin.h @@ -2,10 +2,12 @@ #include "program.h" -#include <library/cpp/yt/string/enum.h> +#include <yt/yt/core/yson/writer.h> #include <yt/yt/core/ytree/convert.h> -#include <yt/yt/core/ytree/yson_serializable.h> +#include <yt/yt/core/ytree/yson_struct.h> + +#include <library/cpp/yt/string/enum.h> #include <util/stream/file.h> diff --git a/yt/yt/library/tracing/jaeger/tracer.cpp b/yt/yt/library/tracing/jaeger/tracer.cpp index 351ec787d4..fd3a409f6d 100644 --- a/yt/yt/library/tracing/jaeger/tracer.cpp +++ b/yt/yt/library/tracing/jaeger/tracer.cpp @@ -11,11 +11,13 @@ #include <yt/yt/core/concurrency/action_queue.h> #include <yt/yt/core/concurrency/periodic_executor.h> -#include <yt/yt/core/ytree/yson_serializable.h> #include <yt/yt/core/misc/protobuf_helpers.h> #include <yt/yt/core/misc/serialize.h> + #include <yt/yt/core/utilex/random.h> +#include <yt/yt/core/ytree/yson_struct.h> + #include <util/string/cast.h> #include <util/string/reverse.h> diff --git a/yt/yt_proto/yt/client/api/rpc_proxy/proto/api_service.proto b/yt/yt_proto/yt/client/api/rpc_proxy/proto/api_service.proto index 8b0a758fd8..297ee2ae7d 100644 --- a/yt/yt_proto/yt/client/api/rpc_proxy/proto/api_service.proto +++ b/yt/yt_proto/yt/client/api/rpc_proxy/proto/api_service.proto @@ -601,6 +601,20 @@ message TRspSelectRows //////////////////////////////////////////////////////////////////////////////// +message TReqAdvanceConsumer +{ + optional NYT.NProto.TGuid transaction_id = 1; + optional string consumer_path = 2; + optional string queue_path = 3; + optional int32 partition_index = 4; + optional int64 old_offset = 5; + optional int64 new_offset = 6; +} + +message TRspAdvanceConsumer +{ +} + message TRowBatchReadOptions { optional int64 max_row_count = 1; |