diff --git a/.github/label-pr-config.yml b/.github/label-pr-config.yml index ac998eeb9d20a7..796877434b4cda 100644 --- a/.github/label-pr-config.yml +++ b/.github/label-pr-config.yml @@ -102,6 +102,7 @@ subSystemLabels: /^lib\/internal\/url\.js$/: whatwg-url /^lib\/internal\/modules\/esm/: esm /^lib\/internal\/webstreams/: web streams + /^lib\/internal\/test_runner/: dont-land-on-v14.x # All other lib/ files map directly /^lib\/_(\w+)_\w+\.js?$/: $1 # e.g. _(stream)_wrap diff --git a/.github/workflows/timezone-update.yml b/.github/workflows/timezone-update.yml new file mode 100644 index 00000000000000..9d567415f4b2a6 --- /dev/null +++ b/.github/workflows/timezone-update.yml @@ -0,0 +1,48 @@ +name: Timezone update +on: + schedule: + # Run once a week at 00:05 AM UTC on Sunday. + - cron: 5 0 * * 0 + + workflow_dispatch: + +jobs: + timezone_update: + if: github.repository == 'nodejs/node' + runs-on: ubuntu-latest + + steps: + - name: Checkout nodejs/node + uses: actions/checkout@v3 + with: + persist-credentials: false + + - name: Checkout unicode-org/icu-data + uses: actions/checkout@v3 + with: + path: icu-data + persist-credentials: false + repository: unicode-org/icu-data + + - run: ./tools/update-timezone.mjs + + - name: Open Pull Request + uses: gr2m/create-or-update-pull-request-action@6720400cad8e74d7adc64640e4e6ea6748b83d8f # Create a PR or update the Action's existing PR + env: + GITHUB_TOKEN: ${{ secrets.GH_USER_TOKEN }} + with: + author: Node.js GitHub Bot + body: | + This PR was generated by tools/timezone-update.yml. + + Updates the ICU files as per the instructions present in https://github.com/nodejs/node/blob/main/doc/contributing/maintaining-icu.md#time-zone-data + + To test, build node off this branch & log the version of tz using + ```js + console.log(process.versions.tz) + ``` + branch: actions/timezone-update + commit-message: 'deps: update timezone' + labels: dependencies + title: 'deps: update timezone' + reviewers: \@nodejs/i18n-api diff --git a/.mailmap b/.mailmap index b361dff7388c0b..4a42b718f44a8e 100644 --- a/.mailmap +++ b/.mailmap @@ -137,6 +137,7 @@ David Mark Clements David Siegel DC Deepjyoti Mondal +dnlup Domenic Denicola Domenic Denicola Doug Wade @@ -200,7 +201,6 @@ Hannes Magnusson Hassaan Pasha Hendrik Schwalm Henry Chin -Nick Sia <31839263+nicksia-vgw@users.noreply.github.com> Herbert Vojčík Hitesh Kanwathirtha Icer Liang @@ -374,6 +374,7 @@ Nam Nguyen Nebu Pookins Netto Farah Nicholas Kinsey +Nick Sia <31839263+nicksia-vgw@users.noreply.github.com> Nick Soggin Nigel Kibodeaux Nikola Glavina diff --git a/AUTHORS b/AUTHORS index 963908417c5d2c..f5184d00a06d04 100644 --- a/AUTHORS +++ b/AUTHORS @@ -2694,7 +2694,7 @@ Yann Hamon Ben Swinburne Colin Prince TJKoury -dnlup +dnlup Hang Jiang Vladislav Kaminsky Daiki Ihara @@ -3514,5 +3514,19 @@ Jeff Dickey <216188+jdxcode@users.noreply.github.com> Matías Zúñiga metonym Brian Evans <53117772+mrbrianevans@users.noreply.github.com> +falsandtru +东灯 <43312495+Lampese@users.noreply.github.com> +Fabian Meyer <3982806+meyfa@users.noreply.github.com> +StefanStojanovic +Claudio Wunder +Shrujal Shah +Taha-Chaudhry <46199675+Taha-Chaudhry@users.noreply.github.com> +smitley +Brian Muenzenmeyer +sidwebworks +Connor Burton +chexiongsheng +Lucas Santos +“Pooja # Generated by tools/update-authors.mjs diff --git a/BUILDING.md b/BUILDING.md index 47e2a71772f212..526df5b519df86 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -621,6 +621,7 @@ Set-ExecutionPolicy Unrestricted -Force iex ((New-Object System.Net.WebClient).DownloadString('https://boxstarter.org/bootstrapper.ps1')) get-boxstarter -Force Install-BoxstarterPackage https://raw.githubusercontent.com/nodejs/node/HEAD/tools/bootstrap/windows_boxstarter -DisableReboots +refreshenv ``` The entire installation using Boxstarter will take up approximately 10 GB of diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ba6c38247b5b1..e06afe72bdbfe9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,7 +35,8 @@ release. -18.9.1
+18.10.0
+18.9.1
18.9.0
18.8.0
18.7.0
diff --git a/README.md b/README.md index ed5c65935d2a54..cea0ff25e0d68c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Node.js -Node.js is an open-source, cross-platform, JavaScript runtime environment. +Node.js is an open-source, cross-platform JavaScript runtime environment. For information on using Node.js, see the [Node.js website][]. @@ -178,6 +178,8 @@ For information about the governance of the Node.js project, see **James M Snell** <> (he/him) * [joyeecheung](https://github.com/joyeecheung) - **Joyee Cheung** <> (she/her) +* [legendecas](https://github.com/legendecas) - + **Chengzhong Wu** <> (he/him) * [mcollina](https://github.com/mcollina) - **Matteo Collina** <> (he/him) * [mhdawson](https://github.com/mhdawson) - @@ -310,8 +312,6 @@ For information about the governance of the Node.js project, see **Gus Caplan** <> (they/them) * [dmabupt](https://github.com/dmabupt) - **Xu Meng** <> (he/him) -* [dnlup](https://github.com/dnlup) - **Daniele Belardi** <> (he/him) * [edsadr](https://github.com/edsadr) - **Adrian Estrada** <> (he/him) * [erickwendel](https://github.com/erickwendel) - @@ -494,6 +494,8 @@ For information about the governance of the Node.js project, see **Jamie Davis** <> (he/him) * [digitalinfinity](https://github.com/digitalinfinity) - **Hitesh Kanwathirtha** <> (he/him) +* [dnlup](https://github.com/dnlup) + **dnlup** <> * [eljefedelrodeodeljefe](https://github.com/eljefedelrodeodeljefe) - **Robert Jefe Lindstaedt** <> * [estliberitas](https://github.com/estliberitas) - diff --git a/android-configure b/android-configure index 6200f35200c774..8bab2e0b90c000 100755 --- a/android-configure +++ b/android-configure @@ -1,83 +1,35 @@ -#!/bin/bash - -# In order to cross-compile node for Android using NDK, run: -# source android-configure [arch] -# -# By running android-configure with source, will allow environment variables to -# be persistent in current session. This is useful for installing native node -# modules with npm. Also, don't forget to set the arch in npm config using -# 'npm config set arch=' - -if [ $# -ne 3 ]; then - echo "$0 should have 3 parameters: ndk_path, target_arch and sdk_version" - return 1 -fi - -NDK_PATH=$1 -ARCH="$2" -ANDROID_SDK_VERSION=$3 - -if [ $ANDROID_SDK_VERSION -lt 24 ]; then - echo "$ANDROID_SDK_VERSION should equal or later than 24 (Android 7.0)" -fi - -case $ARCH in - arm) - DEST_CPU="arm" - TOOLCHAIN_NAME="armv7a-linux-androideabi" - ;; - x86) - DEST_CPU="ia32" - TOOLCHAIN_NAME="i686-linux-android" - ;; - x86_64) - DEST_CPU="x64" - TOOLCHAIN_NAME="x86_64-linux-android" - ARCH="x64" - ;; - arm64|aarch64) - DEST_CPU="arm64" - TOOLCHAIN_NAME="aarch64-linux-android" - ARCH="arm64" - ;; - *) - echo "Unsupported architecture provided: $ARCH" - return 1 - ;; -esac - -HOST_OS="linux" -HOST_ARCH="x86_64" -export CC_host=$(command -v gcc) -export CXX_host=$(command -v g++) - -host_gcc_version=$($CC_host --version | grep gcc | awk '{print $NF}') -major=$(echo $host_gcc_version | awk -F . '{print $1}') -minor=$(echo $host_gcc_version | awk -F . '{print $2}') -if [ -z $major ] || [ -z $minor ] || [ $major -lt 6 ] || ( [ $major -eq 6 ] && [ $minor -lt 3 ] ); then - echo "host gcc $host_gcc_version is too old, need gcc 6.3.0" - return 1 -fi - -SUFFIX="$TOOLCHAIN_NAME$ANDROID_SDK_VERSION" -TOOLCHAIN=$NDK_PATH/toolchains/llvm/prebuilt/$HOST_OS-$HOST_ARCH - -export PATH=$TOOLCHAIN/bin:$PATH -export CC=$TOOLCHAIN/bin/$SUFFIX-clang -export CXX=$TOOLCHAIN/bin/$SUFFIX-clang++ - - -GYP_DEFINES="target_arch=$ARCH" -GYP_DEFINES+=" v8_target_arch=$ARCH" -GYP_DEFINES+=" android_target_arch=$ARCH" -GYP_DEFINES+=" host_os=$HOST_OS OS=android" -export GYP_DEFINES - -if [ -f "configure" ]; then - ./configure \ - --dest-cpu=$DEST_CPU \ - --dest-os=android \ - --without-snapshot \ - --openssl-no-asm \ - --cross-compiling -fi +#!/bin/sh + +# Locate an acceptable Python interpreter and then re-execute the script. +# Note that the mix of single and double quotes is intentional, +# as is the fact that the ] goes on a new line. +_=[ 'exec' '/bin/sh' '-c' ''' +command -v python3.10 >/dev/null && exec python3.10 "$0" "$@" +command -v python3.9 >/dev/null && exec python3.9 "$0" "$@" +command -v python3.8 >/dev/null && exec python3.8 "$0" "$@" +command -v python3.7 >/dev/null && exec python3.7 "$0" "$@" +command -v python3.6 >/dev/null && exec python3.6 "$0" "$@" +command -v python3 >/dev/null && exec python3 "$0" "$@" +exec python "$0" "$@" +''' "$0" "$@" +] +del _ + +import sys +try: + from shutil import which +except ImportError: + from distutils.spawn import find_executable as which + +print('Node.js android configure: Found Python {}.{}.{}...'.format(*sys.version_info)) +acceptable_pythons = ((3, 10), (3, 9), (3, 8), (3, 7), (3, 6)) +if sys.version_info[:2] in acceptable_pythons: + import android_configure +else: + python_cmds = ['python{}.{}'.format(*vers) for vers in acceptable_pythons] + sys.stderr.write('Please use {}.\n'.format(' or '.join(python_cmds))) + for python_cmd in python_cmds: + python_cmd_path = which(python_cmd) + if python_cmd_path and 'pyenv/shims' not in python_cmd_path: + sys.stderr.write('\t{} {}\n'.format(python_cmd_path, ' '.join(sys.argv[:1]))) + sys.exit(1) diff --git a/android-patches/trap-handler.h.patch b/android-patches/trap-handler.h.patch new file mode 100644 index 00000000000000..f4f151f65261f1 --- /dev/null +++ b/android-patches/trap-handler.h.patch @@ -0,0 +1,26 @@ +--- trap-handler.h 2022-08-11 09:01:23.384000000 +0800 ++++ fixed-trap-handler.h 2022-08-11 09:09:15.352000000 +0800 +@@ -17,23 +17,7 @@ + namespace internal { + namespace trap_handler { + +-// X64 on Linux, Windows, MacOS, FreeBSD. +-#if V8_HOST_ARCH_X64 && V8_TARGET_ARCH_X64 && \ +- ((V8_OS_LINUX && !V8_OS_ANDROID) || V8_OS_WIN || V8_OS_DARWIN || \ +- V8_OS_FREEBSD) +-#define V8_TRAP_HANDLER_SUPPORTED true +-// Arm64 (non-simulator) on Mac. +-#elif V8_TARGET_ARCH_ARM64 && V8_HOST_ARCH_ARM64 && V8_OS_DARWIN +-#define V8_TRAP_HANDLER_SUPPORTED true +-// Arm64 simulator on x64 on Linux, Mac, or Windows. +-#elif V8_TARGET_ARCH_ARM64 && V8_HOST_ARCH_X64 && \ +- (V8_OS_LINUX || V8_OS_DARWIN) +-#define V8_TRAP_HANDLER_VIA_SIMULATOR +-#define V8_TRAP_HANDLER_SUPPORTED true +-// Everything else is unsupported. +-#else + #define V8_TRAP_HANDLER_SUPPORTED false +-#endif + + // Setup for shared library export. + #if defined(BUILDING_V8_SHARED) && defined(V8_OS_WIN) \ No newline at end of file diff --git a/android_configure.py b/android_configure.py new file mode 100644 index 00000000000000..57d940239150df --- /dev/null +++ b/android_configure.py @@ -0,0 +1,76 @@ +import platform +import sys +import os + +# TODO: In next version, it will be a JSON file listing all the patches, and then it will iterate through to apply them. +def patch_android(): + print("- Patches List -") + print("[1] [deps/v8/src/trap-handler/trap-handler.h] related to https://github.com/nodejs/node/issues/36287") + if platform.system() == "Linux": + os.system('patch -f ./deps/v8/src/trap-handler/trap-handler.h < ./android-patches/trap-handler.h.patch') + print("\033[92mInfo: \033[0m" + "Tried to patch.") + +if platform.system() == "Windows": + print("android-configure is not supported on Windows yet.") + sys.exit(1) + +if len(sys.argv) == 2 and sys.argv[1] == "patch": + patch_android() + sys.exit(0) + +if len(sys.argv) != 4: + print("Usage: ./android-configure [patch] ") + sys.exit(1) + +if not os.path.exists(sys.argv[1]) or not os.listdir(sys.argv[1]): + print("\033[91mError: \033[0m" + "Invalid path to the Android NDK") + sys.exit(1) + +if int(sys.argv[2]) < 24: + print("\033[91mError: \033[0m" + "Android SDK version must be at least 24 (Android 7.0)") + sys.exit(1) + +android_ndk_path = sys.argv[1] +android_sdk_version = sys.argv[2] +arch = sys.argv[3] + +if arch == "arm": + DEST_CPU = "arm" + TOOLCHAIN_PREFIX = "armv7a-linux-androideabi" +elif arch in ("aarch64", "arm64"): + DEST_CPU = "arm64" + TOOLCHAIN_PREFIX = "aarch64-linux-android" + arch = "arm64" +elif arch == "x86": + DEST_CPU = "ia32" + TOOLCHAIN_PREFIX = "i686-linux-android" +elif arch == "x86_64": + DEST_CPU = "x64" + TOOLCHAIN_PREFIX = "x86_64-linux-android" + arch = "x64" +else: + print("\033[91mError: \033[0m" + "Invalid target architecture, must be one of: arm, arm64, aarch64, x86, x86_64") + sys.exit(1) + +print("\033[92mInfo: \033[0m" + "Configuring for " + DEST_CPU + "...") + +if platform.system() == "Darwin": + host_os = "darwin" + toolchain_path = android_ndk_path + "/toolchains/llvm/prebuilt/darwin-x86_64" + +elif platform.system() == "Linux": + host_os = "linux" + toolchain_path = android_ndk_path + "/toolchains/llvm/prebuilt/linux-x86_64" + +os.environ['PATH'] += os.pathsep + toolchain_path + "/bin" +os.environ['CC'] = toolchain_path + "/bin/" + TOOLCHAIN_PREFIX + android_sdk_version + "-" + "clang" +os.environ['CXX'] = toolchain_path + "/bin/" + TOOLCHAIN_PREFIX + android_sdk_version + "-" + "clang++" + +GYP_DEFINES = "target_arch=" + arch +GYP_DEFINES += " v8_target_arch=" + arch +GYP_DEFINES += " android_target_arch=" + arch +GYP_DEFINES += " host_os=" + host_os + " OS=android" +os.environ['GYP_DEFINES'] = GYP_DEFINES + +if os.path.exists("./configure"): + os.system("./configure --dest-cpu=" + DEST_CPU + " --dest-os=android --openssl-no-asm --cross-compiling") diff --git a/benchmark/fixtures/require-cachable.js b/benchmark/fixtures/require-cachable.js index 85e3a81f4e9fb0..105652a51855eb 100644 --- a/benchmark/fixtures/require-cachable.js +++ b/benchmark/fixtures/require-cachable.js @@ -2,8 +2,8 @@ const { internalBinding } = require('internal/test/binding'); const { - moduleCategories: { canBeRequired } -} = internalBinding('native_module'); + builtinCategories: { canBeRequired } +} = internalBinding('builtins'); for (const key of canBeRequired) { require(`node:${key}`); diff --git a/benchmark/process/next-tick-loop-args.js b/benchmark/process/next-tick-loop-args.js new file mode 100644 index 00000000000000..d163a799aee396 --- /dev/null +++ b/benchmark/process/next-tick-loop-args.js @@ -0,0 +1,37 @@ +'use strict'; +const common = require('../common.js'); +const bench = common.createBenchmark(main, { + n: [1e4, 2e4, 4e4], + loop: [1e2, 2e2], +}); + +function main({ n, loop }) { + bench.start(); + run(); + function run() { + let j = 0; + + function cb() { + j++; + if (j === n) { + loop--; + if (loop === 0) { + bench.end(n); + } else { + run(); + } + } + } + + for (let i = 0; i < n; i++) { + if (i % 4 === 0) + process.nextTick(cb, i, true, 10, 'test'); + else if (i % 3 === 0) + process.nextTick(cb, i, true, 10); + else if (i % 2 === 0) + process.nextTick(cb, i, 20); + else + process.nextTick(cb, i); + } + } +} diff --git a/benchmark/process/next-tick-loop.js b/benchmark/process/next-tick-loop.js new file mode 100644 index 00000000000000..5159910e576be7 --- /dev/null +++ b/benchmark/process/next-tick-loop.js @@ -0,0 +1,30 @@ +'use strict'; +const common = require('../common.js'); +const bench = common.createBenchmark(main, { + n: [1e4, 2e4, 4e4], + loop: [1e2, 2e2], +}); + +function main({ n, loop }) { + bench.start(); + run(); + function run() { + let j = 0; + + function cb() { + j++; + if (j === n) { + loop--; + if (loop === 0) { + bench.end(n); + } else { + run(); + } + } + } + + for (let i = 0; i < n; i++) { + process.nextTick(cb); + } + } +} diff --git a/benchmark/streams/destroy.js b/benchmark/streams/destroy.js new file mode 100644 index 00000000000000..c6811139d8c443 --- /dev/null +++ b/benchmark/streams/destroy.js @@ -0,0 +1,56 @@ +'use strict'; +const common = require('../common.js'); +const { + Duplex, + Readable, + Transform, + Writable, +} = require('stream'); + +const bench = common.createBenchmark(main, { + n: [1e6], + kind: ['duplex', 'readable', 'transform', 'writable'] +}); + +function main({ n, kind }) { + switch (kind) { + case 'duplex': + new Duplex({}); + new Duplex(); + + bench.start(); + for (let i = 0; i < n; ++i) + new Duplex().destroy(); + bench.end(n); + break; + case 'readable': + new Readable({}); + new Readable(); + + bench.start(); + for (let i = 0; i < n; ++i) + new Readable().destroy(); + bench.end(n); + break; + case 'writable': + new Writable({}); + new Writable(); + + bench.start(); + for (let i = 0; i < n; ++i) + new Writable().destroy(); + bench.end(n); + break; + case 'transform': + new Transform({}); + new Transform(); + + bench.start(); + for (let i = 0; i < n; ++i) + new Transform().destroy(); + bench.end(n); + break; + default: + throw new Error('Invalid kind'); + } +} diff --git a/common.gypi b/common.gypi index 33cd361e67a672..3f708d89b1ef38 100644 --- a/common.gypi +++ b/common.gypi @@ -212,6 +212,9 @@ 'cflags': [ '-qINLINE=::150:100000' ] }], ['OS!="mac" and OS!="win" and OS!="zos"', { + # -fno-omit-frame-pointer is necessary for the --perf_basic_prof + # flag to work correctly. perf(1) gets confused about JS stack + # frames otherwise, even with --call-graph dwarf. 'cflags': [ '-fno-omit-frame-pointer' ], }], ['OS=="linux"', { @@ -546,9 +549,6 @@ }], ], }], - ['OS=="freebsd" and node_use_dtrace=="true"', { - 'libraries': [ '-lelf' ], - }], ['OS=="freebsd"', { 'ldflags': [ '-Wl,--export-dynamic', diff --git a/configure.py b/configure.py index d83cebc9d08a49..a4e5723067f286 100755 --- a/configure.py +++ b/configure.py @@ -45,7 +45,7 @@ parser = argparse.ArgumentParser() valid_os = ('win', 'mac', 'solaris', 'freebsd', 'openbsd', 'linux', - 'android', 'aix', 'cloudabi') + 'android', 'aix', 'cloudabi', 'ios') valid_arch = ('arm', 'arm64', 'ia32', 'mips', 'mipsel', 'mips64el', 'ppc', 'ppc64', 'x64', 'x86', 'x86_64', 's390x', 'riscv64', 'loong64') valid_arm_float_abi = ('soft', 'softfp', 'hard') @@ -2083,7 +2083,7 @@ def make_bin_override(): gyp_args += ['-Dbuild_type=' + config['BUILDTYPE']] if options.use_ninja: - gyp_args += ['-f', 'ninja'] + gyp_args += ['-f', 'ninja-' + flavor] elif flavor == 'win' and sys.platform != 'msys': gyp_args += ['-f', 'msvs', '-G', 'msvs_version=auto'] else: diff --git a/deps/cares/cares.gyp b/deps/cares/cares.gyp index 88933e00745d30..74f0e78e069f1e 100644 --- a/deps/cares/cares.gyp +++ b/deps/cares/cares.gyp @@ -157,7 +157,7 @@ 'include_dirs': [ 'config/linux' ], 'sources': [ 'config/linux/ares_config.h' ] }], - [ 'OS=="mac"', { + [ 'OS=="mac" or OS=="ios"', { 'include_dirs': [ 'config/darwin' ], 'sources': [ 'config/darwin/ares_config.h' ] }], diff --git a/deps/corepack/CHANGELOG.md b/deps/corepack/CHANGELOG.md index 97cc62b425f025..8bfc7c0f5142d6 100644 --- a/deps/corepack/CHANGELOG.md +++ b/deps/corepack/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [0.14.1](https://github.com/nodejs/corepack/compare/v0.14.0...v0.14.1) (2022-09-16) + + +### Features + +* update package manager versions ([#179](https://github.com/nodejs/corepack/issues/179)) ([0b88dcb](https://github.com/nodejs/corepack/commit/0b88dcbaaf190117c6f407b6632a4b3b10da8ad9)) + ## [0.14.0](https://github.com/nodejs/corepack/compare/v0.13.0...v0.14.0) (2022-09-02) diff --git a/deps/corepack/dist/corepack.js b/deps/corepack/dist/corepack.js index 8dd76226976b10..923b26744f498c 100755 --- a/deps/corepack/dist/corepack.js +++ b/deps/corepack/dist/corepack.js @@ -17027,7 +17027,7 @@ const supportsColor = { /***/ ((module) => { "use strict"; -module.exports = JSON.parse('{"definitions":{"npm":{"default":"8.19.1+sha1.78bfc5fc1b7bc36881a2d9d1f2c93ad0246f31e5","fetchLatestFrom":{"type":"npm","package":"npm"},"transparent":{"commands":[["npm","init"],["npx"]]},"ranges":{"*":{"url":"https://registry.npmjs.org/npm/-/npm-{}.tgz","bin":{"npm":"./bin/npm-cli.js","npx":"./bin/npx-cli.js"},"registry":{"type":"npm","package":"npm"}}}},"pnpm":{"default":"7.9.5+sha1.8d28e3270689e2afd345777fec9d9535f427b532","fetchLatestFrom":{"type":"npm","package":"pnpm"},"transparent":{"commands":[["pnpm","init"],["pnpx"],["pnpm","dlx"]]},"ranges":{"<6.0.0":{"url":"https://registry.npmjs.org/pnpm/-/pnpm-{}.tgz","bin":{"pnpm":"./bin/pnpm.js","pnpx":"./bin/pnpx.js"},"registry":{"type":"npm","package":"pnpm"}},">=6.0.0":{"url":"https://registry.npmjs.org/pnpm/-/pnpm-{}.tgz","bin":{"pnpm":"./bin/pnpm.cjs","pnpx":"./bin/pnpx.cjs"},"registry":{"type":"npm","package":"pnpm"}}}},"yarn":{"default":"1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447","fetchLatestFrom":{"type":"npm","package":"yarn"},"transparent":{"default":"3.2.3+sha224.953c8233f7a92884eee2de69a1b92d1f2ec1655e66d08071ba9a02fa","commands":[["yarn","dlx"]]},"ranges":{"<2.0.0":{"url":"https://registry.yarnpkg.com/yarn/-/yarn-{}.tgz","bin":{"yarn":"./bin/yarn.js","yarnpkg":"./bin/yarn.js"},"registry":{"type":"npm","package":"yarn"}},">=2.0.0":{"name":"yarn","url":"https://repo.yarnpkg.com/{}/packages/yarnpkg-cli/bin/yarn.js","bin":["yarn","yarnpkg"],"registry":{"type":"url","url":"https://repo.yarnpkg.com/tags","fields":{"tags":"latest","versions":"tags"}}}}}}}'); +module.exports = JSON.parse('{"definitions":{"npm":{"default":"8.19.2+sha1.db90e88584d065f51b069ab46b4f02f5cf4898b7","fetchLatestFrom":{"type":"npm","package":"npm"},"transparent":{"commands":[["npm","init"],["npx"]]},"ranges":{"*":{"url":"https://registry.npmjs.org/npm/-/npm-{}.tgz","bin":{"npm":"./bin/npm-cli.js","npx":"./bin/npx-cli.js"},"registry":{"type":"npm","package":"npm"}}}},"pnpm":{"default":"7.11.0+sha1.ae48bcb805080989f94fe5dca372013c9ae517e7","fetchLatestFrom":{"type":"npm","package":"pnpm"},"transparent":{"commands":[["pnpm","init"],["pnpx"],["pnpm","dlx"]]},"ranges":{"<6.0.0":{"url":"https://registry.npmjs.org/pnpm/-/pnpm-{}.tgz","bin":{"pnpm":"./bin/pnpm.js","pnpx":"./bin/pnpx.js"},"registry":{"type":"npm","package":"pnpm"}},">=6.0.0":{"url":"https://registry.npmjs.org/pnpm/-/pnpm-{}.tgz","bin":{"pnpm":"./bin/pnpm.cjs","pnpx":"./bin/pnpx.cjs"},"registry":{"type":"npm","package":"pnpm"}}}},"yarn":{"default":"1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447","fetchLatestFrom":{"type":"npm","package":"yarn"},"transparent":{"default":"3.2.3+sha224.953c8233f7a92884eee2de69a1b92d1f2ec1655e66d08071ba9a02fa","commands":[["yarn","dlx"]]},"ranges":{"<2.0.0":{"url":"https://registry.yarnpkg.com/yarn/-/yarn-{}.tgz","bin":{"yarn":"./bin/yarn.js","yarnpkg":"./bin/yarn.js"},"registry":{"type":"npm","package":"yarn"}},">=2.0.0":{"name":"yarn","url":"https://repo.yarnpkg.com/{}/packages/yarnpkg-cli/bin/yarn.js","bin":["yarn","yarnpkg"],"registry":{"type":"url","url":"https://repo.yarnpkg.com/tags","fields":{"tags":"latest","versions":"tags"}}}}}}}'); /***/ }), @@ -17038,7 +17038,7 @@ module.exports = JSON.parse('{"definitions":{"npm":{"default":"8.19.1+sha1.78bfc /***/ ((module) => { "use strict"; -module.exports = JSON.parse('{"name":"corepack","version":"0.14.0","homepage":"https://github.com/nodejs/corepack#readme","bugs":{"url":"https://github.com/nodejs/corepack/issues"},"repository":{"type":"git","url":"https://github.com/nodejs/corepack.git"},"license":"MIT","packageManager":"yarn@4.0.0-rc.15+sha224.7fa5c1d1875b041cea8fcbf9a364667e398825364bf5c5c8cd5f6601","devDependencies":{"@babel/core":"^7.14.3","@babel/plugin-transform-modules-commonjs":"^7.14.0","@babel/preset-typescript":"^7.13.0","@types/debug":"^4.1.5","@types/jest":"^29.0.0","@types/node":"^18.0.0","@types/semver":"^7.1.0","@types/tar":"^6.0.0","@types/which":"^2.0.0","@typescript-eslint/eslint-plugin":"^5.0.0","@typescript-eslint/parser":"^5.0.0","@yarnpkg/eslint-config":"^1.0.0-rc.5","@yarnpkg/fslib":"^2.1.0","@zkochan/cmd-shim":"^5.0.0","babel-plugin-dynamic-import-node":"^2.3.3","clipanion":"^3.0.1","debug":"^4.1.1","eslint":"^8.0.0","eslint-plugin-arca":"^0.15.0","jest":"^29.0.0","nock":"^13.0.4","proxy-agent":"^5.0.0","semver":"^7.1.3","supports-color":"^9.0.0","tar":"^6.0.1","terser-webpack-plugin":"^5.1.2","ts-loader":"^9.0.0","ts-node":"^10.0.0","typescript":"^4.3.2","v8-compile-cache":"^2.3.0","webpack":"^5.38.1","webpack-cli":"^4.0.0","which":"^2.0.2"},"scripts":{"build":"rm -rf dist shims && webpack && ts-node ./mkshims.ts","corepack":"ts-node ./sources/_entryPoint.ts","lint":"yarn eslint","prepack":"yarn build","postpack":"rm -rf dist shims","typecheck":"tsc --noEmit","test":"yarn jest"},"files":["dist","shims","LICENSE.md"],"publishConfig":{"bin":{"corepack":"./dist/corepack.js","pnpm":"./dist/pnpm.js","pnpx":"./dist/pnpx.js","yarn":"./dist/yarn.js","yarnpkg":"./dist/yarnpkg.js"},"executableFiles":["./dist/npm.js","./dist/npx.js","./dist/pnpm.js","./dist/pnpx.js","./dist/yarn.js","./dist/yarnpkg.js","./dist/corepack.js","./shims/npm","./shims/npm.ps1","./shims/npx","./shims/npx.ps1","./shims/pnpm","./shims/pnpm.ps1","./shims/pnpx","./shims/pnpx.ps1","./shims/yarn","./shims/yarn.ps1","./shims/yarnpkg","./shims/yarnpkg.ps1"]},"resolutions":{"vm2":"patch:vm2@npm:3.9.9#.yarn/patches/vm2-npm-3.9.9-03fd1f4dc5.patch"}}'); +module.exports = JSON.parse('{"name":"corepack","version":"0.14.1","homepage":"https://github.com/nodejs/corepack#readme","bugs":{"url":"https://github.com/nodejs/corepack/issues"},"repository":{"type":"git","url":"https://github.com/nodejs/corepack.git"},"license":"MIT","packageManager":"yarn@4.0.0-rc.15+sha224.7fa5c1d1875b041cea8fcbf9a364667e398825364bf5c5c8cd5f6601","devDependencies":{"@babel/core":"^7.14.3","@babel/plugin-transform-modules-commonjs":"^7.14.0","@babel/preset-typescript":"^7.13.0","@types/debug":"^4.1.5","@types/jest":"^29.0.0","@types/node":"^18.0.0","@types/semver":"^7.1.0","@types/tar":"^6.0.0","@types/which":"^2.0.0","@typescript-eslint/eslint-plugin":"^5.0.0","@typescript-eslint/parser":"^5.0.0","@yarnpkg/eslint-config":"^1.0.0-rc.5","@yarnpkg/fslib":"^2.1.0","@zkochan/cmd-shim":"^5.0.0","babel-plugin-dynamic-import-node":"^2.3.3","clipanion":"^3.0.1","debug":"^4.1.1","eslint":"^8.0.0","eslint-plugin-arca":"^0.15.0","jest":"^29.0.0","nock":"^13.0.4","proxy-agent":"^5.0.0","semver":"^7.1.3","supports-color":"^9.0.0","tar":"^6.0.1","terser-webpack-plugin":"^5.1.2","ts-loader":"^9.0.0","ts-node":"^10.0.0","typescript":"^4.3.2","v8-compile-cache":"^2.3.0","webpack":"^5.38.1","webpack-cli":"^4.0.0","which":"^2.0.2"},"scripts":{"build":"rm -rf dist shims && webpack && ts-node ./mkshims.ts","corepack":"ts-node ./sources/_entryPoint.ts","lint":"yarn eslint","prepack":"yarn build","postpack":"rm -rf dist shims","typecheck":"tsc --noEmit","test":"yarn jest"},"files":["dist","shims","LICENSE.md"],"publishConfig":{"bin":{"corepack":"./dist/corepack.js","pnpm":"./dist/pnpm.js","pnpx":"./dist/pnpx.js","yarn":"./dist/yarn.js","yarnpkg":"./dist/yarnpkg.js"},"executableFiles":["./dist/npm.js","./dist/npx.js","./dist/pnpm.js","./dist/pnpx.js","./dist/yarn.js","./dist/yarnpkg.js","./dist/corepack.js","./shims/npm","./shims/npm.ps1","./shims/npx","./shims/npx.ps1","./shims/pnpm","./shims/pnpm.ps1","./shims/pnpx","./shims/pnpx.ps1","./shims/yarn","./shims/yarn.ps1","./shims/yarnpkg","./shims/yarnpkg.ps1"]},"resolutions":{"vm2":"patch:vm2@npm:3.9.9#.yarn/patches/vm2-npm-3.9.9-03fd1f4dc5.patch"}}'); /***/ }) diff --git a/deps/corepack/package.json b/deps/corepack/package.json index bf8806ae9374f6..323996f93b7de8 100644 --- a/deps/corepack/package.json +++ b/deps/corepack/package.json @@ -1,6 +1,6 @@ { "name": "corepack", - "version": "0.14.0", + "version": "0.14.1", "homepage": "https://github.com/nodejs/corepack#readme", "bugs": { "url": "https://github.com/nodejs/corepack/issues" diff --git a/deps/ngtcp2/README.md b/deps/ngtcp2/README.md index bd56beff9ea5fa..3585fa1f3490c7 100644 --- a/deps/ngtcp2/README.md +++ b/deps/ngtcp2/README.md @@ -9,7 +9,8 @@ The sources are pulled from: * nghttp3: https://github.com/ngtcp2/nghttp3 In both the `ngtcp2` and `nghttp3` git repos, the active development occurs -in the default branch (currently named `master` in each). +in the default branch (currently named `main` in each). Tagged versions do not +always point to the default branch. We only use a subset of the sources for each. @@ -17,14 +18,17 @@ We only use a subset of the sources for each. The `nghttp3` library depends on `ngtcp2`. Both should always be updated together. From `ngtcp2` we only want the contents of the `lib` and `crypto` -directories; from `nghttp3` we only want the contents of the `lib`. +directories; from `nghttp3` we only want the contents of the `lib` directory. + +After updating either dependency, check if any source files or include +directories have been added or removed and update `ngtcp2.gyp` accordingly. ### Updating ngtcp2 -To update ngtcp2: +To update ngtcp2, replace `v0.8.1` with the desired git tag: ```sh -$ git clone https://github.com/ngtcp2/ngtcp2 +$ git clone --depth=1 --branch=v0.8.1 https://github.com/ngtcp2/ngtcp2 $ cd ngtcp2 $ autoreconf -i $ ./configure --prefix=$PWD/build --enable-lib-only @@ -34,10 +38,10 @@ $ cp -R crypto/* ../node/deps/ngtcp2/ngtcp2/crypto/ ### Updating nghttp3 -To update ngtcp2: +To update nghttp3, replace `v0.7.0` with the desired git tag: ```sh -$ git clone https://github.com/ngtcp2/nghttp3 +$ git clone --depth=1 --branch=v0.7.0 https://github.com/ngtcp2/nghttp3 $ cd nghttp3 $ autoreconf -i $ ./configure --prefix=$PWD/build --enable-lib-only diff --git a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h index bc66c40ce211e4..cd10e4def7019b 100644 --- a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h +++ b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h @@ -125,13 +125,6 @@ typedef ptrdiff_t nghttp3_ssize; * already in use. */ #define NGHTTP3_ERR_STREAM_IN_USE -105 -/** - * @macro - * - * :macro:`NGHTTP3_ERR_PUSH_ID_BLOCKED` indicates that there are no - * spare push ID available. - */ -#define NGHTTP3_ERR_PUSH_ID_BLOCKED -106 /** * @macro * @@ -167,13 +160,6 @@ typedef ptrdiff_t nghttp3_ssize; * field is too large to process. */ #define NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE -112 -/** - * @macro - * - * :macro:`NGHTTP3_ERR_IGNORE_STREAM` indicates that a stream should - * be ignored. - */ -#define NGHTTP3_ERR_IGNORE_STREAM -113 /** * @macro * @@ -184,17 +170,17 @@ typedef ptrdiff_t nghttp3_ssize; /** * @macro * - * :macro:`NGHTTP3_ERR_IGNORE_PUSH_PROMISE` indicates that a push - * promise should be ignored. + * :macro:`NGHTTP3_ERR_CONN_CLOSING` indicates that a connection is + * closing state. */ -#define NGHTTP3_ERR_IGNORE_PUSH_PROMISE -115 +#define NGHTTP3_ERR_CONN_CLOSING -116 /** * @macro * - * :macro:`NGHTTP3_ERR_CONN_CLOSING` indicates that a connection is - * closing state. + * :macro:`NGHTTP3_ERR_STREAM_DATA_OVERFLOW` indicates that the length + * of stream data is too long and causes overflow. */ -#define NGHTTP3_ERR_CONN_CLOSING -116 +#define NGHTTP3_ERR_STREAM_DATA_OVERFLOW -117 /** * @macro * @@ -451,59 +437,69 @@ typedef ptrdiff_t nghttp3_ssize; /** * @functypedef * - * Custom memory allocator to replace malloc(). The |mem_user_data| - * is the mem_user_data member of :type:`nghttp3_mem` structure. + * :type:`nghttp3_malloc` is a custom memory allocator to replace + * :manpage:`malloc(3)`. The |user_data| is the + * :member:`nghttp3_mem.user_data`. */ -typedef void *(*nghttp3_malloc)(size_t size, void *mem_user_data); +typedef void *(*nghttp3_malloc)(size_t size, void *user_data); /** * @functypedef * - * Custom memory allocator to replace free(). The |mem_user_data| is - * the mem_user_data member of :type:`nghttp3_mem` structure. + * :type:`nghttp3_free` is a custom memory allocator to replace + * :manpage:`free(3)`. The |user_data| is the + * :member:`nghttp3_mem.user_data`. */ -typedef void (*nghttp3_free)(void *ptr, void *mem_user_data); +typedef void (*nghttp3_free)(void *ptr, void *user_data); /** * @functypedef * - * Custom memory allocator to replace calloc(). The |mem_user_data| - * is the mem_user_data member of :type:`nghttp3_mem` structure. + * :type:`nghttp3_calloc` is a custom memory allocator to replace + * :manpage:`calloc(3)`. The |user_data| is the + * :member:`nghttp3_mem.user_data`. */ -typedef void *(*nghttp3_calloc)(size_t nmemb, size_t size, void *mem_user_data); +typedef void *(*nghttp3_calloc)(size_t nmemb, size_t size, void *user_data); /** * @functypedef * - * Custom memory allocator to replace realloc(). The |mem_user_data| - * is the mem_user_data member of :type:`nghttp3_mem` structure. + * :type:`nghttp3_realloc` is a custom memory allocator to replace + * :manpage:`realloc(3)`. The |user_data| is the + * :member:`nghttp3_mem.user_data`. */ -typedef void *(*nghttp3_realloc)(void *ptr, size_t size, void *mem_user_data); +typedef void *(*nghttp3_realloc)(void *ptr, size_t size, void *user_data); /** * @struct * * :type:`nghttp3_mem` is a custom memory allocator functions and user - * defined pointer. The |mem_user_data| member is passed to each + * defined pointer. The :member:`user_data` field is passed to each * allocator function. This can be used, for example, to achieve * per-session memory pool. * * In the following example code, ``my_malloc``, ``my_free``, * ``my_calloc`` and ``my_realloc`` are the replacement of the - * standard allocators ``malloc``, ``free``, ``calloc`` and - * ``realloc`` respectively:: + * standard allocators :manpage:`malloc(3)`, :manpage:`free(3)`, + * :manpage:`calloc(3)` and :manpage:`realloc(3)` respectively:: * - * void *my_malloc_cb(size_t size, void *mem_user_data) { + * void *my_malloc_cb(size_t size, void *user_data) { + * (void)user_data; * return my_malloc(size); * } * - * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } + * void my_free_cb(void *ptr, void *user_data) { + * (void)user_data; + * my_free(ptr); + * } * - * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { + * void *my_calloc_cb(size_t nmemb, size_t size, void *user_data) { + * (void)user_data; * return my_calloc(nmemb, size); * } * - * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { + * void *my_realloc_cb(void *ptr, size_t size, void *user_data) { + * (void)user_data; * return my_realloc(ptr, size); * } * @@ -516,27 +512,28 @@ typedef void *(*nghttp3_realloc)(void *ptr, size_t size, void *mem_user_data); */ typedef struct nghttp3_mem { /** - * :member:`mem_user_data` is an arbitrary user supplied data. This + * :member:`user_data` is an arbitrary user supplied data. This * is passed to each allocator function. */ - void *mem_user_data; + void *user_data; /** * :member:`malloc` is a custom allocator function to replace - * malloc(). + * :manpage:`malloc(3)`. */ nghttp3_malloc malloc; /** - * :member:`free` is a custom allocator function to replace free(). + * :member:`free` is a custom allocator function to replace + * :manpage:`free(3)`. */ nghttp3_free free; /** * :member:`calloc` is a custom allocator function to replace - * calloc(). + * :manpage:`calloc(3)`. */ nghttp3_calloc calloc; /** * :member:`realloc` is a custom allocator function to replace - * realloc(). + * :manpage:`realloc(3)`. */ nghttp3_realloc realloc; } nghttp3_mem; @@ -652,8 +649,8 @@ NGHTTP3_EXTERN void nghttp3_buf_init(nghttp3_buf *buf); * @function * * `nghttp3_buf_free` frees resources allocated for |buf| using |mem| - * as memory allocator. buf->begin must be a heap buffer allocated by - * |mem|. + * as memory allocator. :member:`buf->begin ` must + * be a heap buffer allocated by |mem|. */ NGHTTP3_EXTERN void nghttp3_buf_free(nghttp3_buf *buf, const nghttp3_mem *mem); @@ -662,7 +659,8 @@ NGHTTP3_EXTERN void nghttp3_buf_free(nghttp3_buf *buf, const nghttp3_mem *mem); * * `nghttp3_buf_left` returns the number of additional bytes which can * be written to the underlying buffer. In other words, it returns - * buf->end - buf->last. + * :member:`buf->end ` - :member:`buf->last + * `. */ NGHTTP3_EXTERN size_t nghttp3_buf_left(const nghttp3_buf *buf); @@ -670,14 +668,17 @@ NGHTTP3_EXTERN size_t nghttp3_buf_left(const nghttp3_buf *buf); * @function * * `nghttp3_buf_len` returns the number of bytes left to read. In - * other words, it returns buf->last - buf->pos. + * other words, it returns :member:`buf->last ` - + * :member:`buf->pos `. */ NGHTTP3_EXTERN size_t nghttp3_buf_len(const nghttp3_buf *buf); /** * @function * - * `nghttp3_buf_reset` sets buf->pos and buf->last to buf->begin. + * `nghttp3_buf_reset` sets :member:`buf->pos ` and + * :member:`buf->last ` to :member:`buf->begin + * `. */ NGHTTP3_EXTERN void nghttp3_buf_reset(nghttp3_buf *buf); @@ -692,7 +693,7 @@ NGHTTP3_EXTERN void nghttp3_buf_reset(nghttp3_buf *buf); * * :macro:`NGHTTP3_NV_FLAG_NONE` indicates no flag set. */ -#define NGHTTP3_NV_FLAG_NONE 0 +#define NGHTTP3_NV_FLAG_NONE 0x00u /** * @macro @@ -701,7 +702,7 @@ NGHTTP3_EXTERN void nghttp3_buf_reset(nghttp3_buf *buf); * pair must not be indexed. Other implementation calls this bit as * "sensitive". */ -#define NGHTTP3_NV_FLAG_NEVER_INDEX 0x01 +#define NGHTTP3_NV_FLAG_NEVER_INDEX 0x01u /** * @macro @@ -710,7 +711,7 @@ NGHTTP3_EXTERN void nghttp3_buf_reset(nghttp3_buf *buf); * If this flag is set, the library does not make a copy of header * field name. This could improve performance. */ -#define NGHTTP3_NV_FLAG_NO_COPY_NAME 0x02 +#define NGHTTP3_NV_FLAG_NO_COPY_NAME 0x02u /** * @macro @@ -719,7 +720,7 @@ NGHTTP3_EXTERN void nghttp3_buf_reset(nghttp3_buf *buf); * application. If this flag is set, the library does not make a copy * of header field value. This could improve performance. */ -#define NGHTTP3_NV_FLAG_NO_COPY_VALUE 0x04 +#define NGHTTP3_NV_FLAG_NO_COPY_VALUE 0x04u /** * @struct @@ -748,7 +749,7 @@ typedef struct nghttp3_nv { size_t valuelen; /** * :member:`flags` is bitwise OR of one or more of - * NGHTTP3_NV_FLAG_*. + * :macro:`NGHTTP3_NV_FLAG_* `. */ uint8_t flags; } nghttp3_nv; @@ -1057,7 +1058,7 @@ typedef enum nghttp3_qpack_token { * :type:`nghttp3_nv` and has reference counted buffers and tokens * which might be useful for applications. */ -typedef struct { +typedef struct nghttp3_qpack_nv { /** * :member:`name` is the buffer containing header field name. * NULL-termination is guaranteed. @@ -1076,7 +1077,7 @@ typedef struct { int32_t token; /** * :member:`flags` is a bitwise OR of one or more of - * NGHTTP3_NV_FLAG_*. See :macro:`NGHTTP3_NV_FLAG_NONE`. + * :macro:`NGHTTP3_NV_FLAG_* `. */ uint8_t flags; } nghttp3_qpack_nv; @@ -1092,11 +1093,14 @@ typedef struct nghttp3_qpack_encoder nghttp3_qpack_encoder; * @function * * `nghttp3_qpack_encoder_new` initializes QPACK encoder. |pencoder| - * must be non-NULL pointer. |max_dtable_size| is the maximum dynamic - * table size. |max_blocked| is the maximum number of streams which - * can be blocked. |mem| is a memory allocator. This function - * allocates memory for :type:`nghttp3_qpack_encoder` itself and - * assigns its pointer to |*pencoder| if it succeeds. + * must be non-NULL pointer. |hard_max_dtable_capacity| is the upper + * bound of the dynamic table capacity. |mem| is a memory allocator. + * This function allocates memory for :type:`nghttp3_qpack_encoder` + * itself and assigns its pointer to |*pencoder| if it succeeds. + * + * The maximum dynamic table capacity is still 0. In order to change + * the maximum dynamic table capacity, call + * `nghttp3_qpack_encoder_set_max_dtable_capacity`. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -1105,8 +1109,7 @@ typedef struct nghttp3_qpack_encoder nghttp3_qpack_encoder; * Out of memory. */ NGHTTP3_EXTERN int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder, - size_t max_dtable_size, - size_t max_blocked, + size_t hard_max_dtable_capacity, const nghttp3_mem *mem); /** @@ -1174,82 +1177,24 @@ NGHTTP3_EXTERN nghttp3_ssize nghttp3_qpack_encoder_read_decoder( /** * @function * - * `nghttp3_qpack_encoder_set_max_dtable_size` sets max dynamic table - * size to |max_dtable_size|. - * - * This function returns the number of bytes read, or one of the - * following negative error codes: - * - * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT` - * |max_dtable_size| exceeds the hard limit that decoder specifies. - */ -NGHTTP3_EXTERN int -nghttp3_qpack_encoder_set_max_dtable_size(nghttp3_qpack_encoder *encoder, - size_t max_dtable_size); - -/** - * @function - * - * `nghttp3_qpack_encoder_set_hard_max_dtable_size` sets hard maximum - * dynamic table size to |hard_max_dtable_size|. - * - * This function returns the number of bytes read, or one of the - * following negative error codes: - * - * TBD - */ -NGHTTP3_EXTERN int -nghttp3_qpack_encoder_set_hard_max_dtable_size(nghttp3_qpack_encoder *encoder, - size_t hard_max_dtable_size); - -/** - * @function - * - * `nghttp3_qpack_encoder_set_max_blocked` sets the number of streams - * which can be blocked to |max_blocked|. - * - * This function returns the number of bytes read, or one of the - * following negative error codes: - * - * TBD - */ -NGHTTP3_EXTERN int -nghttp3_qpack_encoder_set_max_blocked(nghttp3_qpack_encoder *encoder, - size_t max_blocked); - -/** - * @function - * - * `nghttp3_qpack_encoder_ack_header` tells |encoder| that header - * block for a stream denoted by |stream_id| was acknowledged by - * decoder. This function is provided for debugging purpose only. In - * HTTP/3, |encoder| knows acknowledgement of header block by reading - * decoder stream with `nghttp3_qpack_encoder_read_decoder()`. + * `nghttp3_qpack_encoder_set_max_dtable_capacity` sets max dynamic + * table capacity to |max_dtable_capacity|. If |max_dtable_capacity| is + * larger than ``hard_max_dtable_capacity`` parameter of + * `nghttp3_qpack_encoder_new`, it is truncated to the latter. */ NGHTTP3_EXTERN void -nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder, - int64_t stream_id); +nghttp3_qpack_encoder_set_max_dtable_capacity(nghttp3_qpack_encoder *encoder, + size_t max_dtable_capacity); /** * @function * - * `nghttp3_qpack_encoder_add_insert_count` increments known received - * count of |encoder| by |n|. This function is provided for debugging - * purpose only. In HTTP/3, |encoder| increments known received count - * by reading decoder stream with - * `nghttp3_qpack_encoder_read_decoder()`. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :macro:`NGHTTP3_ERR_NOMEM` - * Out of memory. - * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR` - * |n| is too large. + * `nghttp3_qpack_encoder_set_max_blocked_streams` sets the number of + * streams which can be blocked to |max_blocked_streams|. */ -NGHTTP3_EXTERN int -nghttp3_qpack_encoder_add_insert_count(nghttp3_qpack_encoder *encoder, - uint64_t n); +NGHTTP3_EXTERN void +nghttp3_qpack_encoder_set_max_blocked_streams(nghttp3_qpack_encoder *encoder, + size_t max_blocked_streams); /** * @function @@ -1265,23 +1210,11 @@ nghttp3_qpack_encoder_ack_everything(nghttp3_qpack_encoder *encoder); /** * @function * - * `nghttp3_qpack_encoder_cancel_stream` tells |encoder| that stream - * denoted by |stream_id| is cancelled. This function is provided for - * debugging purpose only. In HTTP/3, |encoder| knows this by reading - * decoder stream with `nghttp3_qpack_encoder_read_decoder()`. - */ -NGHTTP3_EXTERN void -nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder, - int64_t stream_id); - -/** - * @function - * - * `nghttp3_qpack_encoder_get_num_blocked` returns the number of - * streams which are potentially blocked at decoder side. + * `nghttp3_qpack_encoder_get_num_blocked_streams` returns the number + * of streams which are potentially blocked at decoder side. */ NGHTTP3_EXTERN size_t -nghttp3_qpack_encoder_get_num_blocked(nghttp3_qpack_encoder *encoder); +nghttp3_qpack_encoder_get_num_blocked_streams(nghttp3_qpack_encoder *encoder); /** * @struct @@ -1351,11 +1284,12 @@ typedef struct nghttp3_qpack_decoder nghttp3_qpack_decoder; * @function * * `nghttp3_qpack_decoder_new` initializes QPACK decoder. |pdecoder| - * must be non-NULL pointer. |max_dtable_size| is the maximum dynamic - * table size. |max_blocked| is the maximum number of streams which - * can be blocked. |mem| is a memory allocator. This function - * allocates memory for :type:`nghttp3_qpack_decoder` itself and - * assigns its pointer to |*pdecoder| if it succeeds. + * must be non-NULL pointer. |hard_max_dtable_capacity| is the upper + * bound of the dynamic table capacity. |max_blocked_streams| is the + * maximum number of streams which can be blocked. |mem| is a memory + * allocator. This function allocates memory for + * :type:`nghttp3_qpack_decoder` itself and assigns its pointer to + * |*pdecoder| if it succeeds. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -1364,8 +1298,8 @@ typedef struct nghttp3_qpack_decoder nghttp3_qpack_decoder; * Out of memory. */ NGHTTP3_EXTERN int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder, - size_t max_dtable_size, - size_t max_blocked, + size_t hard_max_dtable_capacity, + size_t max_blocked_streams, const nghttp3_mem *mem); /** @@ -1415,7 +1349,7 @@ nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder); * * :macro:`NGHTTP3_QPACK_DECODE_FLAG_NONE` indicates that no flag set. */ -#define NGHTTP3_QPACK_DECODE_FLAG_NONE 0 +#define NGHTTP3_QPACK_DECODE_FLAG_NONE 0x00u /** * @macro @@ -1423,7 +1357,7 @@ nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder); * :macro:`NGHTTP3_QPACK_DECODE_FLAG_EMIT` indicates that a header * field is successfully decoded. */ -#define NGHTTP3_QPACK_DECODE_FLAG_EMIT 0x01 +#define NGHTTP3_QPACK_DECODE_FLAG_EMIT 0x01u /** * @macro @@ -1431,7 +1365,7 @@ nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder); * :macro:`NGHTTP3_QPACK_DECODE_FLAG_FINAL` indicates that all header * fields have been decoded. */ -#define NGHTTP3_QPACK_DECODE_FLAG_FINAL 0x02 +#define NGHTTP3_QPACK_DECODE_FLAG_FINAL 0x02u /** * @macro @@ -1439,7 +1373,7 @@ nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder); * :macro:`NGHTTP3_QPACK_DECODE_FLAG_BLOCKED` indicates that decoding * has been blocked. */ -#define NGHTTP3_QPACK_DECODE_FLAG_BLOCKED 0x04 +#define NGHTTP3_QPACK_DECODE_FLAG_BLOCKED 0x04u /** * @function @@ -1459,12 +1393,19 @@ nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder); * due to required insert count. * * When a header field is decoded, an application receives it in |nv|. - * nv->name and nv->value are reference counted buffer, and their + * :member:`nv->name ` and :member:`nv->value + * ` are reference counted buffer, and their * reference counts are already incremented for application use. * Therefore, when application finishes processing the header field, * it must call `nghttp3_rcbuf_decref(nv->name) * ` and `nghttp3_rcbuf_decref(nv->value) - * ` or memory leak might occur. + * ` or memory leak might occur. These + * :type:`nghttp3_rcbuf` objects hold the pointer to + * :type:`nghttp3_mem` that is passed to `nghttp3_qpack_decoder_new` + * (or either `nghttp3_conn_client_new` or `nghttp3_conn_server_new` + * if it is used indirectly). As long as these objects are alive, the + * pointed :type:`nghttp3_mem` object must be available. Otherwise, + * `nghttp3_rcbuf_decref` will cause undefined behavior. * * This function returns the number of bytes read, or one of the * following negative error codes: @@ -1529,14 +1470,24 @@ nghttp3_qpack_decoder_cancel_stream(nghttp3_qpack_decoder *decoder, /** * @function * - * `nghttp3_qpack_decoder_set_dtable_cap` sets |cap| as maximum - * dynamic table size. Normally, the maximum capacity is communicated - * in encoder stream. This function is provided for debugging and - * testing purpose. + * `nghttp3_qpack_decoder_set_max_dtable_capacity` sets + * |max_dtable_capacity| as maximum dynamic table size. + * |max_dtable_capacity| must be equal to or smaller than + * ``hard_max_dtable_capacity`` parameter of + * `nghttp3_qpack_decoder_new`. Normally, the maximum capacity is + * communicated in encoder stream. This function is provided for + * debugging and testing purpose. + * + * This function returns 0 if it succeeds, or one of the + * following negative error codes: + * + * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT` + * |max_dtable_capacity| exceeds the upper bound of the dynamic + * table capacity. */ -NGHTTP3_EXTERN void -nghttp3_qpack_decoder_set_dtable_cap(nghttp3_qpack_decoder *decoder, - size_t cap); +NGHTTP3_EXTERN int +nghttp3_qpack_decoder_set_max_dtable_capacity(nghttp3_qpack_decoder *decoder, + size_t max_dtable_capacity); /** * @function @@ -1571,10 +1522,10 @@ NGHTTP3_EXTERN uint64_t nghttp3_err_infer_quic_app_error_code(int liberr); * * :type:`nghttp3_debug_vprintf_callback` is a callback function * invoked when the library outputs debug logging. The function is - * called with arguments suitable for ``vfprintf(3)`` + * called with arguments suitable for :manpage:`vfprintf(3)`. * * The debug output is only enabled if the library is built with - * ``DEBUGBUILD`` macro defined. + * :macro:`DEBUGBUILD` macro defined. */ typedef void (*nghttp3_debug_vprintf_callback)(const char *format, va_list args); @@ -1583,27 +1534,51 @@ typedef void (*nghttp3_debug_vprintf_callback)(const char *format, * @function * * `nghttp3_set_debug_vprintf_callback` sets a debug output callback - * called by the library when built with ``DEBUGBUILD`` macro defined. - * If this option is not used, debug log is written into standard - * error output. + * called by the library when built with :macro:`DEBUGBUILD` macro + * defined. If this option is not used, debug log is written into + * standard error output. * - * For builds without ``DEBUGBUILD`` macro defined, this function is - * noop. + * For builds without :macro:`DEBUGBUILD` macro defined, this function + * is noop. * - * Note that building with ``DEBUGBUILD`` may cause significant + * Note that building with :macro:`DEBUGBUILD` may cause significant * performance penalty to libnghttp3 because of extra processing. It * should be used for debugging purpose only. * * .. Warning:: * - * Building with ``DEBUGBUILD`` may cause significant performance - * penalty to libnghttp3 because of extra processing. It should be - * used for debugging purpose only. We write this two times because - * this is important. + * Building with :macro:`DEBUGBUILD` may cause significant + * performance penalty to libnghttp3 because of extra processing. + * It should be used for debugging purpose only. We write this two + * times because this is important. */ NGHTTP3_EXTERN void nghttp3_set_debug_vprintf_callback( nghttp3_debug_vprintf_callback debug_vprintf_callback); +/** + * @macrosection + * + * Shutdown related constants + */ + +/** + * @macro + * + * :macro:`NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID` specifies stream id sent + * by a server when it initiates graceful shutdown of the connection + * via `nghttp3_conn_submit_shutdown_notice`. + */ +#define NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID ((1ull << 62) - 4) + +/** + * @macro + * + * :macro:`NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID` specifies push id sent + * by a client when it initiates graceful shutdown of the connection + * via `nghttp3_conn_submit_shutdown_notice`. + */ +#define NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID ((1ull << 62) - 1) + /** * @struct * @@ -1625,7 +1600,7 @@ typedef struct nghttp3_conn nghttp3_conn; * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. */ typedef int (*nghttp3_acked_stream_data)(nghttp3_conn *conn, int64_t stream_id, - size_t datalen, void *conn_user_data, + uint64_t datalen, void *conn_user_data, void *stream_user_data); /** @@ -1711,7 +1686,7 @@ typedef int (*nghttp3_begin_headers)(nghttp3_conn *conn, int64_t stream_id, * |name| contains a field name and |value| contains a field value. * |token| is one of token defined in :type:`nghttp3_qpack_token` or * -1 if no token is defined for |name|. |flags| is bitwise OR of - * zero or more of NGHTTP3_NV_FLAG_*. + * zero or more of :macro:`NGHTTP3_NV_FLAG_* `. * * The buffers for |name| and |value| are reference counted. If * application needs to keep them, increment the reference count with @@ -1735,78 +1710,17 @@ typedef int (*nghttp3_recv_header)(nghttp3_conn *conn, int64_t stream_id, * :type:`nghttp3_end_headers` is a callback function which is invoked * when an incoming header block has ended. * + * If the stream ends with this header block, |fin| is set to nonzero. + * * The implementation of this callback must return 0 if it succeeds. * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the * caller immediately. Any values other than 0 is treated as * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. */ typedef int (*nghttp3_end_headers)(nghttp3_conn *conn, int64_t stream_id, - void *conn_user_data, + int fin, void *conn_user_data, void *stream_user_data); -/** - * @functypedef - * - * :type:`nghttp3_begin_push_promise` is a callback function which is - * invoked when an incoming header block section in PUSH_PROMISE is - * started on a stream denoted by |stream_id|. |push_id| identifies a - * push promise. Each header field is passed to application by - * :type:`nghttp3_recv_push_promise` callback. And then - * :type:`nghttp3_end_push_promise` is called when a whole header - * block is processed. - * - * The implementation of this callback must return 0 if it succeeds. - * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the - * caller immediately. Any values other than 0 is treated as - * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. - */ -typedef int (*nghttp3_begin_push_promise)(nghttp3_conn *conn, int64_t stream_id, - int64_t push_id, void *conn_user_data, - void *stream_user_data); - -/** - * @functypedef - * - * :type:`nghttp3_recv_push_promise` is a callback function which is - * invoked when a header field in PUSH_PROMISE is received on a stream - * denoted by |stream_id|. |push_id| identifies a push promise. - * |name| contains a field name and |value| contains a field value. - * |token| is one of token defined in :type:`nghttp3_qpack_token` or - * -1 if no token is defined for |name|. |flags| is bitwise OR of - * zero or more of NGHTTP3_NV_FLAG_*. - * - * The buffers for |name| and |value| are reference counted. If - * application needs to keep them, increment the reference count with - * `nghttp3_rcbuf_incref`. When they are no longer used, call - * `nghttp3_rcbuf_decref`. - * - * The implementation of this callback must return 0 if it succeeds. - * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the - * caller immediately. Any values other than 0 is treated as - * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. - */ -typedef int (*nghttp3_recv_push_promise)(nghttp3_conn *conn, int64_t stream_id, - int64_t push_id, int32_t token, - nghttp3_rcbuf *name, - nghttp3_rcbuf *value, uint8_t flags, - void *conn_user_data, - void *stream_user_data); - -/** - * @functypedef - * - * :type:`nghttp3_end_push_promise` is a callback function which is - * invoked when an incoming header block in PUSH_PROMISE has ended. - * - * The implementation of this callback must return 0 if it succeeds. - * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the - * caller immediately. Any values other than 0 is treated as - * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. - */ -typedef int (*nghttp3_end_push_promise)(nghttp3_conn *conn, int64_t stream_id, - int64_t push_id, void *conn_user_data, - void *stream_user_data); - /** * @functypedef * @@ -1827,26 +1741,7 @@ typedef int (*nghttp3_end_stream)(nghttp3_conn *conn, int64_t stream_id, /** * @functypedef * - * :type:`nghttp3_cancel_push` is a callback function which is invoked - * when the push identified by |push_id| is cancelled by remote - * endpoint. If a stream has been bound to the push ID, |stream_id| - * contains the stream ID and |stream_user_data| points to the stream - * user data. Otherwise, |stream_id| is -1 and |stream_user_data| is - * NULL. - * - * The implementation of this callback must return 0 if it succeeds. - * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the - * caller immediately. Any values other than 0 is treated as - * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. - */ -typedef int (*nghttp3_cancel_push)(nghttp3_conn *conn, int64_t push_id, - int64_t stream_id, void *conn_user_data, - void *stream_user_data); - -/** - * @functypedef - * - * :type:`nghttp3_send_stop_sending` is a callback function which is + * :type:`nghttp3_stop_sending` is a callback function which is * invoked when the library asks application to send STOP_SENDING to * the stream identified by |stream_id|. |app_error_code| indicates * the reason for this action. @@ -1856,43 +1751,61 @@ typedef int (*nghttp3_cancel_push)(nghttp3_conn *conn, int64_t push_id, * caller immediately. Any values other than 0 is treated as * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. */ -typedef int (*nghttp3_send_stop_sending)(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code, - void *conn_user_data, - void *stream_user_data); +typedef int (*nghttp3_stop_sending)(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, + void *conn_user_data, + void *stream_user_data); /** * @functypedef * - * :type:`nghttp3_push_stream` is a callback function which is invoked - * when a push stream identified by |stream_id| is opened with - * |push_id|. + * :type:`nghttp3_reset_stream` is a callback function which is + * invoked when the library asks application to reset stream + * identified by |stream_id|. |app_error_code| indicates the reason + * for this action. * * The implementation of this callback must return 0 if it succeeds. * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the * caller immediately. Any values other than 0 is treated as * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. */ -typedef int (*nghttp3_push_stream)(nghttp3_conn *conn, int64_t push_id, - int64_t stream_id, void *conn_user_data); +typedef int (*nghttp3_reset_stream)(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, + void *conn_user_data, + void *stream_user_data); /** * @functypedef * - * :type:`nghttp3_reset_stream` is a callback function which is - * invoked when the library asks application to reset stream - * identified by |stream_id|. |app_error_code| indicates the reason - * for this action. + * :type:`nghttp3_shutdown` is a callback function which is invoked + * when a shutdown is initiated by the remote endpoint. For client, + * |id| contains a stream id of a client initiated stream, for server, + * it contains a push id. All client streams with stream id or pushes + * with push id equal to or larger than |id| are guaranteed to not be + * processed by the remote endpoint. + * + * Parameter |id| for client can contain a special value + * :macro:`NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID` and for server it can + * contain special value + * :macro:`NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID`. These values signal + * request for graceful shutdown of the connection, triggered by + * remote endpoint's invocation of + * `nghttp3_conn_submit_shutdown_notice`. + * + * It is possible that this callback is invoked multiple times on a + * single connection, however the |id| can only stay the same or + * decrease, never increase. * * The implementation of this callback must return 0 if it succeeds. * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the * caller immediately. Any values other than 0 is treated as * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. */ -typedef int (*nghttp3_reset_stream)(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code, - void *conn_user_data, - void *stream_user_data); +typedef int (*nghttp3_shutdown)(nghttp3_conn *conn, int64_t id, + void *conn_user_data); + +#define NGHTTP3_CALLBACKS_VERSION_V1 1 +#define NGHTTP3_CALLBACKS_VERSION NGHTTP3_CALLBACKS_VERSION_V1 /** * @struct @@ -1953,37 +1866,11 @@ typedef struct nghttp3_callbacks { */ nghttp3_end_headers end_trailers; /** - * :member:`begin_push_promise` is a callback function which is - * invoked when a push promise has started on a particular stream. - */ - nghttp3_begin_push_promise begin_push_promise; - /** - * :member:`recv_push_promise` is a callback function which is - * invoked when a single header field in a push promise is received - * on a particular stream. - */ - nghttp3_recv_push_promise recv_push_promise; - /** - * :member:`end_push_promise` is a callback function which is - * invoked when a push promise has ended on a particular stream. - */ - nghttp3_end_push_promise end_push_promise; - /** - * :member:`cancel_push` is a callback function which is invoked - * when a push promise has been cancelled by a remote endpoint. - */ - nghttp3_cancel_push cancel_push; - /** - * :member:`send_stop_sending` is a callback function which is - * invoked when the library asks application to send STOP_SENDING to - * a particular stream. - */ - nghttp3_send_stop_sending send_stop_sending; - /** - * :member:`push_stream` is a callback function which is invoked - * when a push stream has opened. + * :member:`stop_sending` is a callback function which is invoked + * when the library asks application to send STOP_SENDING to a + * particular stream. */ - nghttp3_push_stream push_stream; + nghttp3_stop_sending stop_sending; /** * :member:`end_stream` is a callback function which is invoked when * a receiving side of stream has been closed. @@ -1995,34 +1882,53 @@ typedef struct nghttp3_callbacks { * RESET_STREAM). */ nghttp3_reset_stream reset_stream; + /** + * :member:`shutdown` is a callback function which is invoked when + * the remote endpoint has signalled initiation of connection shutdown. + */ + nghttp3_shutdown shutdown; } nghttp3_callbacks; +#define NGHTTP3_SETTINGS_VERSION_V1 1 +#define NGHTTP3_SETTINGS_VERSION NGHTTP3_SETTINGS_VERSION_V1 + /** * @struct * * :type:`nghttp3_settings` defines HTTP/3 settings. */ -typedef struct { +typedef struct nghttp3_settings { /** * :member:`max_field_section_size` specifies the maximum header * section (block) size. */ uint64_t max_field_section_size; /** - * :member:`max_pushes` specifies the maximum number of concurrent - * pushes it accepts from a remote endpoint. + * :member:`qpack_max_dtable_capacity` is the maximum size of QPACK + * dynamic table. */ - uint64_t max_pushes; + size_t qpack_max_dtable_capacity; /** - * :member:`qpack_max_table_capacity` is the maximum size of QPACK - * dynamic table. + * :member:`qpack_encoder_max_dtable_capacity` is the upper bound of + * QPACK dynamic table capacity that the QPACK encoder is willing to + * use. The effective maximum dynamic table capacity is the minimum + * of this field and the value of the received + * SETTINGS_QPACK_MAX_TABLE_CAPACITY. If this field is set to 0, + * the encoder does not use the dynamic table. */ - size_t qpack_max_table_capacity; + size_t qpack_encoder_max_dtable_capacity; /** * :member:`qpack_blocked_streams` is the maximum number of streams * which can be blocked while they are being decoded. */ size_t qpack_blocked_streams; + /** + * :member:`enable_connect_protocol`, if set to nonzero, enables + * Extended CONNECT Method (see + * https://www.ietf.org/archive/id/draft-ietf-httpbis-h3-websockets-00.html). + * Client ignores this field. + */ + int enable_connect_protocol; } nghttp3_settings; /** @@ -2030,34 +1936,51 @@ typedef struct { * * `nghttp3_settings_default` fills |settings| with the default * values. + * + * - :member:`max_field_section_size + * ` = :expr:`((1ull << 62) - 1)` + * - :member:`qpack_max_dtable_capacity + * ` = 0 + * - :member:`qpack_encoder_max_dtable_capacity + * ` = 4096 + * - :member:`qpack_blocked_streams + * ` = 0 + * - :member:`enable_connect_protocol + * ` = 0 */ -NGHTTP3_EXTERN void nghttp3_settings_default(nghttp3_settings *settings); +NGHTTP3_EXTERN void +nghttp3_settings_default_versioned(int settings_version, + nghttp3_settings *settings); /** * @function * * `nghttp3_conn_client_new` creates :type:`nghttp3_conn` and * initializes it for client use. The pointer to the object is stored - * in |*pconn|. + * in |*pconn|. If |mem| is ``NULL``, the memory allocator returned + * by `nghttp3_mem_default` is used. */ -NGHTTP3_EXTERN int nghttp3_conn_client_new(nghttp3_conn **pconn, - const nghttp3_callbacks *callbacks, - const nghttp3_settings *settings, - const nghttp3_mem *mem, - void *conn_user_data); +NGHTTP3_EXTERN int +nghttp3_conn_client_new_versioned(nghttp3_conn **pconn, int callbacks_version, + const nghttp3_callbacks *callbacks, + int settings_version, + const nghttp3_settings *settings, + const nghttp3_mem *mem, void *conn_user_data); /** * @function * * `nghttp3_conn_server_new` creates :type:`nghttp3_conn` and * initializes it for server use. The pointer to the object is stored - * in |*pconn|. + * in |*pconn|. If |mem| is ``NULL``, the memory allocator returned + * by `nghttp3_mem_default` is used. */ -NGHTTP3_EXTERN int nghttp3_conn_server_new(nghttp3_conn **pconn, - const nghttp3_callbacks *callbacks, - const nghttp3_settings *settings, - const nghttp3_mem *mem, - void *conn_user_data); +NGHTTP3_EXTERN int +nghttp3_conn_server_new_versioned(nghttp3_conn **pconn, int callbacks_version, + const nghttp3_callbacks *callbacks, + int settings_version, + const nghttp3_settings *settings, + const nghttp3_mem *mem, void *conn_user_data); /** * @function @@ -2177,8 +2100,8 @@ NGHTTP3_EXTERN int nghttp3_conn_add_ack_offset(nghttp3_conn *conn, * `nghttp3_conn_block_stream` tells the library that stream * identified by |stream_id| is blocked due to QUIC flow control. */ -NGHTTP3_EXTERN int nghttp3_conn_block_stream(nghttp3_conn *conn, - int64_t stream_id); +NGHTTP3_EXTERN void nghttp3_conn_block_stream(nghttp3_conn *conn, + int64_t stream_id); /** * @function @@ -2190,6 +2113,26 @@ NGHTTP3_EXTERN int nghttp3_conn_block_stream(nghttp3_conn *conn, NGHTTP3_EXTERN int nghttp3_conn_unblock_stream(nghttp3_conn *conn, int64_t stream_id); +/** + * @function + * + * `nghttp3_conn_is_stream_writable` returns nonzero if a stream + * identified by |stream_id| is writable. It is not writable if: + * + * - the stream does not exist; or, + * - the stream is closed (e.g., `nghttp3_conn_close_stream` is + * called); or, + * - the stream is QUIC flow control blocked (e.g., + * `nghttp3_conn_block_stream` is called); or, + * - the stream is input data blocked (e.g., + * :macro:`NGHTTP3_ERR_WOULDBLOCK` is returned from + * :type:`nghttp3_read_data_callback`); or, + * - the stream is half-closed local (e.g., + * `nghttp3_conn_shutdown_stream_write` is called). + */ +NGHTTP3_EXTERN int nghttp3_conn_is_stream_writable(nghttp3_conn *conn, + int64_t stream_id); + /** * @function * @@ -2198,8 +2141,19 @@ NGHTTP3_EXTERN int nghttp3_conn_unblock_stream(nghttp3_conn *conn, * prohibited. This works like `nghttp3_conn_block_stream`, but it * cannot be unblocked by `nghttp3_conn_unblock_stream`. */ -NGHTTP3_EXTERN int nghttp3_conn_shutdown_stream_write(nghttp3_conn *conn, - int64_t stream_id); +NGHTTP3_EXTERN void nghttp3_conn_shutdown_stream_write(nghttp3_conn *conn, + int64_t stream_id); + +/** + * @function + * + * `nghttp3_conn_shutdown_stream_read` tells the library that + * read-side of stream denoted by |stream_id| is abruptly closed and + * any further incoming data and pending stream data should be + * discarded. + */ +NGHTTP3_EXTERN int nghttp3_conn_shutdown_stream_read(nghttp3_conn *conn, + int64_t stream_id); /** * @function @@ -2215,21 +2169,21 @@ NGHTTP3_EXTERN int nghttp3_conn_resume_stream(nghttp3_conn *conn, * * `nghttp3_conn_close_stream` closes stream identified by * |stream_id|. |app_error_code| is the reason of the closure. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND` + * Stream not found. + * :macro:`NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM` + * A critical stream is closed. + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` + * User callback failed */ NGHTTP3_EXTERN int nghttp3_conn_close_stream(nghttp3_conn *conn, int64_t stream_id, uint64_t app_error_code); -/** - * @function - * - * `nghttp3_conn_reset_stream` must be called if stream identified by - * |stream_id| is reset by a remote endpoint. This is required in - * order to cancel QPACK stream. - */ -NGHTTP3_EXTERN int nghttp3_conn_reset_stream(nghttp3_conn *conn, - int64_t stream_id); - /** * @macrosection * @@ -2237,19 +2191,25 @@ NGHTTP3_EXTERN int nghttp3_conn_reset_stream(nghttp3_conn *conn, */ /** + * @macro + * * :macro:`NGHTTP3_DATA_FLAG_NONE` indicates no flag set. */ -#define NGHTTP3_DATA_FLAG_NONE 0x00 +#define NGHTTP3_DATA_FLAG_NONE 0x00u /** + * @macro + * * :macro:`NGHTTP3_DATA_FLAG_EOF` indicates that all request or * response body has been provided to the library. It also indicates * that sending side of stream is closed unless * :macro:`NGHTTP3_DATA_FLAG_NO_END_STREAM` is given at the same time. */ -#define NGHTTP3_DATA_FLAG_EOF 0x01 +#define NGHTTP3_DATA_FLAG_EOF 0x01u /** + * @macro + * * :macro:`NGHTTP3_DATA_FLAG_NO_END_STREAM` indicates that sending * side of stream is not closed even if :macro:`NGHTTP3_DATA_FLAG_EOF` * is set. Usually this flag is used to send trailer fields with @@ -2257,7 +2217,7 @@ NGHTTP3_EXTERN int nghttp3_conn_reset_stream(nghttp3_conn *conn, * `nghttp3_conn_submit_trailers()` has been called, regardless of * this flag, the submitted trailer fields are sent. */ -#define NGHTTP3_DATA_FLAG_NO_END_STREAM 0x02 +#define NGHTTP3_DATA_FLAG_NO_END_STREAM 0x02u /** * @function @@ -2320,7 +2280,7 @@ typedef nghttp3_ssize (*nghttp3_read_data_callback)( * :type:`nghttp3_data_reader` specifies the way how to generate * request or response body. */ -typedef struct { +typedef struct nghttp3_data_reader { /** * :member:`read_data` is a callback function to generate body. */ @@ -2343,24 +2303,6 @@ NGHTTP3_EXTERN int nghttp3_conn_submit_request( nghttp3_conn *conn, int64_t stream_id, const nghttp3_nv *nva, size_t nvlen, const nghttp3_data_reader *dr, void *stream_user_data); -/** - * @function - * - * `nghttp3_conn_submit_push_promise` submits push promise on the - * stream identified by |stream_id|. |stream_id| must be a client - * initiated bidirectional stream. Only server can submit push - * promise. On success, a push ID is assigned to |*ppush_id|. |nva| - * of length |nvlen| specifies HTTP request header fields. In order - * to submit HTTP response, first call - * `nghttp3_conn_bind_push_stream()` and then - * `nghttp3_conn_submit_response()`. - */ -NGHTTP3_EXTERN int nghttp3_conn_submit_push_promise(nghttp3_conn *conn, - int64_t *ppush_id, - int64_t stream_id, - const nghttp3_nv *nva, - size_t nvlen); - /** * @function * @@ -2401,35 +2343,12 @@ NGHTTP3_EXTERN int nghttp3_conn_submit_trailers(nghttp3_conn *conn, const nghttp3_nv *nva, size_t nvlen); -/** - * @function - * - * `nghttp3_conn_bind_push_stream` binds the stream identified by - * |stream_id| to the push identified by |push_id|. |stream_id| must - * be a server initiated unidirectional stream. |push_id| must be - * obtained from `nghttp3_conn_submit_push_promise()`. To send - * response to this push, call `nghttp3_conn_submit_response()`. - */ -NGHTTP3_EXTERN int nghttp3_conn_bind_push_stream(nghttp3_conn *conn, - int64_t push_id, - int64_t stream_id); - -/** - * @function - * - * `nghttp3_conn_cancel_push` cancels the push identified by - * |push_id|. - */ -NGHTTP3_EXTERN int nghttp3_conn_cancel_push(nghttp3_conn *conn, - int64_t push_id); - /** * @function * * `nghttp3_conn_submit_shutdown_notice` notifies the other endpoint - * to stop creating new stream (for server) or push (for client). - * After a couple of RTTs later, call `nghttp3_conn_shutdown` to start - * graceful shutdown. + * to stop creating new stream. After a couple of RTTs later, call + * `nghttp3_conn_shutdown` to start graceful shutdown. */ NGHTTP3_EXTERN int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn); @@ -2439,8 +2358,8 @@ NGHTTP3_EXTERN int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn); * `nghttp3_conn_shutdown` starts graceful shutdown. It should be * called after `nghttp3_conn_submit_shutdown_notice` and a couple of * RTT. After calling this function, the local endpoint starts - * rejecting new incoming streams (for server) or pushes (for client). - * The existing streams or pushes are processed normally. + * rejecting new incoming streams. The existing streams are processed + * normally. */ NGHTTP3_EXTERN int nghttp3_conn_shutdown(nghttp3_conn *conn); @@ -2459,18 +2378,23 @@ NGHTTP3_EXTERN int nghttp3_conn_set_stream_user_data(nghttp3_conn *conn, * * `nghttp3_conn_get_frame_payload_left` returns the number of bytes * left to read current frame payload for a stream denoted by - * |stream_id|. If no such stream is found, it returns - * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND`. + * |stream_id|. If no such stream is found, it returns 0. + */ +NGHTTP3_EXTERN uint64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn, + int64_t stream_id); + +/** + * @macrosection + * + * HTTP stream priority flags */ -NGHTTP3_EXTERN int64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn, - int64_t stream_id); /** * @macro * * :macro:`NGHTTP3_DEFAULT_URGENCY` is the default urgency level. */ -#define NGHTTP3_DEFAULT_URGENCY 1 +#define NGHTTP3_DEFAULT_URGENCY 3 /** * @macro @@ -2518,8 +2442,9 @@ typedef struct nghttp3_pri { * @function * * `nghttp3_conn_get_stream_priority` stores stream priority of a - * stream denoted by |stream_id| into |*dest|. Only server can use - * this function. + * stream denoted by |stream_id| into |*dest|. |stream_id| must + * identify client initiated bidirectional stream. Only server can + * use this function. * * This function must not be called if |conn| is initialized as * client. @@ -2537,15 +2462,22 @@ NGHTTP3_EXTERN int nghttp3_conn_get_stream_priority(nghttp3_conn *conn, /** * @function * - * `nghttp3_conn_set_stream_priority` updates stream priority of a - * stream denoted by |stream_id| by the value pointed by |pri|. + * `nghttp3_conn_set_stream_priority` updates priority of a stream + * denoted by |stream_id| with the value pointed by |pri|. + * |stream_id| must identify client initiated bidirectional stream. * - * This function must not be called if |conn| is initialized as - * client. + * Both client and server can update stream priority with this + * function. + * + * If server updates stream priority with this function, it completely + * overrides stream priority set by client and the attempts to update + * priority by client are ignored. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * + * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT` + * |stream_id| is not a client initiated bidirectional stream ID. * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND` * Stream not found. * :macro:`NGHTTP3_ERR_NOMEM` @@ -2572,14 +2504,14 @@ nghttp3_conn_is_remote_qpack_encoder_stream(nghttp3_conn *conn, * `nghttp3_vec_len` returns the sum of length in |vec| of |cnt| * elements. */ -NGHTTP3_EXTERN size_t nghttp3_vec_len(const nghttp3_vec *vec, size_t cnt); +NGHTTP3_EXTERN uint64_t nghttp3_vec_len(const nghttp3_vec *vec, size_t cnt); /** * @function * * `nghttp3_check_header_name` returns nonzero if HTTP header field * name |name| of length |len| is valid according to - * http://tools.ietf.org/html/rfc7230#section-3.2 + * :rfc:`7230#section-3.2`. * * Because this is a header field name in HTTP/3, the upper cased * alphabet is treated as error. @@ -2591,7 +2523,7 @@ NGHTTP3_EXTERN int nghttp3_check_header_name(const uint8_t *name, size_t len); * * `nghttp3_check_header_value` returns nonzero if HTTP header field * value |value| of length |len| is valid according to - * http://tools.ietf.org/html/rfc7230#section-3.2 + * :rfc:`7230#section-3.2`. */ NGHTTP3_EXTERN int nghttp3_check_header_value(const uint8_t *value, size_t len); @@ -2614,6 +2546,12 @@ NGHTTP3_EXTERN int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value, size_t len); +/** + * @macrosection + * + * nghttp3_info flags + */ + /** * @macro * @@ -2627,7 +2565,7 @@ NGHTTP3_EXTERN int nghttp3_http_parse_priority(nghttp3_pri *dest, * :type:`nghttp3_info` is what `nghttp3_version()` returns. It holds * information about the particular nghttp3 version. */ -typedef struct { +typedef struct nghttp3_info { /** * :member:`age` is the age of this struct. This instance of * nghttp3 sets it to :macro:`NGHTTP3_VERSION_AGE` but a future @@ -2657,7 +2595,49 @@ typedef struct { * met, this function will return a ``NULL``. Pass in 0 to skip the * version checking. */ -NGHTTP3_EXTERN nghttp3_info *nghttp3_version(int least_version); +NGHTTP3_EXTERN const nghttp3_info *nghttp3_version(int least_version); + +/** + * @function + * + * `nghttp3_err_is_fatal` returns nonzero if |liberr| is a fatal + * error. |liberr| must be one of nghttp3 library error codes (which + * is defined as NGHTTP3_ERR_* macro, such as + * :macro:`NGHTTP3_ERR_NOMEM`). + */ +NGHTTP3_EXTERN int nghttp3_err_is_fatal(int liberr); + +/* + * Versioned function wrappers + */ + +/* + * `nghttp3_settings_default` is a wrapper around + * `nghttp3_settings_default_versioned` to set the correct struct + * version. + */ +#define nghttp3_settings_default(SETTINGS) \ + nghttp3_settings_default_versioned(NGHTTP3_SETTINGS_VERSION, (SETTINGS)) + +/* + * `nghttp3_conn_client_new` is a wrapper around + * `nghttp3_conn_client_new_versioned` to set the correct struct + * version. + */ +#define nghttp3_conn_client_new(PCONN, CALLBACKS, SETTINGS, MEM, USER_DATA) \ + nghttp3_conn_client_new_versioned((PCONN), NGHTTP3_CALLBACKS_VERSION, \ + (CALLBACKS), NGHTTP3_SETTINGS_VERSION, \ + (SETTINGS), (MEM), (USER_DATA)) + +/* + * `nghttp3_conn_server_new` is a wrapper around + * `nghttp3_conn_server_new_versioned` to set the correct struct + * version. + */ +#define nghttp3_conn_server_new(PCONN, CALLBACKS, SETTINGS, MEM, USER_DATA) \ + nghttp3_conn_server_new_versioned((PCONN), NGHTTP3_CALLBACKS_VERSION, \ + (CALLBACKS), NGHTTP3_SETTINGS_VERSION, \ + (SETTINGS), (MEM), (USER_DATA)) #ifdef __cplusplus } diff --git a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h index 69d29e9f140c33..bc57eb2cfcf2d6 100644 --- a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h +++ b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h @@ -31,7 +31,7 @@ * * Version number of the nghttp3 library release. */ -#define NGHTTP3_VERSION "0.1.0-DEV" +#define NGHTTP3_VERSION "0.7.0" /** * @macro @@ -41,6 +41,6 @@ * number, 8 bits for minor and 8 bits for patch. Version 1.2.3 * becomes 0x010203. */ -#define NGHTTP3_VERSION_NUM 0x000100 +#define NGHTTP3_VERSION_NUM 0x000700 #endif /* NGHTTP3_VERSION_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_balloc.c b/deps/ngtcp2/nghttp3/lib/nghttp3_balloc.c new file mode 100644 index 00000000000000..e134d0f4dceb75 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_balloc.c @@ -0,0 +1,91 @@ +/* + * nghttp3 + * + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_balloc.h" + +#include + +#include "nghttp3_mem.h" + +void nghttp3_balloc_init(nghttp3_balloc *balloc, size_t blklen, + const nghttp3_mem *mem) { + assert((blklen & 0xfu) == 0); + + balloc->mem = mem; + balloc->blklen = blklen; + balloc->head = NULL; + nghttp3_buf_wrap_init(&balloc->buf, (void *)"", 0); +} + +void nghttp3_balloc_free(nghttp3_balloc *balloc) { + if (balloc == NULL) { + return; + } + + nghttp3_balloc_clear(balloc); +} + +void nghttp3_balloc_clear(nghttp3_balloc *balloc) { + nghttp3_memblock_hd *p, *next; + + for (p = balloc->head; p; p = next) { + next = p->next; + nghttp3_mem_free(balloc->mem, p); + } + + balloc->head = NULL; + nghttp3_buf_wrap_init(&balloc->buf, (void *)"", 0); +} + +int nghttp3_balloc_get(nghttp3_balloc *balloc, void **pbuf, size_t n) { + uint8_t *p; + nghttp3_memblock_hd *hd; + + assert(n <= balloc->blklen); + + if (nghttp3_buf_left(&balloc->buf) < n) { + p = nghttp3_mem_malloc(balloc->mem, sizeof(nghttp3_memblock_hd) + 0x10u + + balloc->blklen); + if (p == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + hd = (nghttp3_memblock_hd *)(void *)p; + hd->next = balloc->head; + balloc->head = hd; + nghttp3_buf_wrap_init( + &balloc->buf, + (uint8_t *)(((uintptr_t)p + sizeof(nghttp3_memblock_hd) + 0xfu) & + ~(uintptr_t)0xfu), + balloc->blklen); + } + + assert(((uintptr_t)balloc->buf.last & 0xfu) == 0); + + *pbuf = balloc->buf.last; + balloc->buf.last += (n + 0xfu) & ~(uintptr_t)0xfu; + + return 0; +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_balloc.h b/deps/ngtcp2/nghttp3/lib/nghttp3_balloc.h new file mode 100644 index 00000000000000..e02f61d16b5763 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_balloc.h @@ -0,0 +1,92 @@ +/* + * nghttp3 + * + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_BALLOC_H +#define NGHTTP3_BALLOC_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp3_buf.h" + +typedef struct nghttp3_memblock_hd nghttp3_memblock_hd; + +/* + * nghttp3_memblock_hd is the header of memory block. + */ +struct nghttp3_memblock_hd { + nghttp3_memblock_hd *next; +}; + +/* + * nghttp3_balloc is a custom memory allocator. It allocates |blklen| + * bytes of memory at once on demand, and returns its slice when the + * allocation is requested. + */ +typedef struct nghttp3_balloc { + /* mem is the underlying memory allocator. */ + const nghttp3_mem *mem; + /* blklen is the size of memory block. */ + size_t blklen; + /* head points to the list of memory block allocated so far. */ + nghttp3_memblock_hd *head; + /* buf wraps the current memory block for allocation requests. */ + nghttp3_buf buf; +} nghttp3_balloc; + +/* + * nghttp3_balloc_init initializes |balloc| with |blklen| which is the + * size of memory block. + */ +void nghttp3_balloc_init(nghttp3_balloc *balloc, size_t blklen, + const nghttp3_mem *mem); + +/* + * nghttp3_balloc_free releases all allocated memory blocks. + */ +void nghttp3_balloc_free(nghttp3_balloc *balloc); + +/* + * nghttp3_balloc_get allocates |n| bytes of memory and assigns its + * pointer to |*pbuf|. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_balloc_get(nghttp3_balloc *balloc, void **pbuf, size_t n); + +/* + * nghttp3_balloc_clear releases all allocated memory blocks and + * initializes its state. + */ +void nghttp3_balloc_clear(nghttp3_balloc *balloc); + +#endif /* NGHTTP3_BALLOC_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c index 2f9ce7b10ca89e..1fbb72c98af2f2 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c @@ -34,6 +34,10 @@ #include "nghttp3_conv.h" #include "nghttp3_http.h" +/* NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY is the upper bound of the + dynamic table capacity that QPACK encoder is willing to use. */ +#define NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY 4096 + /* * conn_remote_stream_uni returns nonzero if |stream_id| is remote * unidirectional stream ID. @@ -62,15 +66,16 @@ static int conn_call_begin_headers(nghttp3_conn *conn, nghttp3_stream *stream) { return 0; } -static int conn_call_end_headers(nghttp3_conn *conn, nghttp3_stream *stream) { +static int conn_call_end_headers(nghttp3_conn *conn, nghttp3_stream *stream, + int fin) { int rv; if (!conn->callbacks.end_headers) { return 0; } - rv = conn->callbacks.end_headers(conn, stream->node.nid.id, conn->user_data, - stream->user_data); + rv = conn->callbacks.end_headers(conn, stream->node.nid.id, fin, + conn->user_data, stream->user_data); if (rv != 0) { /* TODO Allow ignore headers */ return NGHTTP3_ERR_CALLBACK_FAILURE; @@ -97,52 +102,16 @@ static int conn_call_begin_trailers(nghttp3_conn *conn, return 0; } -static int conn_call_end_trailers(nghttp3_conn *conn, nghttp3_stream *stream) { +static int conn_call_end_trailers(nghttp3_conn *conn, nghttp3_stream *stream, + int fin) { int rv; if (!conn->callbacks.end_trailers) { return 0; } - rv = conn->callbacks.end_trailers(conn, stream->node.nid.id, conn->user_data, - stream->user_data); - if (rv != 0) { - /* TODO Allow ignore headers */ - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_begin_push_promise(nghttp3_conn *conn, - nghttp3_stream *stream, - int64_t push_id) { - int rv; - - if (!conn->callbacks.begin_push_promise) { - return 0; - } - - rv = conn->callbacks.begin_push_promise(conn, stream->node.nid.id, push_id, - conn->user_data, stream->user_data); - if (rv != 0) { - /* TODO Allow ignore headers */ - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_end_push_promise(nghttp3_conn *conn, - nghttp3_stream *stream, int64_t push_id) { - int rv; - - if (!conn->callbacks.end_push_promise) { - return 0; - } - - rv = conn->callbacks.end_push_promise(conn, stream->node.nid.id, push_id, - conn->user_data, stream->user_data); + rv = conn->callbacks.end_trailers(conn, stream->node.nid.id, fin, + conn->user_data, stream->user_data); if (rv != 0) { /* TODO Allow ignore headers */ return NGHTTP3_ERR_CALLBACK_FAILURE; @@ -167,36 +136,16 @@ static int conn_call_end_stream(nghttp3_conn *conn, nghttp3_stream *stream) { return 0; } -static int conn_call_cancel_push(nghttp3_conn *conn, int64_t push_id, - nghttp3_stream *stream) { - int rv; - - if (!conn->callbacks.cancel_push) { - return 0; - } - - rv = conn->callbacks.cancel_push( - conn, push_id, stream ? stream->node.nid.id : -1, conn->user_data, - stream ? stream->user_data : NULL); - if (rv != 0) { - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_send_stop_sending(nghttp3_conn *conn, - nghttp3_stream *stream, - uint64_t app_error_code) { +static int conn_call_stop_sending(nghttp3_conn *conn, nghttp3_stream *stream, + uint64_t app_error_code) { int rv; - if (!conn->callbacks.send_stop_sending) { + if (!conn->callbacks.stop_sending) { return 0; } - rv = conn->callbacks.send_stop_sending(conn, stream->node.nid.id, - app_error_code, conn->user_data, - stream->user_data); + rv = conn->callbacks.stop_sending(conn, stream->node.nid.id, app_error_code, + conn->user_data, stream->user_data); if (rv != 0) { return NGHTTP3_ERR_CALLBACK_FAILURE; } @@ -221,23 +170,6 @@ static int conn_call_reset_stream(nghttp3_conn *conn, nghttp3_stream *stream, return 0; } -static int conn_call_push_stream(nghttp3_conn *conn, int64_t push_id, - nghttp3_stream *stream) { - int rv; - - if (!conn->callbacks.push_stream) { - return 0; - } - - rv = conn->callbacks.push_stream(conn, push_id, stream->node.nid.id, - conn->user_data); - if (rv != 0) { - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - - return 0; -} - static int conn_call_deferred_consume(nghttp3_conn *conn, nghttp3_stream *stream, size_t nconsumed) { @@ -278,37 +210,40 @@ static int cycle_less(const nghttp3_pq_entry *lhsx, return rhs->cycle - lhs->cycle <= NGHTTP3_TNODE_MAX_CYCLE_GAP; } -static int conn_new(nghttp3_conn **pconn, int server, - const nghttp3_callbacks *callbacks, +static int conn_new(nghttp3_conn **pconn, int server, int callbacks_version, + const nghttp3_callbacks *callbacks, int settings_version, const nghttp3_settings *settings, const nghttp3_mem *mem, void *user_data) { int rv; nghttp3_conn *conn; size_t i; + (void)callbacks_version; + (void)settings_version; + + if (mem == NULL) { + mem = nghttp3_mem_default(); + } conn = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_conn)); if (conn == NULL) { return NGHTTP3_ERR_NOMEM; } - rv = nghttp3_map_init(&conn->streams, mem); - if (rv != 0) { - goto streams_init_fail; - } + nghttp3_objalloc_init(&conn->out_chunk_objalloc, + NGHTTP3_STREAM_MIN_CHUNK_SIZE * 16, mem); + nghttp3_objalloc_stream_init(&conn->stream_objalloc, 64, mem); - rv = nghttp3_map_init(&conn->pushes, mem); - if (rv != 0) { - goto pushes_init_fail; - } + nghttp3_map_init(&conn->streams, mem); rv = nghttp3_qpack_decoder_init(&conn->qdec, - settings->qpack_max_table_capacity, + settings->qpack_max_dtable_capacity, settings->qpack_blocked_streams, mem); if (rv != 0) { goto qdec_init_fail; } - rv = nghttp3_qpack_encoder_init(&conn->qenc, 0, 0, mem); + rv = nghttp3_qpack_encoder_init( + &conn->qenc, settings->qpack_encoder_max_dtable_capacity, mem); if (rv != 0) { goto qenc_init_fail; } @@ -319,18 +254,13 @@ static int conn_new(nghttp3_conn **pconn, int server, nghttp3_pq_init(&conn->sched[i].spq, cycle_less, mem); } - rv = nghttp3_idtr_init(&conn->remote.bidi.idtr, server, mem); - if (rv != 0) { - goto remote_bidi_idtr_init_fail; - } - - rv = nghttp3_gaptr_init(&conn->remote.uni.push_idtr, mem); - if (rv != 0) { - goto push_idtr_init_fail; - } + nghttp3_idtr_init(&conn->remote.bidi.idtr, server, mem); conn->callbacks = *callbacks; conn->local.settings = *settings; + if (!server) { + conn->local.settings.enable_connect_protocol = 0; + } nghttp3_settings_default(&conn->remote.settings); conn->mem = mem; conn->user_data = user_data; @@ -338,52 +268,50 @@ static int conn_new(nghttp3_conn **pconn, int server, conn->server = server; conn->rx.goaway_id = NGHTTP3_VARINT_MAX + 1; conn->tx.goaway_id = NGHTTP3_VARINT_MAX + 1; - conn->rx.max_stream_id_bidi = 0; - conn->rx.max_push_id = 0; + conn->rx.max_stream_id_bidi = -4; *pconn = conn; return 0; -push_idtr_init_fail: - nghttp3_idtr_free(&conn->remote.bidi.idtr); -remote_bidi_idtr_init_fail: - nghttp3_qpack_encoder_free(&conn->qenc); qenc_init_fail: nghttp3_qpack_decoder_free(&conn->qdec); qdec_init_fail: - nghttp3_map_free(&conn->pushes); -pushes_init_fail: nghttp3_map_free(&conn->streams); -streams_init_fail: + nghttp3_objalloc_free(&conn->stream_objalloc); + nghttp3_objalloc_free(&conn->out_chunk_objalloc); nghttp3_mem_free(mem, conn); return rv; } -int nghttp3_conn_client_new(nghttp3_conn **pconn, - const nghttp3_callbacks *callbacks, - const nghttp3_settings *settings, - const nghttp3_mem *mem, void *user_data) { +int nghttp3_conn_client_new_versioned(nghttp3_conn **pconn, + int callbacks_version, + const nghttp3_callbacks *callbacks, + int settings_version, + const nghttp3_settings *settings, + const nghttp3_mem *mem, void *user_data) { int rv; - rv = conn_new(pconn, /* server = */ 0, callbacks, settings, mem, user_data); + rv = conn_new(pconn, /* server = */ 0, callbacks_version, callbacks, + settings_version, settings, mem, user_data); if (rv != 0) { return rv; } - (*pconn)->remote.uni.unsent_max_pushes = settings->max_pushes; - return 0; } -int nghttp3_conn_server_new(nghttp3_conn **pconn, - const nghttp3_callbacks *callbacks, - const nghttp3_settings *settings, - const nghttp3_mem *mem, void *user_data) { +int nghttp3_conn_server_new_versioned(nghttp3_conn **pconn, + int callbacks_version, + const nghttp3_callbacks *callbacks, + int settings_version, + const nghttp3_settings *settings, + const nghttp3_mem *mem, void *user_data) { int rv; - rv = conn_new(pconn, /* server = */ 1, callbacks, settings, mem, user_data); + rv = conn_new(pconn, /* server = */ 1, callbacks_version, callbacks, + settings_version, settings, mem, user_data); if (rv != 0) { return rv; } @@ -391,17 +319,8 @@ int nghttp3_conn_server_new(nghttp3_conn **pconn, return 0; } -static int free_push_promise(nghttp3_map_entry *ent, void *ptr) { - nghttp3_push_promise *pp = nghttp3_struct_of(ent, nghttp3_push_promise, me); - const nghttp3_mem *mem = ptr; - - nghttp3_push_promise_del(pp, mem); - - return 0; -} - -static int free_stream(nghttp3_map_entry *ent, void *ptr) { - nghttp3_stream *stream = nghttp3_struct_of(ent, nghttp3_stream, me); +static int free_stream(void *data, void *ptr) { + nghttp3_stream *stream = data; (void)ptr; @@ -420,8 +339,6 @@ void nghttp3_conn_del(nghttp3_conn *conn) { nghttp3_buf_free(&conn->tx.qpack.ebuf, conn->mem); nghttp3_buf_free(&conn->tx.qpack.rbuf, conn->mem); - nghttp3_gaptr_free(&conn->remote.uni.push_idtr); - nghttp3_idtr_free(&conn->remote.bidi.idtr); for (i = 0; i < NGHTTP3_URGENCY_LEVELS; ++i) { @@ -433,15 +350,30 @@ void nghttp3_conn_del(nghttp3_conn *conn) { nghttp3_qpack_encoder_free(&conn->qenc); nghttp3_qpack_decoder_free(&conn->qdec); - nghttp3_map_each_free(&conn->pushes, free_push_promise, (void *)conn->mem); - nghttp3_map_free(&conn->pushes); - nghttp3_map_each_free(&conn->streams, free_stream, NULL); nghttp3_map_free(&conn->streams); + nghttp3_objalloc_free(&conn->stream_objalloc); + nghttp3_objalloc_free(&conn->out_chunk_objalloc); + nghttp3_mem_free(conn->mem, conn); } +static int conn_bidi_idtr_open(nghttp3_conn *conn, int64_t stream_id) { + int rv; + + rv = nghttp3_idtr_open(&conn->remote.bidi.idtr, stream_id); + if (rv != 0) { + return rv; + } + + if (nghttp3_ksl_len(&conn->remote.bidi.idtr.gap.gap) > 32) { + nghttp3_gaptr_drop_first_gap(&conn->remote.bidi.idtr.gap); + } + + return 0; +} + nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id, const uint8_t *src, size_t srclen, int fin) { @@ -455,8 +387,18 @@ nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id, /* QUIC transport ensures that this is new stream. */ if (conn->server) { if (nghttp3_client_stream_bidi(stream_id)) { - rv = nghttp3_idtr_open(&conn->remote.bidi.idtr, stream_id); - assert(rv == 0); + rv = conn_bidi_idtr_open(conn, stream_id); + if (rv != 0) { + if (nghttp3_err_is_fatal(rv)) { + return rv; + } + + /* Ignore return value. We might drop the first gap if there + are many gaps if QUIC stack allows too many holes in stream + ID space. idtr is used to decide whether PRIORITY_UPDATE + frame should be ignored or not and the frame is optional. + Ignoring them causes no harm. */ + } conn->rx.max_stream_id_bidi = nghttp3_max(conn->rx.max_stream_id_bidi, stream_id); @@ -566,12 +508,7 @@ static nghttp3_ssize conn_read_type(nghttp3_conn *conn, nghttp3_stream *stream, rstate->state = NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE; break; case NGHTTP3_STREAM_TYPE_PUSH: - if (conn->server) { - return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; - } - stream->type = NGHTTP3_STREAM_TYPE_PUSH; - rstate->state = NGHTTP3_PUSH_STREAM_STATE_PUSH_ID; - break; + return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; case NGHTTP3_STREAM_TYPE_QPACK_ENCODER: if (conn->flags & NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED) { return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; @@ -603,7 +540,6 @@ nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream, int fin) { nghttp3_ssize nread = 0; nghttp3_ssize nconsumed = 0; - size_t push_nproc; int rv; assert(srclen || fin); @@ -646,13 +582,6 @@ nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream, } nconsumed = nghttp3_conn_read_control(conn, stream, src, srclen); break; - case NGHTTP3_STREAM_TYPE_PUSH: - if (fin) { - stream->flags |= NGHTTP3_STREAM_FLAG_READ_EOF; - } - nconsumed = - nghttp3_conn_read_push(conn, &push_nproc, stream, src, srclen, fin); - break; case NGHTTP3_STREAM_TYPE_QPACK_ENCODER: if (fin) { return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM; @@ -668,8 +597,7 @@ nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream, case NGHTTP3_STREAM_TYPE_UNKNOWN: nconsumed = (nghttp3_ssize)srclen; - rv = conn_call_send_stop_sending(conn, stream, - NGHTTP3_H3_STREAM_CREATION_ERROR); + rv = conn_call_stop_sending(conn, stream, NGHTTP3_H3_STREAM_CREATION_ERROR); if (rv != 0) { return rv; } @@ -701,6 +629,8 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, size_t nconsumed = 0; int busy = 0; size_t len; + const uint8_t *pri_field_value = NULL; + size_t pri_field_valuelen = 0; assert(srclen); @@ -753,12 +683,6 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, } switch (rstate->fr.hd.type) { - case NGHTTP3_FRAME_CANCEL_PUSH: - if (rstate->left == 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - rstate->state = NGHTTP3_CTRL_STREAM_STATE_CANCEL_PUSH; - break; case NGHTTP3_FRAME_SETTINGS: /* SETTINGS frame might be empty. */ if (rstate->left == 0) { @@ -782,6 +706,19 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, } rstate->state = NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID; break; + case NGHTTP3_FRAME_PRIORITY_UPDATE: + if (!conn->server) { + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + if (rstate->left == 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + rstate->state = NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE_PRI_ELEM_ID; + break; + case NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID: + /* We do not support push */ + return NGHTTP3_ERR_H3_ID_ERROR; + case NGHTTP3_FRAME_CANCEL_PUSH: /* We do not support push */ case NGHTTP3_FRAME_DATA: case NGHTTP3_FRAME_HEADERS: case NGHTTP3_FRAME_PUSH_PROMISE: @@ -797,34 +734,6 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, break; } break; - case NGHTTP3_CTRL_STREAM_STATE_CANCEL_PUSH: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - assert(len > 0); - nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len)); - if (nread < 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - rstate->left -= nread; - if (rvint->left) { - return (nghttp3_ssize)nconsumed; - } - rstate->fr.cancel_push.push_id = rvint->acc; - nghttp3_varint_read_state_reset(rvint); - - if (conn->server) { - rv = nghttp3_conn_on_server_cancel_push(conn, &rstate->fr.cancel_push); - } else { - rv = nghttp3_conn_on_client_cancel_push(conn, &rstate->fr.cancel_push); - } - if (rv != 0) { - return rv; - } - - nghttp3_stream_read_state_reset(rstate); - break; case NGHTTP3_CTRL_STREAM_STATE_SETTINGS: for (; p != end;) { if (rstate->left == 0) { @@ -953,7 +862,7 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, return (nghttp3_ssize)nconsumed; } - if (conn->server && !nghttp3_client_stream_bidi(rvint->acc)) { + if (!conn->server && !nghttp3_client_stream_bidi(rvint->acc)) { return NGHTTP3_ERR_H3_ID_ERROR; } if (conn->rx.goaway_id < rvint->acc) { @@ -964,6 +873,14 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, conn->rx.goaway_id = rvint->acc; nghttp3_varint_read_state_reset(rvint); + if (conn->callbacks.shutdown) { + rv = + conn->callbacks.shutdown(conn, conn->rx.goaway_id, conn->user_data); + if (rv != 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + } + nghttp3_stream_read_state_reset(rstate); break; case NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID: @@ -982,13 +899,105 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, return (nghttp3_ssize)nconsumed; } - if (conn->local.uni.max_pushes > (uint64_t)rvint->acc) { + if (conn->local.uni.max_pushes > (uint64_t)rvint->acc + 1) { return NGHTTP3_ERR_H3_FRAME_ERROR; } - conn->local.uni.max_pushes = (uint64_t)rvint->acc; + conn->local.uni.max_pushes = (uint64_t)rvint->acc + 1; + nghttp3_varint_read_state_reset(rvint); + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE_PRI_ELEM_ID: + /* server side only */ + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + assert(len > 0); + nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len)); + if (nread < 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + if (rvint->left) { + return (nghttp3_ssize)nconsumed; + } + + rstate->fr.priority_update.pri_elem_id = rvint->acc; nghttp3_varint_read_state_reset(rvint); + if (rstate->left == 0) { + rstate->fr.priority_update.pri.urgency = NGHTTP3_DEFAULT_URGENCY; + rstate->fr.priority_update.pri.inc = 0; + + rv = nghttp3_conn_on_priority_update(conn, &rstate->fr.priority_update); + if (rv != 0) { + return rv; + } + + nghttp3_stream_read_state_reset(rstate); + break; + } + + rstate->state = NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE; + + /* Fall through */ + case NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE: + /* We need to buffer Priority Field Value because it might be + fragmented. */ + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + assert(len > 0); + if (conn->rx.pri_fieldbuflen == 0 && rstate->left == (int64_t)len) { + /* Everything is in the input buffer. Apply same length + limit we impose when buffering the field. */ + if (len > sizeof(conn->rx.pri_fieldbuf)) { + busy = 1; + rstate->state = NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME; + break; + } + + pri_field_value = p; + pri_field_valuelen = len; + } else if (len + conn->rx.pri_fieldbuflen > + sizeof(conn->rx.pri_fieldbuf)) { + busy = 1; + rstate->state = NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME; + break; + } else { + memcpy(conn->rx.pri_fieldbuf + conn->rx.pri_fieldbuflen, p, len); + conn->rx.pri_fieldbuflen += len; + + if (rstate->left == (int64_t)len) { + pri_field_value = conn->rx.pri_fieldbuf; + pri_field_valuelen = conn->rx.pri_fieldbuflen; + } + } + + p += len; + nconsumed += len; + rstate->left -= (int64_t)len; + + if (rstate->left) { + return (nghttp3_ssize)nconsumed; + } + + rstate->fr.priority_update.pri.urgency = NGHTTP3_DEFAULT_URGENCY; + rstate->fr.priority_update.pri.inc = 0; + + if (nghttp3_http_parse_priority(&rstate->fr.priority_update.pri, + pri_field_value, + pri_field_valuelen) != 0) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + + rv = nghttp3_conn_on_priority_update(conn, &rstate->fr.priority_update); + if (rv != 0) { + return rv; + } + + conn->rx.pri_fieldbuflen = 0; + nghttp3_stream_read_state_reset(rstate); break; case NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME: @@ -1012,365 +1021,34 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, return (nghttp3_ssize)nconsumed; } -nghttp3_ssize nghttp3_conn_read_push(nghttp3_conn *conn, size_t *pnproc, - nghttp3_stream *stream, const uint8_t *src, - size_t srclen, int fin) { - const uint8_t *p = src, *end = src ? src + srclen : src; +static int conn_delete_stream(nghttp3_conn *conn, nghttp3_stream *stream) { + int bidi = nghttp3_client_stream_bidi(stream->node.nid.id); int rv; - nghttp3_stream_read_state *rstate = &stream->rstate; - nghttp3_varint_read_state *rvint = &rstate->rvint; - nghttp3_ssize nread; - size_t nconsumed = 0; - int busy = 0; - size_t len; - int64_t push_id; - - if (stream->flags & (NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED | - NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED)) { - *pnproc = 0; - if (srclen == 0) { - return 0; - } + rv = conn_call_deferred_consume(conn, stream, + nghttp3_stream_get_buffered_datalen(stream)); + if (rv != 0) { + return rv; + } - rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); + if (bidi && conn->callbacks.stream_close) { + rv = conn->callbacks.stream_close(conn, stream->node.nid.id, + stream->error_code, conn->user_data, + stream->user_data); if (rv != 0) { - return rv; + return NGHTTP3_ERR_CALLBACK_FAILURE; } - return 0; } - for (; p != end || busy;) { - busy = 0; - switch (rstate->state) { - case NGHTTP3_PUSH_STREAM_STATE_PUSH_ID: - assert(end - p > 0); - nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin); - if (nread < 0) { - return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - if (rvint->left) { - goto almost_done; - } + rv = nghttp3_map_remove(&conn->streams, + (nghttp3_map_key_type)stream->node.nid.id); - push_id = rvint->acc; - nghttp3_varint_read_state_reset(rvint); + assert(0 == rv); - rv = nghttp3_conn_on_stream_push_id(conn, stream, push_id); - if (rv != 0) { - if (rv == NGHTTP3_ERR_IGNORE_STREAM) { - rstate->state = NGHTTP3_PUSH_STREAM_STATE_IGN_REST; - break; - } - return (nghttp3_ssize)rv; - } + nghttp3_stream_del(stream); - rstate->state = NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE; - - if (stream->flags & NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED) { - if (p != end) { - rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); - if (rv != 0) { - return rv; - } - } - *pnproc = (size_t)(p - src); - return (nghttp3_ssize)nconsumed; - } - - if (end == p) { - goto almost_done; - } - - /* Fall through */ - case NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE: - assert(end - p > 0); - nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin); - if (nread < 0) { - return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - if (rvint->left) { - goto almost_done; - } - - rstate->fr.hd.type = rvint->acc; - nghttp3_varint_read_state_reset(rvint); - rstate->state = NGHTTP3_PUSH_STREAM_STATE_FRAME_LENGTH; - if (p == end) { - goto almost_done; - } - /* Fall through */ - case NGHTTP3_PUSH_STREAM_STATE_FRAME_LENGTH: - assert(end - p > 0); - nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin); - if (nread < 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - if (rvint->left) { - goto almost_done; - } - - rstate->left = rstate->fr.hd.length = rvint->acc; - nghttp3_varint_read_state_reset(rvint); - - switch (rstate->fr.hd.type) { - case NGHTTP3_FRAME_DATA: - rv = nghttp3_stream_transit_rx_http_state( - stream, NGHTTP3_HTTP_EVENT_DATA_BEGIN); - if (rv != 0) { - return rv; - } - /* DATA frame might be empty. */ - if (rstate->left == 0) { - rv = nghttp3_stream_transit_rx_http_state( - stream, NGHTTP3_HTTP_EVENT_DATA_END); - assert(0 == rv); - - nghttp3_stream_read_state_reset(rstate); - break; - } - rstate->state = NGHTTP3_PUSH_STREAM_STATE_DATA; - break; - case NGHTTP3_FRAME_HEADERS: - rv = nghttp3_stream_transit_rx_http_state( - stream, NGHTTP3_HTTP_EVENT_HEADERS_BEGIN); - if (rv != 0) { - return rv; - } - if (rstate->left == 0) { - rv = nghttp3_stream_empty_headers_allowed(stream); - if (rv != 0) { - return rv; - } - - rv = nghttp3_stream_transit_rx_http_state( - stream, NGHTTP3_HTTP_EVENT_HEADERS_END); - assert(0 == rv); - - nghttp3_stream_read_state_reset(rstate); - break; - } - - switch (stream->rx.hstate) { - case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: - rv = conn_call_begin_headers(conn, stream); - break; - case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: - rv = conn_call_begin_trailers(conn, stream); - break; - default: - /* Unreachable */ - assert(0); - } - - if (rv != 0) { - return rv; - } - - rstate->state = NGHTTP3_PUSH_STREAM_STATE_HEADERS; - break; - case NGHTTP3_FRAME_PUSH_PROMISE: - case NGHTTP3_FRAME_CANCEL_PUSH: - case NGHTTP3_FRAME_SETTINGS: - case NGHTTP3_FRAME_GOAWAY: - case NGHTTP3_FRAME_MAX_PUSH_ID: - case NGHTTP3_H2_FRAME_PRIORITY: - case NGHTTP3_H2_FRAME_PING: - case NGHTTP3_H2_FRAME_WINDOW_UPDATE: - case NGHTTP3_H2_FRAME_CONTINUATION: - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; - default: - /* TODO Handle reserved frame type */ - busy = 1; - rstate->state = NGHTTP3_PUSH_STREAM_STATE_IGN_FRAME; - break; - } - break; - case NGHTTP3_PUSH_STREAM_STATE_DATA: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - rv = nghttp3_conn_on_data(conn, stream, p, len); - if (rv != 0) { - return rv; - } - - p += len; - rstate->left -= (int64_t)len; - - if (rstate->left) { - goto almost_done; - } - - rv = nghttp3_stream_transit_rx_http_state(stream, - NGHTTP3_HTTP_EVENT_DATA_END); - assert(0 == rv); - - nghttp3_stream_read_state_reset(rstate); - break; - case NGHTTP3_PUSH_STREAM_STATE_HEADERS: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - nread = nghttp3_conn_on_headers(conn, stream, NULL, p, len, - (int64_t)len == rstate->left); - if (nread < 0) { - return nread; - } - - p += nread; - nconsumed += (size_t)nread; - rstate->left -= nread; - - if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { - if (p != end && nghttp3_stream_get_buffered_datalen(stream) == 0) { - rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); - if (rv != 0) { - return rv; - } - } - *pnproc = (size_t)(p - src); - return (nghttp3_ssize)nconsumed; - } - - if (rstate->left) { - goto almost_done; - } - - switch (stream->rx.hstate) { - case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: - rv = nghttp3_http_on_response_headers(&stream->rx.http); - if (rv != 0) { - return rv; - } - - rv = conn_call_end_headers(conn, stream); - break; - case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: - rv = conn_call_end_trailers(conn, stream); - break; - default: - /* Unreachable */ - assert(0); - } - - if (rv != 0) { - return rv; - } - - rv = nghttp3_stream_transit_rx_http_state(stream, - NGHTTP3_HTTP_EVENT_HEADERS_END); - assert(0 == rv); - - nghttp3_stream_read_state_reset(rstate); - break; - case NGHTTP3_PUSH_STREAM_STATE_IGN_FRAME: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - p += len; - nconsumed += len; - rstate->left -= (int64_t)len; - - if (rstate->left) { - goto almost_done; - } - - nghttp3_stream_read_state_reset(rstate); - break; - case NGHTTP3_PUSH_STREAM_STATE_IGN_REST: - nconsumed += (size_t)(end - p); - *pnproc = (size_t)(p - src); - return (nghttp3_ssize)nconsumed; - } - } - -almost_done: - if (fin) { - switch (rstate->state) { - case NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE: - if (rvint->left) { - return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; - } - rv = nghttp3_stream_transit_rx_http_state(stream, - NGHTTP3_HTTP_EVENT_MSG_END); - if (rv != 0) { - return rv; - } - rv = conn_call_end_stream(conn, stream); - if (rv != 0) { - return rv; - } - break; - case NGHTTP3_PUSH_STREAM_STATE_IGN_REST: - break; - default: - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - } - - *pnproc = (size_t)(p - src); - return (nghttp3_ssize)nconsumed; -} - -static void conn_delete_push_promise(nghttp3_conn *conn, - nghttp3_push_promise *pp) { - int rv; - - rv = nghttp3_map_remove(&conn->pushes, (key_type)pp->node.nid.id); - assert(0 == rv); - - if (!conn->server && - !(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED)) { - ++conn->remote.uni.unsent_max_pushes; - } - - nghttp3_push_promise_del(pp, conn->mem); -} - -static int conn_delete_stream(nghttp3_conn *conn, nghttp3_stream *stream) { - int bidi_or_push = nghttp3_stream_bidi_or_push(stream); - int rv; - - if (bidi_or_push) { - rv = nghttp3_http_on_remote_end_stream(stream); - if (rv != 0) { - return rv; - } - } - - rv = conn_call_deferred_consume(conn, stream, - nghttp3_stream_get_buffered_datalen(stream)); - if (rv != 0) { - return rv; - } - - if (bidi_or_push && conn->callbacks.stream_close) { - rv = conn->callbacks.stream_close(conn, stream->node.nid.id, - stream->error_code, conn->user_data, - stream->user_data); - if (rv != 0) { - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - } - - rv = nghttp3_map_remove(&conn->streams, (key_type)stream->node.nid.id); - - assert(0 == rv); - - if (stream->pp) { - assert(stream->type == NGHTTP3_STREAM_TYPE_PUSH); - - conn_delete_push_promise(conn, stream->pp); - } - - nghttp3_stream_del(stream); - - return 0; -} + return 0; +} static int conn_process_blocked_stream_data(nghttp3_conn *conn, nghttp3_stream *stream) { @@ -1380,21 +1058,19 @@ static int conn_process_blocked_stream_data(nghttp3_conn *conn, int rv; size_t len; + assert(nghttp3_client_stream_bidi(stream->node.nid.id)); + for (;;) { len = nghttp3_ringbuf_len(&stream->inq); if (len == 0) { break; } + buf = nghttp3_ringbuf_get(&stream->inq, 0); - if (nghttp3_stream_uni(stream->node.nid.id)) { - nconsumed = nghttp3_conn_read_push( - conn, &nproc, stream, buf->pos, nghttp3_buf_len(buf), - len == 1 && (stream->flags & NGHTTP3_STREAM_FLAG_READ_EOF)); - } else { - nconsumed = nghttp3_conn_read_bidi( - conn, &nproc, stream, buf->pos, nghttp3_buf_len(buf), - len == 1 && (stream->flags & NGHTTP3_STREAM_FLAG_READ_EOF)); - } + + nconsumed = nghttp3_conn_read_bidi( + conn, &nproc, stream, buf->pos, nghttp3_buf_len(buf), + len == 1 && (stream->flags & NGHTTP3_STREAM_FLAG_READ_EOF)); if (nconsumed < 0) { return (int)nconsumed; } @@ -1468,8 +1144,14 @@ nghttp3_ssize nghttp3_conn_read_qpack_decoder(nghttp3_conn *conn, return nghttp3_qpack_encoder_read_decoder(&conn->qenc, src, srclen); } +static nghttp3_tnode *stream_get_sched_node(nghttp3_stream *stream) { + return &stream->node; +} + static int conn_update_stream_priority(nghttp3_conn *conn, nghttp3_stream *stream, uint8_t pri) { + assert(nghttp3_client_stream_bidi(stream->node.nid.id)); + if (stream->node.pri == pri) { return 0; } @@ -1478,8 +1160,6 @@ static int conn_update_stream_priority(nghttp3_conn *conn, stream->node.pri = pri; - assert(nghttp3_stream_bidi_or_push(stream)); - if (nghttp3_stream_require_schedule(stream)) { return nghttp3_conn_schedule_stream(conn, stream); } @@ -1498,10 +1178,12 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, size_t nconsumed = 0; int busy = 0; size_t len; - nghttp3_push_promise *pp; - nghttp3_push_promise fake_pp = {{0}, {{0}, 0, {0}, 0, 0, 0}, {0}, NULL, -1, - 0}; - nghttp3_frame_entry frent; + + if (stream->flags & NGHTTP3_STREAM_FLAG_SHUT_RD) { + *pnproc = srclen; + + return (nghttp3_ssize)srclen; + } if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { *pnproc = 0; @@ -1614,23 +1296,13 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, rstate->state = NGHTTP3_REQ_STREAM_STATE_HEADERS; break; - case NGHTTP3_FRAME_PUSH_PROMISE: - if (conn->server) { - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; - } - - if (rstate->left == 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - /* No stream->rx.hstate change with PUSH_PROMISE */ - - rstate->state = NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE_PUSH_ID; - break; + case NGHTTP3_FRAME_PUSH_PROMISE: /* We do not support push */ case NGHTTP3_FRAME_CANCEL_PUSH: case NGHTTP3_FRAME_SETTINGS: case NGHTTP3_FRAME_GOAWAY: case NGHTTP3_FRAME_MAX_PUSH_ID: + case NGHTTP3_FRAME_PRIORITY_UPDATE: + case NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID: case NGHTTP3_H2_FRAME_PRIORITY: case NGHTTP3_H2_FRAME_PING: case NGHTTP3_H2_FRAME_WINDOW_UPDATE: @@ -1662,199 +1334,15 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, nghttp3_stream_read_state_reset(rstate); break; - case NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE_PUSH_ID: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), - (int64_t)len == rstate->left); - if (nread < 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - rstate->left -= nread; - if (rvint->left) { - goto almost_done; - } - - rstate->fr.push_promise.push_id = rvint->acc; - nghttp3_varint_read_state_reset(rvint); - - if (rstate->left == 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - rv = nghttp3_conn_on_push_promise_push_id( - conn, rstate->fr.push_promise.push_id, stream); - if (rv != 0) { - if (rv == NGHTTP3_ERR_IGNORE_PUSH_PROMISE) { - rstate->state = NGHTTP3_REQ_STREAM_STATE_IGN_PUSH_PROMISE; - if (p == end) { - goto almost_done; - } - break; - } - - return rv; - } - - rstate->state = NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE; - - if (p == end) { - goto almost_done; - } - /* Fall through */ - case NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE: - pp = - nghttp3_conn_find_push_promise(conn, rstate->fr.push_promise.push_id); - - assert(pp); - - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - nread = nghttp3_conn_on_headers(conn, stream, pp, p, len, - (int64_t)len == rstate->left); - if (nread < 0) { - return nread; - } - - p += nread; - nconsumed += (size_t)nread; - rstate->left -= nread; - - if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { - if (p != end && nghttp3_stream_get_buffered_datalen(stream) == 0) { - rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); - if (rv != 0) { - return rv; - } - } - *pnproc = (size_t)(p - src); - return (nghttp3_ssize)nconsumed; - } - - if (rstate->left) { - goto almost_done; - } - - rv = nghttp3_http_on_request_headers(&pp->http); - if (rv != 0) { - return rv; - } - - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_RECVED; - - rv = conn_call_end_push_promise(conn, stream, pp->node.nid.id); - if (rv != 0) { - return rv; - } - - /* Find pp again because application might call - nghttp3_conn_cancel_push and it may delete pp. */ - pp = - nghttp3_conn_find_push_promise(conn, rstate->fr.push_promise.push_id); - if (!pp) { - nghttp3_stream_read_state_reset(rstate); - break; - } - - if (!pp->stream && (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_CANCELLED)) { - if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL) { - rv = conn_call_cancel_push(conn, pp->node.nid.id, pp->stream); - if (rv != 0) { - return rv; - } - } - - conn_delete_push_promise(conn, pp); - - nghttp3_stream_read_state_reset(rstate); - break; - } - - if (pp->stream) { - ++conn->remote.uni.unsent_max_pushes; - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED; - - if (!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL)) { - assert(pp->stream->flags & NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED); - - rv = conn_call_push_stream(conn, pp->node.nid.id, pp->stream); - if (rv != 0) { - return rv; - } - - pp->stream->flags &= - (uint16_t)~NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED; - - rv = conn_process_blocked_stream_data(conn, pp->stream); - if (rv != 0) { - return rv; - } - } - } - - nghttp3_stream_read_state_reset(rstate); - break; - case NGHTTP3_REQ_STREAM_STATE_IGN_PUSH_PROMISE: + case NGHTTP3_REQ_STREAM_STATE_HEADERS: len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - nread = nghttp3_conn_on_headers(conn, stream, &fake_pp, p, len, + nread = nghttp3_conn_on_headers(conn, stream, p, len, (int64_t)len == rstate->left); if (nread < 0) { - return nread; - } - - p += nread; - nconsumed += (size_t)nread; - rstate->left -= nread; - - if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { - if (p != end && nghttp3_stream_get_buffered_datalen(stream) == 0) { - rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); - if (rv != 0) { - return rv; - } - } - *pnproc = (size_t)(p - src); - return (nghttp3_ssize)nconsumed; - } - - if (rstate->left) { - goto almost_done; - } - - pp = - nghttp3_conn_find_push_promise(conn, rstate->fr.push_promise.push_id); - if (pp) { - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_RECVED; - - if ((conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) && - conn->tx.goaway_id <= pp->node.nid.id) { - if (pp->stream) { - rv = nghttp3_conn_reject_push_stream(conn, pp->stream); - if (rv != 0) { - return rv; - } - } else { - frent.fr.hd.type = NGHTTP3_FRAME_CANCEL_PUSH; - frent.fr.cancel_push.push_id = pp->node.nid.id; - - rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent); - if (rv != 0) { - return rv; - } - - conn_delete_push_promise(conn, pp); - } + if (nread == NGHTTP3_ERR_MALFORMED_HTTP_HEADER) { + goto http_header_error; } - } - nghttp3_stream_read_state_reset(rstate); - break; - case NGHTTP3_REQ_STREAM_STATE_HEADERS: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - nread = nghttp3_conn_on_headers(conn, stream, NULL, p, len, - (int64_t)len == rstate->left); - if (nread < 0) { return nread; } @@ -1891,9 +1379,14 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, default: /* Unreachable */ assert(0); + abort(); } if (rv != 0) { + if (rv == NGHTTP3_ERR_MALFORMED_HTTP_HEADER) { + goto http_header_error; + } + return rv; } @@ -1901,7 +1394,10 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: /* Only server utilizes priority information to schedule streams. */ - if (conn->server) { + if (conn->server && + (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_PRIORITY) && + !(stream->flags & NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED) && + !(stream->flags & NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET)) { rv = conn_update_stream_priority(conn, stream, stream->rx.http.pri); if (rv != 0) { return rv; @@ -1909,11 +1405,11 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, } /* fall through */ case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: - rv = conn_call_end_headers(conn, stream); + rv = conn_call_end_headers(conn, stream, p == end && fin); break; case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: - rv = conn_call_end_trailers(conn, stream); + rv = conn_call_end_trailers(conn, stream, p == end && fin); break; default: /* Unreachable */ @@ -1924,11 +1420,30 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, return rv; } - rv = nghttp3_stream_transit_rx_http_state(stream, - NGHTTP3_HTTP_EVENT_HEADERS_END); - assert(0 == rv); + rv = nghttp3_stream_transit_rx_http_state(stream, + NGHTTP3_HTTP_EVENT_HEADERS_END); + assert(0 == rv); + + nghttp3_stream_read_state_reset(rstate); + + break; + + http_header_error: + stream->flags |= NGHTTP3_STREAM_FLAG_HTTP_ERROR; + + busy = 1; + rstate->state = NGHTTP3_REQ_STREAM_STATE_IGN_REST; + + rv = conn_call_stop_sending(conn, stream, NGHTTP3_H3_MESSAGE_ERROR); + if (rv != 0) { + return rv; + } + + rv = conn_call_reset_stream(conn, stream, NGHTTP3_H3_MESSAGE_ERROR); + if (rv != 0) { + return rv; + } - nghttp3_stream_read_state_reset(rstate); break; case NGHTTP3_REQ_STREAM_STATE_IGN_FRAME: len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); @@ -1944,7 +1459,7 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, break; case NGHTTP3_REQ_STREAM_STATE_IGN_REST: nconsumed += (size_t)(end - p); - *pnproc = (size_t)(p - src); + *pnproc = (size_t)(end - src); return (nghttp3_ssize)nconsumed; } } @@ -1999,144 +1514,6 @@ int nghttp3_conn_on_data(nghttp3_conn *conn, nghttp3_stream *stream, return 0; } -static int push_idtr_push(nghttp3_gaptr *push_idtr, int64_t push_id) { - int rv; - - rv = nghttp3_gaptr_push(push_idtr, (uint64_t)push_id, 1); - if (rv != 0) { - return rv; - } - - /* Server SHOULD use push ID sequentially, but even if it does, we - might see gaps in push IDs because we might stop reading stream - which ignores PUSH_PROMISE. In order to limit the number of - gaps, drop earlier gaps if certain limit is reached. This makes - otherwise valid push ignored.*/ - if (nghttp3_ksl_len(&push_idtr->gap) > 100) { - nghttp3_gaptr_drop_first_gap(push_idtr); - } - - return 0; -} - -int nghttp3_conn_on_push_promise_push_id(nghttp3_conn *conn, int64_t push_id, - nghttp3_stream *stream) { - int rv; - nghttp3_gaptr *push_idtr = &conn->remote.uni.push_idtr; - nghttp3_push_promise *pp; - - if (conn->remote.uni.max_pushes <= (uint64_t)push_id) { - return NGHTTP3_ERR_H3_ID_ERROR; - } - - pp = nghttp3_conn_find_push_promise(conn, push_id); - if (pp) { - if ((pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_BOUND) || - (pp->stream_id != -1 && pp->stream_id != stream->node.nid.id)) { - return NGHTTP3_ERR_IGNORE_PUSH_PROMISE; - } - if (pp->stream) { - assert(pp->stream->flags & NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED); - /* Push unidirectional stream has already been received and - blocked */ - } else if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_CANCELLED) { - /* We will call begin_push_promise callback even if push is - cancelled */ - } else { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - assert(pp->stream_id == -1); - - pp->stream_id = stream->node.nid.id; - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_BOUND; - } else if (nghttp3_gaptr_is_pushed(push_idtr, (uint64_t)push_id, 1)) { - return NGHTTP3_ERR_IGNORE_PUSH_PROMISE; - } else { - rv = push_idtr_push(push_idtr, push_id); - if (rv != 0) { - return rv; - } - - rv = nghttp3_conn_create_push_promise(conn, &pp, push_id, &stream->node); - if (rv != 0) { - return rv; - } - } - - conn->rx.max_push_id = nghttp3_max(conn->rx.max_push_id, push_id); - - if ((conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) && - conn->tx.goaway_id <= push_id) { - return NGHTTP3_ERR_IGNORE_PUSH_PROMISE; - } - - rv = conn_call_begin_push_promise(conn, stream, push_id); - if (rv != 0) { - return rv; - } - - return 0; -} - -int nghttp3_conn_on_client_cancel_push(nghttp3_conn *conn, - const nghttp3_frame_cancel_push *fr) { - nghttp3_push_promise *pp; - nghttp3_gaptr *push_idtr = &conn->remote.uni.push_idtr; - int rv; - - if (conn->remote.uni.max_pushes <= (uint64_t)fr->push_id) { - return NGHTTP3_ERR_H3_ID_ERROR; - } - - pp = nghttp3_conn_find_push_promise(conn, fr->push_id); - if (pp == NULL) { - if (nghttp3_gaptr_is_pushed(push_idtr, (uint64_t)fr->push_id, 1)) { - /* push is already cancelled or server is misbehaving */ - return 0; - } - - /* We have not received PUSH_PROMISE yet */ - rv = push_idtr_push(push_idtr, fr->push_id); - if (rv != 0) { - return rv; - } - - conn->rx.max_push_id = nghttp3_max(conn->rx.max_push_id, fr->push_id); - - rv = nghttp3_conn_create_push_promise(conn, &pp, fr->push_id, NULL); - if (rv != 0) { - return rv; - } - - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL; - - /* cancel_push callback will be called after PUSH_PROMISE frame is - completely received. */ - - return 0; - } - - if (pp->stream) { - return 0; - } - - if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED) { - rv = conn_call_cancel_push(conn, pp->node.nid.id, pp->stream); - if (rv != 0) { - return rv; - } - - conn_delete_push_promise(conn, pp); - - return 0; - } - - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL; - - return 0; -} - static nghttp3_pq *conn_get_sched_pq(nghttp3_conn *conn, nghttp3_tnode *tnode) { uint32_t urgency = nghttp3_pri_uint8_urgency(tnode->pri); @@ -2145,121 +1522,8 @@ static nghttp3_pq *conn_get_sched_pq(nghttp3_conn *conn, nghttp3_tnode *tnode) { return &conn->sched[urgency].spq; } -int nghttp3_conn_on_server_cancel_push(nghttp3_conn *conn, - const nghttp3_frame_cancel_push *fr) { - nghttp3_push_promise *pp; - nghttp3_stream *stream; - int rv; - - if (conn->local.uni.next_push_id <= fr->push_id) { - return NGHTTP3_ERR_H3_ID_ERROR; - } - - pp = nghttp3_conn_find_push_promise(conn, fr->push_id); - if (pp == NULL) { - return 0; - } - - stream = pp->stream; - - rv = conn_call_cancel_push(conn, fr->push_id, stream); - if (rv != 0) { - return rv; - } - - if (stream) { - rv = nghttp3_conn_close_stream(conn, stream->node.nid.id, - NGHTTP3_H3_REQUEST_CANCELLED); - if (rv != 0) { - assert(NGHTTP3_ERR_STREAM_NOT_FOUND != rv); - return rv; - } - return 0; - } - - nghttp3_tnode_unschedule(&pp->node, conn_get_sched_pq(conn, &pp->node)); - - conn_delete_push_promise(conn, pp); - - return 0; -} - -int nghttp3_conn_on_stream_push_id(nghttp3_conn *conn, nghttp3_stream *stream, - int64_t push_id) { - nghttp3_push_promise *pp; - int rv; - - if (nghttp3_gaptr_is_pushed(&conn->remote.uni.push_idtr, (uint64_t)push_id, - 1)) { - pp = nghttp3_conn_find_push_promise(conn, push_id); - if (pp) { - if (pp->stream) { - return NGHTTP3_ERR_H3_ID_ERROR; - } - pp->stream = stream; - stream->pp = pp; - - assert(!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL)); - - if ((conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) && - conn->tx.goaway_id <= push_id) { - rv = nghttp3_conn_reject_push_stream(conn, stream); - if (rv != 0) { - return rv; - } - return NGHTTP3_ERR_IGNORE_STREAM; - } - - if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED) { - ++conn->remote.uni.unsent_max_pushes; - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED; - - return conn_call_push_stream(conn, push_id, stream); - } - - stream->flags |= NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED; - - return 0; - } - - /* Push ID has been received, but pp is gone. This means that - push is cancelled or server is misbehaving. We have no - information to distinguish the two, so just cancel QPACK stream - just in case, and ask application to send STOP_SENDING and - ignore all frames in this stream. */ - rv = nghttp3_conn_cancel_push_stream(conn, stream); - if (rv != 0) { - return rv; - } - return NGHTTP3_ERR_IGNORE_STREAM; - } - - if (conn->remote.uni.max_pushes <= (uint64_t)push_id) { - return NGHTTP3_ERR_H3_ID_ERROR; - } - - rv = push_idtr_push(&conn->remote.uni.push_idtr, push_id); - if (rv != 0) { - return rv; - } - - /* Don't know the associated stream of PUSH_PROMISE. It doesn't - matter because client sends nothing to this stream. */ - rv = nghttp3_conn_create_push_promise(conn, &pp, push_id, NULL); - if (rv != 0) { - return rv; - } - - pp->stream = stream; - stream->pp = pp; - stream->flags |= NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED; - - return 0; -} - static nghttp3_ssize conn_decode_headers(nghttp3_conn *conn, nghttp3_stream *stream, - nghttp3_push_promise *pp, const uint8_t *src, size_t srclen, int fin) { nghttp3_ssize nread; @@ -2272,37 +1536,26 @@ static nghttp3_ssize conn_decode_headers(nghttp3_conn *conn, nghttp3_http_state *http; int request = 0; int trailers = 0; - int ignore_pp = 0; - if (pp) { + switch (stream->rx.hstate) { + case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: request = 1; - ignore_pp = pp->stream_id != stream->node.nid.id; - if (ignore_pp) { - http = NULL; - } else { - http = &pp->http; - } - } else { - switch (stream->rx.hstate) { - case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: - request = 1; - /* Fall through */ - case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: - recv_header = conn->callbacks.recv_header; - break; - case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: - request = 1; - /* Fall through */ - case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: - trailers = 1; - recv_header = conn->callbacks.recv_trailer; - break; - default: - /* Unreachable */ - assert(0); - } - http = &stream->rx.http; + /* Fall through */ + case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: + recv_header = conn->callbacks.recv_header; + break; + case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: + request = 1; + /* Fall through */ + case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: + trailers = 1; + recv_header = conn->callbacks.recv_trailer; + break; + default: + /* Unreachable */ + assert(0); } + http = &stream->rx.http; nghttp3_buf_wrap_init(&buf, (uint8_t *)src, srclen); buf.last = buf.end; @@ -2342,17 +1595,9 @@ static nghttp3_ssize conn_decode_headers(nghttp3_conn *conn, } if (flags & NGHTTP3_QPACK_DECODE_FLAG_EMIT) { - if (ignore_pp) { - nghttp3_rcbuf_decref(nv.name); - nghttp3_rcbuf_decref(nv.value); - - continue; - } - - assert(http); - - rv = nghttp3_http_on_header(http, stream->rstate.fr.hd.type, &nv, request, - trailers); + rv = nghttp3_http_on_header( + http, &nv, request, trailers, + conn->server && conn->local.settings.enable_connect_protocol); switch (rv) { case NGHTTP3_ERR_MALFORMED_HTTP_HEADER: break; @@ -2360,18 +1605,13 @@ static nghttp3_ssize conn_decode_headers(nghttp3_conn *conn, rv = 0; break; case 0: - if (pp) { - if (conn->callbacks.recv_push_promise) { - rv = conn->callbacks.recv_push_promise( - conn, stream->node.nid.id, pp->node.nid.id, nv.token, nv.name, - nv.value, nv.flags, conn->user_data, stream->user_data); - } - break; - } if (recv_header) { rv = recv_header(conn, stream->node.nid.id, nv.token, nv.name, nv.value, nv.flags, conn->user_data, stream->user_data); + if (rv != 0) { + rv = NGHTTP3_ERR_CALLBACK_FAILURE; + } } break; default: @@ -2393,23 +1633,19 @@ static nghttp3_ssize conn_decode_headers(nghttp3_conn *conn, nghttp3_ssize nghttp3_conn_on_headers(nghttp3_conn *conn, nghttp3_stream *stream, - nghttp3_push_promise *pp, const uint8_t *src, size_t srclen, int fin) { if (srclen == 0 && !fin) { return 0; } - return conn_decode_headers(conn, stream, pp, src, srclen, fin); + return conn_decode_headers(conn, stream, src, srclen, fin); } int nghttp3_conn_on_settings_entry_received(nghttp3_conn *conn, const nghttp3_frame_settings *fr) { const nghttp3_settings_entry *ent = &fr->iv[0]; nghttp3_settings *dest = &conn->remote.settings; - int rv; - size_t max_table_capacity = SIZE_MAX; - size_t max_blocked_streams = SIZE_MAX; /* TODO Check for duplicates */ switch (ent->id) { @@ -2417,29 +1653,44 @@ int nghttp3_conn_on_settings_entry_received(nghttp3_conn *conn, dest->max_field_section_size = ent->value; break; case NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY: - dest->qpack_max_table_capacity = (size_t)ent->value; - max_table_capacity = - nghttp3_min(max_table_capacity, dest->qpack_max_table_capacity); - rv = nghttp3_qpack_encoder_set_hard_max_dtable_size(&conn->qenc, - max_table_capacity); - if (rv != 0) { - return rv; + if (dest->qpack_max_dtable_capacity != 0) { + return NGHTTP3_ERR_H3_SETTINGS_ERROR; } - rv = nghttp3_qpack_encoder_set_max_dtable_size(&conn->qenc, - max_table_capacity); - if (rv != 0) { - return rv; + + if (ent->value == 0) { + break; } + + dest->qpack_max_dtable_capacity = (size_t)ent->value; + + nghttp3_qpack_encoder_set_max_dtable_capacity(&conn->qenc, + (size_t)ent->value); break; case NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS: + if (dest->qpack_blocked_streams != 0) { + return NGHTTP3_ERR_H3_SETTINGS_ERROR; + } + + if (ent->value == 0) { + break; + } + dest->qpack_blocked_streams = (size_t)ent->value; - max_blocked_streams = - nghttp3_min(max_blocked_streams, dest->qpack_blocked_streams); - rv = - nghttp3_qpack_encoder_set_max_blocked(&conn->qenc, max_blocked_streams); - if (rv != 0) { - return rv; + + nghttp3_qpack_encoder_set_max_blocked_streams( + &conn->qenc, (size_t)nghttp3_min(100, ent->value)); + break; + case NGHTTP3_SETTINGS_ID_ENABLE_CONNECT_PROTOCOL: + if (!conn->server) { + break; + } + if (ent->value != 0 && ent->value != 1) { + return NGHTTP3_ERR_H3_SETTINGS_ERROR; + } + if (ent->value == 0 && dest->enable_connect_protocol) { + return NGHTTP3_ERR_H3_SETTINGS_ERROR; } + dest->enable_connect_protocol = (int)ent->value; break; case NGHTTP3_H2_SETTINGS_ID_ENABLE_PUSH: case NGHTTP3_H2_SETTINGS_ID_MAX_CONCURRENT_STREAMS: @@ -2454,8 +1705,71 @@ int nghttp3_conn_on_settings_entry_received(nghttp3_conn *conn, return 0; } +static int +conn_on_priority_update_stream(nghttp3_conn *conn, + const nghttp3_frame_priority_update *fr) { + int64_t stream_id = fr->pri_elem_id; + nghttp3_stream *stream; + int rv; + + if (!nghttp3_client_stream_bidi(stream_id) || + nghttp3_ord_stream_id(stream_id) > conn->remote.bidi.max_client_streams) { + return NGHTTP3_ERR_H3_ID_ERROR; + } + + stream = nghttp3_conn_find_stream(conn, stream_id); + if (stream == NULL) { + if ((conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) && + conn->tx.goaway_id <= stream_id) { + /* Connection is going down. Ignore priority signal. */ + return 0; + } + + rv = conn_bidi_idtr_open(conn, stream_id); + if (rv != 0) { + if (nghttp3_err_is_fatal(rv)) { + return rv; + } + + assert(rv == NGHTTP3_ERR_STREAM_IN_USE); + + /* The stream is gone. Just ignore. */ + return 0; + } + + conn->rx.max_stream_id_bidi = + nghttp3_max(conn->rx.max_stream_id_bidi, stream_id); + rv = nghttp3_conn_create_stream(conn, &stream, stream_id); + if (rv != 0) { + return rv; + } + + stream->node.pri = nghttp3_pri_to_uint8(&fr->pri); + stream->flags |= NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED; + + return 0; + } + + if (stream->flags & NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET) { + return 0; + } + + stream->flags |= NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED; + + return conn_update_stream_priority(conn, stream, + nghttp3_pri_to_uint8(&fr->pri)); +} + +int nghttp3_conn_on_priority_update(nghttp3_conn *conn, + const nghttp3_frame_priority_update *fr) { + assert(conn->server); + assert(fr->hd.type == NGHTTP3_FRAME_PRIORITY_UPDATE); + + return conn_on_priority_update_stream(conn, fr); +} + static int conn_stream_acked_data(nghttp3_stream *stream, int64_t stream_id, - size_t datalen, void *user_data) { + uint64_t datalen, void *user_data) { nghttp3_conn *conn = stream->conn; int rv; @@ -2477,76 +1791,34 @@ int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream, nghttp3_stream *stream; int rv; nghttp3_stream_callbacks callbacks = { - conn_stream_acked_data, - }; - - rv = nghttp3_stream_new(&stream, stream_id, conn->next_seq, &callbacks, - conn->mem); - if (rv != 0) { - return rv; - } - - stream->conn = conn; - - rv = nghttp3_map_insert(&conn->streams, &stream->me); - if (rv != 0) { - nghttp3_stream_del(stream); - return rv; - } - - ++conn->next_seq; - *pstream = stream; - - return 0; -} - -int nghttp3_conn_create_push_promise(nghttp3_conn *conn, - nghttp3_push_promise **ppp, - int64_t push_id, - nghttp3_tnode *assoc_tnode) { - nghttp3_push_promise *pp; - int rv; + conn_stream_acked_data, + }; - rv = nghttp3_push_promise_new(&pp, push_id, conn->next_seq, assoc_tnode, - conn->mem); + rv = nghttp3_stream_new(&stream, stream_id, conn->next_seq, &callbacks, + &conn->out_chunk_objalloc, &conn->stream_objalloc, + conn->mem); if (rv != 0) { return rv; } - rv = nghttp3_map_insert(&conn->pushes, &pp->me); + stream->conn = conn; + + rv = nghttp3_map_insert(&conn->streams, + (nghttp3_map_key_type)stream->node.nid.id, stream); if (rv != 0) { - nghttp3_push_promise_del(pp, conn->mem); + nghttp3_stream_del(stream); return rv; } ++conn->next_seq; - *ppp = pp; + *pstream = stream; return 0; } nghttp3_stream *nghttp3_conn_find_stream(nghttp3_conn *conn, int64_t stream_id) { - nghttp3_map_entry *me; - - me = nghttp3_map_find(&conn->streams, (key_type)stream_id); - if (me == NULL) { - return NULL; - } - - return nghttp3_struct_of(me, nghttp3_stream, me); -} - -nghttp3_push_promise *nghttp3_conn_find_push_promise(nghttp3_conn *conn, - int64_t push_id) { - nghttp3_map_entry *me; - - me = nghttp3_map_find(&conn->pushes, (key_type)push_id); - if (me == NULL) { - return NULL; - } - - return nghttp3_struct_of(me, nghttp3_push_promise, me); + return nghttp3_map_find(&conn->streams, (nghttp3_map_key_type)stream_id); } int nghttp3_conn_bind_control_stream(nghttp3_conn *conn, int64_t stream_id) { @@ -2680,15 +1952,6 @@ nghttp3_ssize nghttp3_conn_writev_stream(nghttp3_conn *conn, } if (conn->tx.ctrl && !nghttp3_stream_is_blocked(conn->tx.ctrl)) { - if (!(conn->flags & NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED) && - !(conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) && - conn->remote.uni.unsent_max_pushes > conn->remote.uni.max_pushes) { - rv = nghttp3_conn_submit_max_push_id(conn); - if (rv != 0) { - return rv; - } - } - ncnt = conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, conn->tx.ctrl); if (ncnt) { @@ -2727,7 +1990,7 @@ nghttp3_ssize nghttp3_conn_writev_stream(nghttp3_conn *conn, return ncnt; } - if (nghttp3_stream_bidi_or_push(stream) && + if (nghttp3_client_stream_bidi(stream->node.nid.id) && !nghttp3_stream_require_schedule(stream)) { nghttp3_conn_unschedule_stream(conn, stream); } @@ -2748,10 +2011,6 @@ nghttp3_stream *nghttp3_conn_get_next_tx_stream(nghttp3_conn *conn) { tnode = nghttp3_struct_of(nghttp3_pq_top(pq), nghttp3_tnode, pe); - if (tnode->nid.type == NGHTTP3_NODE_ID_TYPE_PUSH) { - return nghttp3_struct_of(tnode, nghttp3_push_promise, node)->stream; - } - return nghttp3_struct_of(tnode, nghttp3_stream, node); } @@ -2764,7 +2023,7 @@ int nghttp3_conn_add_write_offset(nghttp3_conn *conn, int64_t stream_id, int rv; if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; + return 0; } rv = nghttp3_stream_add_outq_offset(stream, n); @@ -2774,7 +2033,7 @@ int nghttp3_conn_add_write_offset(nghttp3_conn *conn, int64_t stream_id, stream->unscheduled_nwrite += n; - if (!nghttp3_stream_bidi_or_push(stream)) { + if (!nghttp3_client_stream_bidi(stream->node.nid.id)) { return 0; } @@ -2795,7 +2054,7 @@ int nghttp3_conn_add_ack_offset(nghttp3_conn *conn, int64_t stream_id, nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; + return 0; } return nghttp3_stream_add_ack_offset(stream, n); @@ -2840,21 +2099,12 @@ static int conn_submit_headers_data(nghttp3_conn *conn, nghttp3_stream *stream, return 0; } -static nghttp3_tnode *stream_get_dependency_node(nghttp3_stream *stream) { - if (stream->pp) { - assert(stream->type == NGHTTP3_STREAM_TYPE_PUSH); - return &stream->pp->node; - } - - return &stream->node; -} - int nghttp3_conn_schedule_stream(nghttp3_conn *conn, nghttp3_stream *stream) { /* Assume that stream stays on the same urgency level */ + nghttp3_tnode *node = stream_get_sched_node(stream); int rv; - rv = nghttp3_tnode_schedule(stream_get_dependency_node(stream), - conn_get_sched_pq(conn, &stream->node), + rv = nghttp3_tnode_schedule(node, conn_get_sched_pq(conn, node), stream->unscheduled_nwrite); if (rv != 0) { return rv; @@ -2867,7 +2117,7 @@ int nghttp3_conn_schedule_stream(nghttp3_conn *conn, nghttp3_stream *stream) { int nghttp3_conn_ensure_stream_scheduled(nghttp3_conn *conn, nghttp3_stream *stream) { - if (nghttp3_tnode_is_scheduled(stream_get_dependency_node(stream))) { + if (nghttp3_tnode_is_scheduled(stream_get_sched_node(stream))) { return 0; } @@ -2876,8 +2126,9 @@ int nghttp3_conn_ensure_stream_scheduled(nghttp3_conn *conn, void nghttp3_conn_unschedule_stream(nghttp3_conn *conn, nghttp3_stream *stream) { - nghttp3_tnode_unschedule(stream_get_dependency_node(stream), - conn_get_sched_pq(conn, &stream->node)); + nghttp3_tnode *node = stream_get_sched_node(stream); + + nghttp3_tnode_unschedule(node, conn_get_sched_pq(conn, node)); } int nghttp3_conn_submit_request(nghttp3_conn *conn, int64_t stream_id, @@ -2983,184 +2234,6 @@ int nghttp3_conn_submit_trailers(nghttp3_conn *conn, int64_t stream_id, return conn_submit_headers_data(conn, stream, nva, nvlen, NULL); } -int nghttp3_conn_submit_push_promise(nghttp3_conn *conn, int64_t *ppush_id, - int64_t stream_id, const nghttp3_nv *nva, - size_t nvlen) { - nghttp3_stream *stream; - int rv; - nghttp3_nv *nnva; - nghttp3_frame_entry frent; - int64_t push_id; - nghttp3_push_promise *pp; - - assert(conn->server); - assert(conn->tx.qenc); - assert(nghttp3_client_stream_bidi(stream_id)); - - if (conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_RECVED) { - return NGHTTP3_ERR_CONN_CLOSING; - } - - stream = nghttp3_conn_find_stream(conn, stream_id); - if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; - } - - if (conn->local.uni.max_pushes <= (uint64_t)conn->local.uni.next_push_id) { - return NGHTTP3_ERR_PUSH_ID_BLOCKED; - } - - push_id = conn->local.uni.next_push_id; - - rv = nghttp3_conn_create_push_promise(conn, &pp, push_id, &stream->node); - if (rv != 0) { - return rv; - } - - ++conn->local.uni.next_push_id; - - rv = nghttp3_nva_copy(&nnva, nva, nvlen, conn->mem); - if (rv != 0) { - return rv; - } - - frent.fr.hd.type = NGHTTP3_FRAME_PUSH_PROMISE; - frent.fr.push_promise.push_id = push_id; - frent.fr.push_promise.nva = nnva; - frent.fr.push_promise.nvlen = nvlen; - - rv = nghttp3_stream_frq_add(stream, &frent); - if (rv != 0) { - nghttp3_nva_del(nnva, conn->mem); - return rv; - } - - *ppush_id = push_id; - - if (nghttp3_stream_require_schedule(stream)) { - return nghttp3_conn_schedule_stream(conn, stream); - } - - return 0; -} - -int nghttp3_conn_bind_push_stream(nghttp3_conn *conn, int64_t push_id, - int64_t stream_id) { - nghttp3_push_promise *pp; - nghttp3_stream *stream; - int rv; - - assert(conn->server); - assert(nghttp3_server_stream_uni(stream_id)); - - pp = nghttp3_conn_find_push_promise(conn, push_id); - if (pp == NULL) { - return NGHTTP3_ERR_INVALID_ARGUMENT; - } - - assert(NULL == nghttp3_conn_find_stream(conn, stream_id)); - - rv = nghttp3_conn_create_stream(conn, &stream, stream_id); - if (rv != 0) { - return rv; - } - - stream->type = NGHTTP3_STREAM_TYPE_PUSH; - stream->pp = pp; - - pp->stream = stream; - - rv = nghttp3_stream_write_stream_type_push_id(stream); - if (rv != 0) { - return rv; - } - - return 0; -} - -int nghttp3_conn_cancel_push(nghttp3_conn *conn, int64_t push_id) { - if (conn->server) { - return nghttp3_conn_server_cancel_push(conn, push_id); - } - return nghttp3_conn_client_cancel_push(conn, push_id); -} - -int nghttp3_conn_server_cancel_push(nghttp3_conn *conn, int64_t push_id) { - nghttp3_push_promise *pp; - nghttp3_frame_entry frent; - int rv; - - assert(conn->tx.ctrl); - - pp = nghttp3_conn_find_push_promise(conn, push_id); - if (pp == NULL) { - return NGHTTP3_ERR_INVALID_ARGUMENT; - } - - if (!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL)) { - frent.fr.hd.type = NGHTTP3_FRAME_CANCEL_PUSH; - frent.fr.cancel_push.push_id = push_id; - - rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent); - if (rv != 0) { - return rv; - } - - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL; - } - - if (pp->stream) { - /* CANCEL_PUSH will be sent, but it does not affect pushed stream. - Stream should be explicitly cancelled. */ - rv = conn_call_reset_stream(conn, pp->stream, NGHTTP3_H3_REQUEST_CANCELLED); - if (rv != 0) { - return rv; - } - } - - nghttp3_tnode_unschedule(&pp->node, conn_get_sched_pq(conn, &pp->node)); - - conn_delete_push_promise(conn, pp); - - return 0; -} - -int nghttp3_conn_client_cancel_push(nghttp3_conn *conn, int64_t push_id) { - nghttp3_push_promise *pp; - nghttp3_frame_entry frent; - int rv; - - pp = nghttp3_conn_find_push_promise(conn, push_id); - if (pp == NULL) { - return NGHTTP3_ERR_INVALID_ARGUMENT; - } - - if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL) { - return 0; - } - - if (!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED)) { - return NGHTTP3_ERR_INVALID_STATE; - } - - if (pp->stream) { - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL; - return nghttp3_conn_cancel_push_stream(conn, pp->stream); - } - - frent.fr.hd.type = NGHTTP3_FRAME_CANCEL_PUSH; - frent.fr.cancel_push.push_id = push_id; - - rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent); - if (rv != 0) { - return rv; - } - - conn_delete_push_promise(conn, pp); - - return 0; -} - int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn) { nghttp3_frame_entry frent; int rv; @@ -3168,7 +2241,8 @@ int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn) { assert(conn->tx.ctrl); frent.fr.hd.type = NGHTTP3_FRAME_GOAWAY; - frent.fr.goaway.id = conn->server ? (1ull << 62) - 4 : (1ull << 62) - 1; + frent.fr.goaway.id = conn->server ? NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID + : NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID; assert(frent.fr.goaway.id <= conn->tx.goaway_id); @@ -3194,7 +2268,7 @@ int nghttp3_conn_shutdown(nghttp3_conn *conn) { frent.fr.goaway.id = nghttp3_min((1ll << 62) - 4, conn->rx.max_stream_id_bidi + 4); } else { - frent.fr.goaway.id = nghttp3_min((1ll << 62) - 1, conn->rx.max_push_id + 1); + frent.fr.goaway.id = 0; } assert(frent.fr.goaway.id <= conn->tx.goaway_id); @@ -3213,12 +2287,7 @@ int nghttp3_conn_shutdown(nghttp3_conn *conn) { int nghttp3_conn_reject_stream(nghttp3_conn *conn, nghttp3_stream *stream) { int rv; - rv = nghttp3_qpack_decoder_cancel_stream(&conn->qdec, stream->node.nid.id); - if (rv != 0) { - return rv; - } - - rv = conn_call_send_stop_sending(conn, stream, NGHTTP3_H3_REQUEST_REJECTED); + rv = conn_call_stop_sending(conn, stream, NGHTTP3_H3_REQUEST_REJECTED); if (rv != 0) { return rv; } @@ -3226,74 +2295,46 @@ int nghttp3_conn_reject_stream(nghttp3_conn *conn, nghttp3_stream *stream) { return conn_call_reset_stream(conn, stream, NGHTTP3_H3_REQUEST_REJECTED); } -static int conn_reject_push_stream(nghttp3_conn *conn, nghttp3_stream *stream, - uint64_t app_error_code) { - int rv; - - /* TODO Send Stream Cancellation if we have not processed all - incoming stream data up to fin */ - rv = nghttp3_qpack_decoder_cancel_stream(&conn->qdec, stream->node.nid.id); - if (rv != 0) { - return rv; - } - - return conn_call_send_stop_sending(conn, stream, app_error_code); -} - -int nghttp3_conn_reject_push_stream(nghttp3_conn *conn, - nghttp3_stream *stream) { - return conn_reject_push_stream(conn, stream, NGHTTP3_H3_REQUEST_REJECTED); -} - -int nghttp3_conn_cancel_push_stream(nghttp3_conn *conn, - nghttp3_stream *stream) { - return conn_reject_push_stream(conn, stream, NGHTTP3_H3_REQUEST_CANCELLED); -} - -int nghttp3_conn_block_stream(nghttp3_conn *conn, int64_t stream_id) { +void nghttp3_conn_block_stream(nghttp3_conn *conn, int64_t stream_id) { nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; + return; } stream->flags |= NGHTTP3_STREAM_FLAG_FC_BLOCKED; stream->unscheduled_nwrite = 0; - if (nghttp3_stream_bidi_or_push(stream)) { + if (nghttp3_client_stream_bidi(stream->node.nid.id)) { nghttp3_conn_unschedule_stream(conn, stream); } - - return 0; } -int nghttp3_conn_shutdown_stream_write(nghttp3_conn *conn, int64_t stream_id) { +void nghttp3_conn_shutdown_stream_write(nghttp3_conn *conn, int64_t stream_id) { nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; + return; } stream->flags |= NGHTTP3_STREAM_FLAG_SHUT_WR; stream->unscheduled_nwrite = 0; - if (nghttp3_stream_bidi_or_push(stream)) { + if (nghttp3_client_stream_bidi(stream->node.nid.id)) { nghttp3_conn_unschedule_stream(conn, stream); } - - return 0; } int nghttp3_conn_unblock_stream(nghttp3_conn *conn, int64_t stream_id) { nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; + return 0; } stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_FC_BLOCKED; - if (nghttp3_stream_bidi_or_push(stream) && + if (nghttp3_client_stream_bidi(stream->node.nid.id) && nghttp3_stream_require_schedule(stream)) { return nghttp3_conn_ensure_stream_scheduled(conn, stream); } @@ -3301,16 +2342,29 @@ int nghttp3_conn_unblock_stream(nghttp3_conn *conn, int64_t stream_id) { return 0; } +int nghttp3_conn_is_stream_writable(nghttp3_conn *conn, int64_t stream_id) { + nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + + if (stream == NULL) { + return 0; + } + + return (stream->flags & + (NGHTTP3_STREAM_FLAG_FC_BLOCKED | + NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED | NGHTTP3_STREAM_FLAG_SHUT_WR | + NGHTTP3_STREAM_FLAG_CLOSED)) == 0; +} + int nghttp3_conn_resume_stream(nghttp3_conn *conn, int64_t stream_id) { nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; + return 0; } stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED; - if (nghttp3_stream_bidi_or_push(stream) && + if (nghttp3_client_stream_bidi(stream->node.nid.id) && nghttp3_stream_require_schedule(stream)) { return nghttp3_conn_ensure_stream_scheduled(conn, stream); } @@ -3336,9 +2390,7 @@ int nghttp3_conn_close_stream(nghttp3_conn *conn, int64_t stream_id, nghttp3_conn_unschedule_stream(conn, stream); - if (stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX && - (conn->server || !stream->pp || - (stream->pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED))) { + if (stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX) { return conn_delete_stream(conn, stream); } @@ -3346,13 +2398,22 @@ int nghttp3_conn_close_stream(nghttp3_conn *conn, int64_t stream_id, return 0; } -int nghttp3_conn_reset_stream(nghttp3_conn *conn, int64_t stream_id) { +int nghttp3_conn_shutdown_stream_read(nghttp3_conn *conn, int64_t stream_id) { nghttp3_stream *stream; + if (!nghttp3_client_stream_bidi(stream_id)) { + return 0; + } + stream = nghttp3_conn_find_stream(conn, stream_id); if (stream) { - stream->flags |= NGHTTP3_STREAM_FLAG_RESET; + if (stream->flags & NGHTTP3_STREAM_FLAG_SHUT_RD) { + return 0; + } + + stream->flags |= NGHTTP3_STREAM_FLAG_SHUT_RD; } + return nghttp3_qpack_decoder_cancel_stream(&conn->qdec, stream_id); } @@ -3383,26 +2444,6 @@ void nghttp3_conn_set_max_concurrent_streams(nghttp3_conn *conn, max_concurrent_streams); } -int nghttp3_conn_submit_max_push_id(nghttp3_conn *conn) { - nghttp3_frame_entry frent; - int rv; - - assert(conn->tx.ctrl); - assert(!(conn->flags & NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED)); - - frent.fr.hd.type = NGHTTP3_FRAME_MAX_PUSH_ID; - /* The acutal push_id is set when frame is serialized */ - - rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent); - if (rv != 0) { - return rv; - } - - conn->flags |= NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED; - - return 0; -} - int nghttp3_conn_set_stream_user_data(nghttp3_conn *conn, int64_t stream_id, void *stream_user_data) { nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); @@ -3416,48 +2457,66 @@ int nghttp3_conn_set_stream_user_data(nghttp3_conn *conn, int64_t stream_id, return 0; } -int64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn, - int64_t stream_id) { +uint64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn, + int64_t stream_id) { nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; + return 0; } - return stream->rstate.left; + return (uint64_t)stream->rstate.left; } int nghttp3_conn_get_stream_priority(nghttp3_conn *conn, nghttp3_pri *dest, int64_t stream_id) { - nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + nghttp3_stream *stream; assert(conn->server); + if (!nghttp3_client_stream_bidi(stream_id)) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + stream = nghttp3_conn_find_stream(conn, stream_id); if (stream == NULL) { return NGHTTP3_ERR_STREAM_NOT_FOUND; } - dest->urgency = nghttp3_pri_uint8_urgency(stream->rx.http.pri); - dest->inc = nghttp3_pri_uint8_inc(stream->rx.http.pri); + dest->urgency = nghttp3_pri_uint8_urgency(stream->node.pri); + dest->inc = nghttp3_pri_uint8_inc(stream->node.pri); return 0; } int nghttp3_conn_set_stream_priority(nghttp3_conn *conn, int64_t stream_id, const nghttp3_pri *pri) { - nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + nghttp3_stream *stream; + nghttp3_frame_entry frent; - assert(conn->server); assert(pri->urgency < NGHTTP3_URGENCY_LEVELS); assert(pri->inc == 0 || pri->inc == 1); + if (!nghttp3_client_stream_bidi(stream_id)) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + stream = nghttp3_conn_find_stream(conn, stream_id); if (stream == NULL) { return NGHTTP3_ERR_STREAM_NOT_FOUND; } - stream->rx.http.pri = nghttp3_pri_to_uint8(pri); + if (conn->server) { + stream->flags |= NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET; + + return conn_update_stream_priority(conn, stream, nghttp3_pri_to_uint8(pri)); + } + + frent.fr.hd.type = NGHTTP3_FRAME_PRIORITY_UPDATE; + frent.fr.priority_update.pri_elem_id = stream_id; + frent.fr.priority_update.pri = *pri; - return conn_update_stream_priority(conn, stream, stream->rx.http.pri); + return nghttp3_stream_frq_add(conn->tx.ctrl, &frent); } int nghttp3_conn_is_remote_qpack_encoder_stream(nghttp3_conn *conn, @@ -3472,52 +2531,12 @@ int nghttp3_conn_is_remote_qpack_encoder_stream(nghttp3_conn *conn, return stream && stream->type == NGHTTP3_STREAM_TYPE_QPACK_ENCODER; } -void nghttp3_settings_default(nghttp3_settings *settings) { +void nghttp3_settings_default_versioned(int settings_version, + nghttp3_settings *settings) { + (void)settings_version; + memset(settings, 0, sizeof(nghttp3_settings)); settings->max_field_section_size = NGHTTP3_VARINT_MAX; -} - -int nghttp3_push_promise_new(nghttp3_push_promise **ppp, int64_t push_id, - uint64_t seq, nghttp3_tnode *assoc_tnode, - const nghttp3_mem *mem) { - nghttp3_push_promise *pp; - nghttp3_node_id nid; - - pp = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_push_promise)); - if (pp == NULL) { - return NGHTTP3_ERR_NOMEM; - } - - nghttp3_tnode_init( - &pp->node, nghttp3_node_id_init(&nid, NGHTTP3_NODE_ID_TYPE_PUSH, push_id), - seq, NGHTTP3_DEFAULT_URGENCY); - - pp->me.key = (key_type)push_id; - pp->node.nid.id = push_id; - pp->http.status_code = -1; - pp->http.content_length = -1; - - if (assoc_tnode) { - assert(assoc_tnode->nid.type == NGHTTP3_NODE_ID_TYPE_STREAM); - - pp->stream_id = assoc_tnode->nid.id; - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_BOUND; - } else { - pp->stream_id = -1; - } - - *ppp = pp; - - return 0; -} - -void nghttp3_push_promise_del(nghttp3_push_promise *pp, - const nghttp3_mem *mem) { - if (pp == NULL) { - return; - } - - nghttp3_tnode_free(&pp->node); - - nghttp3_mem_free(mem, pp); + settings->qpack_encoder_max_dtable_capacity = + NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY; } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h index f0f012e177001d..fa7071e4b1ddb7 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h @@ -48,80 +48,38 @@ blocked streams for QPACK encoder. */ #define NGHTTP3_QPACK_ENCODER_MAX_BLOCK_STREAMS 100 -/* NGHTTP3_PUSH_PROMISE_FLAG_NONE indicates that no flag is set. */ -#define NGHTTP3_PUSH_PROMISE_FLAG_NONE 0x00 -/* NGHTTP3_PUSH_PROMISE_FLAG_RECVED is set when PUSH_PROMISE is - completely received. */ -#define NGHTTP3_PUSH_PROMISE_FLAG_RECVED 0x01 -/* NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL is set when push is - cancelled by server before receiving PUSH_PROMISE completely. - This flag should have no effect if push stream has already - opened. */ -#define NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL 0x02 -/* NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL is set when push is - canceled by the local endpoint. */ -#define NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL 0x04 -#define NGHTTP3_PUSH_PROMISE_FLAG_CANCELLED \ - (NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL | \ - NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL) -/* NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED indicates that - unsent_max_pushes has been updated for this push ID. */ -#define NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED 0x08 -/* NGHTTP3_PUSH_PROMISE_FLAG_BOUND is set if nghttp3_push_promise - object is bound to a stream where PUSH_PROMISE frame is received - first. nghttp3_push_promise object might be created before - receiving any PUSH_PROMISE when pushed stream is received before - it.*/ -#define NGHTTP3_PUSH_PROMISE_FLAG_BOUND 0x10 - -typedef struct nghttp3_push_promise { - nghttp3_map_entry me; - nghttp3_tnode node; - nghttp3_http_state http; - /* stream is server initiated unidirectional stream which fulfils - the push promise. */ - nghttp3_stream *stream; - /* stream_id is the stream ID where this PUSH_PROMISE is first - received. PUSH_PROMISE with same push ID is allowed to be sent - in the multiple streams. We ignore those duplicated PUSH_PROMISE - entirely because we don't see any value to add extra cost of - processing for it. Even ignoring those frame is not yet easy - because we have to decode the header blocks. Server push is - overly complex and there is no good use case after all. */ - int64_t stream_id; - /* flags is bitwise OR of zero or more of - NGHTTP3_PUSH_PROMISE_FLAG_*. */ - uint16_t flags; -} nghttp3_push_promise; - /* NGHTTP3_CONN_FLAG_NONE indicates that no flag is set. */ -#define NGHTTP3_CONN_FLAG_NONE 0x0000 +#define NGHTTP3_CONN_FLAG_NONE 0x0000u /* NGHTTP3_CONN_FLAG_SETTINGS_RECVED is set when SETTINGS frame has been received. */ -#define NGHTTP3_CONN_FLAG_SETTINGS_RECVED 0x0001 +#define NGHTTP3_CONN_FLAG_SETTINGS_RECVED 0x0001u /* NGHTTP3_CONN_FLAG_CONTROL_OPENED is set when a control stream has opened. */ -#define NGHTTP3_CONN_FLAG_CONTROL_OPENED 0x0002 +#define NGHTTP3_CONN_FLAG_CONTROL_OPENED 0x0002u /* NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED is set when a QPACK encoder stream has opened. */ -#define NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED 0x0004 +#define NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED 0x0004u /* NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED is set when a QPACK decoder stream has opened. */ -#define NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED 0x0008 -/* NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED indicates that MAX_PUSH_ID has - been queued to control stream. */ -#define NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED 0x0010 +#define NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED 0x0008u /* NGHTTP3_CONN_FLAG_GOAWAY_RECVED indicates that GOAWAY frame has received. */ -#define NGHTTP3_CONN_FLAG_GOAWAY_RECVED 0x0020 +#define NGHTTP3_CONN_FLAG_GOAWAY_RECVED 0x0020u /* NGHTTP3_CONN_FLAG_GOAWAY_QUEUED indicates that GOAWAY frame has been submitted for transmission. */ -#define NGHTTP3_CONN_FLAG_GOAWAY_QUEUED 0x0040 +#define NGHTTP3_CONN_FLAG_GOAWAY_QUEUED 0x0040u + +typedef struct nghttp3_chunk { + nghttp3_opl_entry oplent; +} nghttp3_chunk; + +nghttp3_objalloc_def(chunk, nghttp3_chunk, oplent); struct nghttp3_conn { + nghttp3_objalloc out_chunk_objalloc; + nghttp3_objalloc stream_objalloc; nghttp3_callbacks callbacks; nghttp3_map streams; - nghttp3_map pushes; nghttp3_qpack_decoder qdec; nghttp3_qpack_encoder qenc; nghttp3_pq qpack_blocked_streams; @@ -138,11 +96,9 @@ struct nghttp3_conn { nghttp3_settings settings; struct { /* max_pushes is the number of push IDs that local endpoint can - issue. This field is used by server only. */ + issue. This field is used by server only and used just for + validation */ uint64_t max_pushes; - /* next_push_id is the next push ID server uses. This field is - used by server only. */ - int64_t next_push_id; } uni; } local; @@ -154,18 +110,6 @@ struct nghttp3_conn { issue. This field is used on server side only. */ uint64_t max_client_streams; } bidi; - struct { - /* push_idtr tracks which push ID has been used by remote - server. This field is used by client only. */ - nghttp3_gaptr push_idtr; - /* unsent_max_pushes is the maximum number of push which the local - endpoint can accept. This limit is not yet notified to the - remote endpoint. This field is used by client only. */ - uint64_t unsent_max_pushes; - /* max_push is the maximum number of push which the local - endpoint can accept. This field is used by client only. */ - uint64_t max_pushes; - } uni; nghttp3_settings settings; } remote; @@ -174,7 +118,13 @@ struct nghttp3_conn { int64_t goaway_id; int64_t max_stream_id_bidi; - int64_t max_push_id; + + /* pri_fieldbuf is a buffer to store incoming Priority Field Value + in PRIORITY_UPDATE frame. */ + uint8_t pri_fieldbuf[8]; + /* pri_fieldlen is the number of bytes written into + pri_fieldbuf. */ + size_t pri_fieldbuflen; } rx; struct { @@ -192,17 +142,9 @@ struct nghttp3_conn { nghttp3_stream *nghttp3_conn_find_stream(nghttp3_conn *conn, int64_t stream_id); -nghttp3_push_promise *nghttp3_conn_find_push_promise(nghttp3_conn *conn, - int64_t push_id); - int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream, int64_t stream_id); -int nghttp3_conn_create_push_promise(nghttp3_conn *conn, - nghttp3_push_promise **ppp, - int64_t push_id, - nghttp3_tnode *assoc_tnode); - nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, nghttp3_stream *stream, const uint8_t *src, size_t srclen, int fin); @@ -214,10 +156,6 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, nghttp3_stream *stream, const uint8_t *src, size_t srclen); -nghttp3_ssize nghttp3_conn_read_push(nghttp3_conn *conn, size_t *pnproc, - nghttp3_stream *stream, const uint8_t *src, - size_t srclen, int fin); - nghttp3_ssize nghttp3_conn_read_qpack_encoder(nghttp3_conn *conn, const uint8_t *src, size_t srclen); @@ -226,24 +164,14 @@ nghttp3_ssize nghttp3_conn_read_qpack_decoder(nghttp3_conn *conn, const uint8_t *src, size_t srclen); -int nghttp3_conn_on_push_promise_push_id(nghttp3_conn *conn, int64_t push_id, - nghttp3_stream *stream); - -int nghttp3_conn_on_client_cancel_push(nghttp3_conn *conn, - const nghttp3_frame_cancel_push *fr); - -int nghttp3_conn_on_server_cancel_push(nghttp3_conn *conn, - const nghttp3_frame_cancel_push *fr); - -int nghttp3_conn_on_stream_push_id(nghttp3_conn *conn, nghttp3_stream *stream, - int64_t push_id); - int nghttp3_conn_on_data(nghttp3_conn *conn, nghttp3_stream *stream, const uint8_t *data, size_t datalen); +int nghttp3_conn_on_priority_update(nghttp3_conn *conn, + const nghttp3_frame_priority_update *fr); + nghttp3_ssize nghttp3_conn_on_headers(nghttp3_conn *conn, nghttp3_stream *stream, - nghttp3_push_promise *pp, const uint8_t *data, size_t datalen, int fin); @@ -255,12 +183,6 @@ int nghttp3_conn_qpack_blocked_streams_push(nghttp3_conn *conn, void nghttp3_conn_qpack_blocked_streams_pop(nghttp3_conn *conn); -int nghttp3_conn_server_cancel_push(nghttp3_conn *conn, int64_t push_id); - -int nghttp3_conn_client_cancel_push(nghttp3_conn *conn, int64_t push_id); - -int nghttp3_conn_submit_max_push_id(nghttp3_conn *conn); - int nghttp3_conn_schedule_stream(nghttp3_conn *conn, nghttp3_stream *stream); int nghttp3_conn_ensure_stream_scheduled(nghttp3_conn *conn, @@ -270,20 +192,10 @@ void nghttp3_conn_unschedule_stream(nghttp3_conn *conn, nghttp3_stream *stream); int nghttp3_conn_reject_stream(nghttp3_conn *conn, nghttp3_stream *stream); -int nghttp3_conn_reject_push_stream(nghttp3_conn *conn, nghttp3_stream *stream); - -int nghttp3_conn_cancel_push_stream(nghttp3_conn *conn, nghttp3_stream *stream); - /* * nghttp3_conn_get_next_tx_stream returns next stream to send. It * returns NULL if there is no such stream. */ nghttp3_stream *nghttp3_conn_get_next_tx_stream(nghttp3_conn *conn); -int nghttp3_push_promise_new(nghttp3_push_promise **ppp, int64_t push_id, - uint64_t seq, nghttp3_tnode *assoc_tnode, - const nghttp3_mem *mem); - -void nghttp3_push_promise_del(nghttp3_push_promise *pp, const nghttp3_mem *mem); - #endif /* NGHTTP3_CONN_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c index 04bf6a0f298b3b..cb340ab5a11363 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c @@ -58,6 +58,7 @@ int64_t nghttp3_get_varint(size_t *plen, const uint8_t *p) { } assert(0); + abort(); } int64_t nghttp3_get_varint_fb(const uint8_t *p) { return *p & 0x3f; } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h index 860326385ba434..23555be7cac027 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h @@ -88,7 +88,7 @@ STIN uint32_t htonl(uint32_t hostlong) { uint32_t res; unsigned char *p = (unsigned char *)&res; - *p++ = hostlong >> 24; + *p++ = (unsigned char)(hostlong >> 24); *p++ = (hostlong >> 16) & 0xffu; *p++ = (hostlong >> 8) & 0xffu; *p = hostlong & 0xffu; @@ -98,7 +98,7 @@ STIN uint32_t htonl(uint32_t hostlong) { STIN uint16_t htons(uint16_t hostshort) { uint16_t res; unsigned char *p = (unsigned char *)&res; - *p++ = hostshort >> 8; + *p++ = (unsigned char)(hostshort >> 8); *p = hostshort & 0xffu; return res; } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_err.c b/deps/ngtcp2/nghttp3/lib/nghttp3_err.c index fb1353ea8a45c5..5cf94db852f71f 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_err.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_err.c @@ -36,8 +36,6 @@ const char *nghttp3_strerror(int liberr) { return "ERR_WOULDBLOCK"; case NGHTTP3_ERR_STREAM_IN_USE: return "ERR_STREAM_IN_USE"; - case NGHTTP3_ERR_PUSH_ID_BLOCKED: - return "ERR_PUSH_ID_BLOCKED"; case NGHTTP3_ERR_MALFORMED_HTTP_HEADER: return "ERR_MALFORMED_HTTP_HEADER"; case NGHTTP3_ERR_REMOVE_HTTP_HEADER: @@ -48,14 +46,12 @@ const char *nghttp3_strerror(int liberr) { return "ERR_QPACK_FATAL"; case NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE: return "ERR_QPACK_HEADER_TOO_LARGE"; - case NGHTTP3_ERR_IGNORE_STREAM: - return "ERR_IGNORE_STREAM"; case NGHTTP3_ERR_STREAM_NOT_FOUND: return "ERR_STREAM_NOT_FOUND"; - case NGHTTP3_ERR_IGNORE_PUSH_PROMISE: - return "ERR_IGNORE_PUSH_PROMISE"; case NGHTTP3_ERR_CONN_CLOSING: return "ERR_CONN_CLOSING"; + case NGHTTP3_ERR_STREAM_DATA_OVERFLOW: + return "ERR_STREAM_DATA_OVERFLOW"; case NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED: return "ERR_QPACK_DECOMPRESSION_FAILED"; case NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR: @@ -106,6 +102,8 @@ uint64_t nghttp3_err_infer_quic_app_error_code(int liberr) { case NGHTTP3_ERR_H3_MISSING_SETTINGS: return NGHTTP3_H3_MISSING_SETTINGS; case NGHTTP3_ERR_H3_INTERNAL_ERROR: + case NGHTTP3_ERR_NOMEM: + case NGHTTP3_ERR_CALLBACK_FAILURE: return NGHTTP3_H3_INTERNAL_ERROR; case NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM: return NGHTTP3_H3_CLOSED_CRITICAL_STREAM; @@ -124,3 +122,5 @@ uint64_t nghttp3_err_infer_quic_app_error_code(int liberr) { return NGHTTP3_H3_GENERAL_PROTOCOL_ERROR; } } + +int nghttp3_err_is_fatal(int liberr) { return liberr < NGHTTP3_ERR_FATAL; } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c index 6be82c6edaf492..38c395ebe16162 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c @@ -26,6 +26,7 @@ #include "nghttp3_frame.h" #include +#include #include "nghttp3_conv.h" #include "nghttp3_str.h" @@ -70,59 +71,53 @@ size_t nghttp3_frame_write_settings_len(int64_t *ppayloadlen, nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen; } -uint8_t *nghttp3_frame_write_cancel_push(uint8_t *p, - const nghttp3_frame_cancel_push *fr) { +uint8_t *nghttp3_frame_write_goaway(uint8_t *p, + const nghttp3_frame_goaway *fr) { p = nghttp3_frame_write_hd(p, &fr->hd); - p = nghttp3_put_varint(p, fr->push_id); + p = nghttp3_put_varint(p, fr->id); return p; } -size_t -nghttp3_frame_write_cancel_push_len(int64_t *ppayloadlen, - const nghttp3_frame_cancel_push *fr) { - size_t payloadlen = nghttp3_put_varint_len(fr->push_id); +size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen, + const nghttp3_frame_goaway *fr) { + size_t payloadlen = nghttp3_put_varint_len(fr->id); *ppayloadlen = (int64_t)payloadlen; - return nghttp3_put_varint_len(NGHTTP3_FRAME_CANCEL_PUSH) + + return nghttp3_put_varint_len(NGHTTP3_FRAME_GOAWAY) + nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen; } -uint8_t *nghttp3_frame_write_max_push_id(uint8_t *p, - const nghttp3_frame_max_push_id *fr) { +uint8_t * +nghttp3_frame_write_priority_update(uint8_t *p, + const nghttp3_frame_priority_update *fr) { p = nghttp3_frame_write_hd(p, &fr->hd); - p = nghttp3_put_varint(p, fr->push_id); + p = nghttp3_put_varint(p, fr->pri_elem_id); - return p; -} - -size_t -nghttp3_frame_write_max_push_id_len(int64_t *ppayloadlen, - const nghttp3_frame_max_push_id *fr) { - size_t payloadlen = nghttp3_put_varint_len(fr->push_id); + assert(fr->pri.urgency <= NGHTTP3_URGENCY_LOW); - *ppayloadlen = (int64_t)payloadlen; + *p++ = 'u'; + *p++ = '='; + *p++ = (uint8_t)('0' + fr->pri.urgency); - return nghttp3_put_varint_len(NGHTTP3_FRAME_MAX_PUSH_ID) + - nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen; -} - -uint8_t *nghttp3_frame_write_goaway(uint8_t *p, - const nghttp3_frame_goaway *fr) { - p = nghttp3_frame_write_hd(p, &fr->hd); - p = nghttp3_put_varint(p, fr->id); + if (fr->pri.inc) { +#define NGHTTP3_PRIORITY_INCREMENTAL ", i" + p = nghttp3_cpymem(p, (const uint8_t *)NGHTTP3_PRIORITY_INCREMENTAL, + sizeof(NGHTTP3_PRIORITY_INCREMENTAL) - 1); + } return p; } -size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen, - const nghttp3_frame_goaway *fr) { - size_t payloadlen = nghttp3_put_varint_len(fr->id); +size_t nghttp3_frame_write_priority_update_len( + int64_t *ppayloadlen, const nghttp3_frame_priority_update *fr) { + size_t payloadlen = nghttp3_put_varint_len(fr->pri_elem_id) + sizeof("u=U") - + 1 + (fr->pri.inc ? sizeof(", i") - 1 : 0); *ppayloadlen = (int64_t)payloadlen; - return nghttp3_put_varint_len(NGHTTP3_FRAME_GOAWAY) + + return nghttp3_put_varint_len(fr->hd.type) + nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen; } @@ -207,12 +202,3 @@ void nghttp3_frame_headers_free(nghttp3_frame_headers *fr, nghttp3_nva_del(fr->nva, mem); } - -void nghttp3_frame_push_promise_free(nghttp3_frame_push_promise *fr, - const nghttp3_mem *mem) { - if (fr == NULL) { - return; - } - - nghttp3_nva_del(fr->nva, mem); -} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h index 9bd59a9660d85b..b64bbc4ecb9667 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h @@ -42,6 +42,10 @@ typedef enum nghttp3_frame_type { NGHTTP3_FRAME_PUSH_PROMISE = 0x05, NGHTTP3_FRAME_GOAWAY = 0x07, NGHTTP3_FRAME_MAX_PUSH_ID = 0x0d, + /* PRIORITY_UPDATE: + https://tools.ietf.org/html/draft-ietf-httpbis-priority-03 */ + NGHTTP3_FRAME_PRIORITY_UPDATE = 0x0f0700, + NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID = 0x0f0701, } nghttp3_frame_type; typedef enum nghttp3_h2_reserved_type { @@ -66,14 +70,10 @@ typedef struct nghttp3_frame_headers { size_t nvlen; } nghttp3_frame_headers; -typedef struct nghttp3_frame_cancel_push { - nghttp3_frame_hd hd; - int64_t push_id; -} nghttp3_frame_cancel_push; - #define NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE 0x06 #define NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY 0x01 #define NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS 0x07 +#define NGHTTP3_SETTINGS_ID_ENABLE_CONNECT_PROTOCOL 0x08 #define NGHTTP3_H2_SETTINGS_ID_ENABLE_PUSH 0x2 #define NGHTTP3_H2_SETTINGS_ID_MAX_CONCURRENT_STREAMS 0x3 @@ -91,32 +91,28 @@ typedef struct nghttp3_frame_settings { nghttp3_settings_entry iv[1]; } nghttp3_frame_settings; -typedef struct nghttp3_frame_push_promise { - nghttp3_frame_hd hd; - nghttp3_nv *nva; - size_t nvlen; - int64_t push_id; -} nghttp3_frame_push_promise; - typedef struct nghttp3_frame_goaway { nghttp3_frame_hd hd; int64_t id; } nghttp3_frame_goaway; -typedef struct nghttp3_frame_max_push_id { +typedef struct nghttp3_frame_priority_update { nghttp3_frame_hd hd; - int64_t push_id; -} nghttp3_frame_max_push_id; + /* pri_elem_id is stream ID if hd.type == + NGHTTP3_FRAME_PRIORITY_UPDATE. It is push ID if hd.type == + NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID. It is undefined + otherwise. */ + int64_t pri_elem_id; + nghttp3_pri pri; +} nghttp3_frame_priority_update; typedef union nghttp3_frame { nghttp3_frame_hd hd; nghttp3_frame_data data; nghttp3_frame_headers headers; - nghttp3_frame_cancel_push cancel_push; nghttp3_frame_settings settings; - nghttp3_frame_push_promise push_promise; nghttp3_frame_goaway goaway; - nghttp3_frame_max_push_id max_push_id; + nghttp3_frame_priority_update priority_update; } nghttp3_frame; /* @@ -150,42 +146,6 @@ uint8_t *nghttp3_frame_write_settings(uint8_t *dest, size_t nghttp3_frame_write_settings_len(int64_t *pppayloadlen, const nghttp3_frame_settings *fr); -/* - * nghttp3_frame_write_cancel_push writes CANCEL_PUSH frame |fr| to - * |dest|. This function assumes that |dest| has enough space to - * write |fr|. - * - * This function returns |dest| plus the number of bytes written. - */ -uint8_t *nghttp3_frame_write_cancel_push(uint8_t *dest, - const nghttp3_frame_cancel_push *fr); - -/* - * nghttp3_frame_write_cancel_push_len returns the number of bytes - * required to write |fr|. fr->hd.length is ignored. This function - * stores payload length in |*ppayloadlen|. - */ -size_t nghttp3_frame_write_cancel_push_len(int64_t *ppayloadlen, - const nghttp3_frame_cancel_push *fr); - -/* - * nghttp3_frame_write_max_push_id writes MAX_PUSH_ID frame |fr| to - * |dest|. This function assumes that |dest| has enough space to - * write |fr|. - * - * This function returns |dest| plus the number of bytes written. - */ -uint8_t *nghttp3_frame_write_max_push_id(uint8_t *dest, - const nghttp3_frame_max_push_id *fr); - -/* - * nghttp3_frame_write_max_push_id_len returns the number of bytes - * required to write |fr|. fr->hd.length is ignored. This function - * stores payload length in |*ppayloadlen|. - */ -size_t nghttp3_frame_write_max_push_id_len(int64_t *ppayloadlen, - const nghttp3_frame_max_push_id *fr); - /* * nghttp3_frame_write_goaway writes GOAWAY frame |fr| to |dest|. * This function assumes that |dest| has enough space to write |fr|. @@ -203,6 +163,25 @@ uint8_t *nghttp3_frame_write_goaway(uint8_t *dest, size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen, const nghttp3_frame_goaway *fr); +/* + * nghttp3_frame_write_priority_update writes PRIORITY_UPDATE frame + * |fr| to |dest|. This function assumes that |dest| has enough space + * to write |fr|. + * + * This function returns |dest| plus the number of bytes written; + */ +uint8_t * +nghttp3_frame_write_priority_update(uint8_t *dest, + const nghttp3_frame_priority_update *fr); + +/* + * nghttp3_frame_write_priority_update_len returns the number of bytes + * required to write |fr|. fr->hd.length is ignored. This function + * stores payload length in |*ppayloadlen|. + */ +size_t nghttp3_frame_write_priority_update_len( + int64_t *ppayloadlen, const nghttp3_frame_priority_update *fr); + /* * nghttp3_nva_copy copies name/value pairs from |nva|, which contains * |nvlen| pairs, to |*nva_ptr|, which is dynamically allocated so @@ -233,11 +212,4 @@ void nghttp3_nva_del(nghttp3_nv *nva, const nghttp3_mem *mem); void nghttp3_frame_headers_free(nghttp3_frame_headers *fr, const nghttp3_mem *mem); -/* - * nghttp3_frame_push_promise_free frees memory allocated for |fr|. - * It assumes that fr->nva is created by nghttp3_nva_copy() or NULL. - */ -void nghttp3_frame_push_promise_free(nghttp3_frame_push_promise *fr, - const nghttp3_mem *mem); - #endif /* NGHTTP3_FRAME_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c b/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c index c60887d1ec4db7..88cb49a02f892f 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c @@ -24,29 +24,26 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp3_gaptr.h" -#include "nghttp3_range.h" #include #include -int nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem) { - int rv; - nghttp3_range range = {0, UINT64_MAX}; +void nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem) { + nghttp3_ksl_init(&gaptr->gap, nghttp3_ksl_range_compar, sizeof(nghttp3_range), + mem); - rv = nghttp3_ksl_init(&gaptr->gap, nghttp3_ksl_range_compar, - sizeof(nghttp3_range), mem); - if (rv != 0) { - return rv; - } + gaptr->mem = mem; +} + +static int gaptr_gap_init(nghttp3_gaptr *gaptr) { + nghttp3_range range = {0, UINT64_MAX}; + int rv; rv = nghttp3_ksl_insert(&gaptr->gap, NULL, &range, NULL); if (rv != 0) { - nghttp3_ksl_free(&gaptr->gap); return rv; } - gaptr->mem = mem; - return 0; } @@ -58,11 +55,19 @@ void nghttp3_gaptr_free(nghttp3_gaptr *gaptr) { nghttp3_ksl_free(&gaptr->gap); } -int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen) { +int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, + uint64_t datalen) { int rv; nghttp3_range k, m, l, r, q = {offset, offset + datalen}; nghttp3_ksl_it it; + if (nghttp3_ksl_len(&gaptr->gap) == 0) { + rv = gaptr_gap_init(gaptr); + if (rv != 0) { + return rv; + } + } + it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q, nghttp3_ksl_range_exclusive_compar); @@ -74,7 +79,7 @@ int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen) { } if (nghttp3_range_eq(&k, &m)) { - nghttp3_ksl_remove(&gaptr->gap, &it, &k); + nghttp3_ksl_remove_hint(&gaptr->gap, &it, &it, &k); continue; } nghttp3_range_cut(&l, &r, &k, &m); @@ -96,35 +101,69 @@ int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen) { } uint64_t nghttp3_gaptr_first_gap_offset(nghttp3_gaptr *gaptr) { - nghttp3_ksl_it it = nghttp3_ksl_begin(&gaptr->gap); - nghttp3_range r = *(nghttp3_range *)nghttp3_ksl_it_key(&it); + nghttp3_ksl_it it; + nghttp3_range r; + + if (nghttp3_ksl_len(&gaptr->gap) == 0) { + return 0; + } + + it = nghttp3_ksl_begin(&gaptr->gap); + r = *(nghttp3_range *)nghttp3_ksl_it_key(&it); + return r.begin; } -nghttp3_ksl_it nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr, - uint64_t offset) { +nghttp3_range nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr, + uint64_t offset) { nghttp3_range q = {offset, offset + 1}; - return nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q, - nghttp3_ksl_range_exclusive_compar); + nghttp3_ksl_it it; + + if (nghttp3_ksl_len(&gaptr->gap) == 0) { + nghttp3_range r = {0, UINT64_MAX}; + return r; + } + + it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q, + nghttp3_ksl_range_exclusive_compar); + + assert(!nghttp3_ksl_it_end(&it)); + + return *(nghttp3_range *)nghttp3_ksl_it_key(&it); } int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset, - size_t datalen) { + uint64_t datalen) { nghttp3_range q = {offset, offset + datalen}; - nghttp3_ksl_it it = nghttp3_ksl_lower_bound_compar( - &gaptr->gap, &q, nghttp3_ksl_range_exclusive_compar); - nghttp3_range k = *(nghttp3_range *)nghttp3_ksl_it_key(&it); - nghttp3_range m = nghttp3_range_intersect(&q, &k); + nghttp3_ksl_it it; + nghttp3_range k; + nghttp3_range m; + + if (nghttp3_ksl_len(&gaptr->gap) == 0) { + return 0; + } + + it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q, + nghttp3_ksl_range_exclusive_compar); + k = *(nghttp3_range *)nghttp3_ksl_it_key(&it); + m = nghttp3_range_intersect(&q, &k); + return nghttp3_range_len(&m) == 0; } void nghttp3_gaptr_drop_first_gap(nghttp3_gaptr *gaptr) { - nghttp3_ksl_it it = nghttp3_ksl_begin(&gaptr->gap); + nghttp3_ksl_it it; nghttp3_range r; + if (nghttp3_ksl_len(&gaptr->gap) == 0) { + return; + } + + it = nghttp3_ksl_begin(&gaptr->gap); + assert(!nghttp3_ksl_it_end(&it)); r = *(nghttp3_range *)nghttp3_ksl_it_key(&it); - nghttp3_ksl_remove(&gaptr->gap, NULL, &r); + nghttp3_ksl_remove_hint(&gaptr->gap, NULL, &it, &r); } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.h b/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.h index b1713a048bb3c7..7c83c847c9fe29 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.h @@ -34,6 +34,7 @@ #include "nghttp3_mem.h" #include "nghttp3_ksl.h" +#include "nghttp3_range.h" /* * nghttp3_gaptr maintains the gap in the range [0, UINT64_MAX). @@ -48,14 +49,8 @@ typedef struct nghttp3_gaptr { /* * nghttp3_gaptr_init initializes |gaptr|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. */ -int nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem); +void nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem); /* * nghttp3_gaptr_free frees resources allocated for |gaptr|. @@ -72,7 +67,7 @@ void nghttp3_gaptr_free(nghttp3_gaptr *gaptr); * NGHTTP3_ERR_NOMEM * Out of memory */ -int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen); +int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, uint64_t datalen); /* * nghttp3_gaptr_first_gap_offset returns the offset to the first gap. @@ -81,18 +76,18 @@ int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen); uint64_t nghttp3_gaptr_first_gap_offset(nghttp3_gaptr *gaptr); /* - * nghttp3_gaptr_get_first_gap_after returns the iterator pointing to - * the first gap which overlaps or comes after |offset|. + * nghttp3_gaptr_get_first_gap_after returns the first gap which + * overlaps or comes after |offset|. */ -nghttp3_ksl_it nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr, - uint64_t offset); +nghttp3_range nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr, + uint64_t offset); /* * nghttp3_gaptr_is_pushed returns nonzero if range [offset, offset + * datalen) is completely pushed into this object. */ int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset, - size_t datalen); + uint64_t datalen); /* * nghttp3_gaptr_drop_first_gap deletes the first gap entirely as if diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_http.c b/deps/ngtcp2/nghttp3/lib/nghttp3_http.c index 465c36ab2cbdb0..5e06d8c47658e1 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_http.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_http.c @@ -36,12 +36,17 @@ static uint8_t downcase(uint8_t c) { return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c; } +/* + * memieq returns 1 if the data pointed by |a| of length |n| equals to + * |b| of the same length in case-insensitive manner. The data + * pointed by |a| must not include upper cased letters (A-Z). + */ static int memieq(const void *a, const void *b, size_t n) { size_t i; const uint8_t *aa = a, *bb = b; for (i = 0; i < n; ++i) { - if (downcase(aa[i]) != downcase(bb[i])) { + if (aa[i] != downcase(bb[i])) { return 0; } } @@ -73,25 +78,12 @@ static int64_t parse_uint(const uint8_t *s, size_t len) { return n; } -static int lws(const uint8_t *s, size_t n) { - size_t i; - for (i = 0; i < n; ++i) { - if (s[i] != ' ' && s[i] != '\t') { - return 0; - } - } - return 1; -} - static int check_pseudo_header(nghttp3_http_state *http, - const nghttp3_qpack_nv *nv, int flag) { - if (http->flags & flag) { - return 0; - } - if (lws(nv->value->base, nv->value->len)) { + const nghttp3_qpack_nv *nv, uint32_t flag) { + if ((http->flags & flag) || nv->value->len == 0) { return 0; } - http->flags = (uint16_t)(http->flags | flag); + http->flags |= flag; return 1; } @@ -106,98 +98,730 @@ static int expect_response_body(nghttp3_http_state *http) { :path header field value must start with "/". This function must be called after ":method" header field was received. This function returns nonzero if path is valid.*/ -static int check_path(nghttp3_http_state *http) { +static int check_path_flags(nghttp3_http_state *http) { return (http->flags & NGHTTP3_HTTP_FLAG_SCHEME_HTTP) == 0 || ((http->flags & NGHTTP3_HTTP_FLAG_PATH_REGULAR) || ((http->flags & NGHTTP3_HTTP_FLAG_METH_OPTIONS) && (http->flags & NGHTTP3_HTTP_FLAG_PATH_ASTERISK))); } -int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value, - size_t len) { - nghttp3_pri pri = *dest; - const uint8_t *p = value, *end = value + len; +/* Generated by genchartbl.py */ +static const int SF_KEY_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, + 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, + 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, + 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, + 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, + 0 /* RS */, 0 /* US */, 0 /* SPC */, 0 /* ! */, 0 /* " */, + 0 /* # */, 0 /* $ */, 0 /* % */, 0 /* & */, 0 /* ' */, + 0 /* ( */, 0 /* ) */, 1 /* * */, 0 /* + */, 0 /* , */, + 1 /* - */, 1 /* . */, 0 /* / */, 1 /* 0 */, 1 /* 1 */, + 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, + 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */, + 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */, + 0 /* A */, 0 /* B */, 0 /* C */, 0 /* D */, 0 /* E */, + 0 /* F */, 0 /* G */, 0 /* H */, 0 /* I */, 0 /* J */, + 0 /* K */, 0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */, + 0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */, 0 /* T */, + 0 /* U */, 0 /* V */, 0 /* W */, 0 /* X */, 0 /* Y */, + 0 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 0 /* ^ */, + 1 /* _ */, 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, + 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, + 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, + 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 0 /* | */, + 0 /* } */, 0 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */, + 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, + 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */, + 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */, + 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, + 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */, + 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */, + 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, + 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */, + 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */, + 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, + 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */, + 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */, + 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, + 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */, + 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */, + 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, + 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */, + 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */, + 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, + 0 /* 0xff */, +}; - for (;;) { - for (; p != end && (*p == ' ' || *p == '\t'); ++p) - ; +static nghttp3_ssize sf_parse_key(const uint8_t *begin, const uint8_t *end) { + const uint8_t *p = begin; - if (p == end) { - break; + if ((*p < 'a' || 'z' < *p) && *p != '*') { + return -1; + } + + for (; p != end && SF_KEY_CHARS[*p]; ++p) + ; + + return p - begin; +} + +static nghttp3_ssize sf_parse_integer_or_decimal(nghttp3_sf_value *dest, + const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + int sign = 1; + int64_t value = 0; + int type = NGHTTP3_SF_VALUE_TYPE_INTEGER; + size_t len = 0; + size_t fpos = 0; + size_t i; + + if (*p == '-') { + if (++p == end) { + return -1; } + sign = -1; + } + + if (*p < '0' || '9' < *p) { + return -1; + } + + for (; p != end; ++p) { switch (*p) { - case 'u': - ++p; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + value *= 10; + value += *p - '0'; + + if (++len > 15) { + return -1; + } - if (p + 2 > end || *p++ != '=') { - return NGHTTP3_ERR_INVALID_ARGUMENT; + break; + case '.': + if (type != NGHTTP3_SF_VALUE_TYPE_INTEGER) { + goto fin; } - if (!('0' <= *p && *p <= '7')) { - return NGHTTP3_ERR_INVALID_ARGUMENT; + if (len > 12) { + return -1; } + fpos = len; + type = NGHTTP3_SF_VALUE_TYPE_DECIMAL; - pri.urgency = (uint32_t)(*p++ - '0'); + break; + default: + goto fin; + }; + } - if (p == end) { - goto fin; +fin: + switch (type) { + case NGHTTP3_SF_VALUE_TYPE_INTEGER: + if (dest) { + dest->type = (uint8_t)type; + dest->i = value * sign; + } + + return p - begin; + case NGHTTP3_SF_VALUE_TYPE_DECIMAL: + if (fpos == len || len - fpos > 3) { + return -1; + } + + if (dest) { + dest->type = (uint8_t)type; + dest->d = (double)value; + for (i = len - fpos; i > 0; --i) { + dest->d /= (double)10; + } + dest->d *= sign; + } + + return p - begin; + default: + assert(0); + abort(); + } +} + +/* Generated by genchartbl.py */ +static const int SF_DQUOTE_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, + 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, + 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, + 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, + 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, + 0 /* RS */, 0 /* US */, 1 /* SPC */, 1 /* ! */, 0 /* " */, + 1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, 1 /* , */, + 1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */, + 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, + 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */, + 1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */, 1 /* @ */, + 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */, + 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */, + 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */, + 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */, + 1 /* Z */, 1 /* [ */, 0 /* \ */, 1 /* ] */, 1 /* ^ */, + 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, + 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, + 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, + 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */, 1 /* | */, + 1 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */, + 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, + 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */, + 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */, + 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, + 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */, + 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */, + 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, + 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */, + 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */, + 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, + 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */, + 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */, + 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, + 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */, + 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */, + 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, + 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */, + 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */, + 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, + 0 /* 0xff */, +}; + +static nghttp3_ssize sf_parse_string(nghttp3_sf_value *dest, + const uint8_t *begin, const uint8_t *end) { + const uint8_t *p = begin; + + if (*p++ != '"') { + return -1; + } + + for (; p != end; ++p) { + switch (*p) { + case '\\': + if (++p == end) { + return -1; } - if (*p++ != ',') { - return NGHTTP3_ERR_INVALID_ARGUMENT; + switch (*p) { + case '"': + case '\\': + break; + default: + return -1; } break; - case 'i': + case '"': + if (dest) { + dest->type = NGHTTP3_SF_VALUE_TYPE_STRING; + dest->s.base = begin + 1; + dest->s.len = (size_t)(p - dest->s.base); + } + ++p; - if (p == end) { - pri.inc = 1; - goto fin; + return p - begin; + default: + if (!SF_DQUOTE_CHARS[*p]) { + return -1; } + } + } - if (*p == ',') { - pri.inc = 1; - ++p; - break; + return -1; +} + +/* Generated by genchartbl.py */ +static const int SF_TOKEN_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, + 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, + 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, + 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, + 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, + 0 /* RS */, 0 /* US */, 0 /* SPC */, 1 /* ! */, 0 /* " */, + 1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */, 0 /* , */, + 1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */, + 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, + 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 0 /* ; */, + 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */, + 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */, + 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */, + 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */, + 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */, + 1 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 1 /* ^ */, + 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, + 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, + 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, + 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 1 /* | */, + 0 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */, + 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, + 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */, + 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */, + 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, + 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */, + 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */, + 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, + 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */, + 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */, + 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, + 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */, + 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */, + 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, + 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */, + 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */, + 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, + 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */, + 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */, + 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, + 0 /* 0xff */, +}; + +static nghttp3_ssize sf_parse_token(nghttp3_sf_value *dest, + const uint8_t *begin, const uint8_t *end) { + const uint8_t *p = begin; + + if ((*p < 'A' || 'Z' < *p) && (*p < 'a' || 'z' < *p) && *p != '*') { + return -1; + } + + for (; p != end && SF_TOKEN_CHARS[*p]; ++p) + ; + + if (dest) { + dest->type = NGHTTP3_SF_VALUE_TYPE_TOKEN; + dest->s.base = begin; + dest->s.len = (size_t)(p - begin); + } + + return p - begin; +} + +/* Generated by genchartbl.py */ +static const int SF_BYTESEQ_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, + 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, + 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, + 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, + 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, + 0 /* RS */, 0 /* US */, 0 /* SPC */, 0 /* ! */, 0 /* " */, + 0 /* # */, 0 /* $ */, 0 /* % */, 0 /* & */, 0 /* ' */, + 0 /* ( */, 0 /* ) */, 0 /* * */, 1 /* + */, 0 /* , */, + 0 /* - */, 0 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */, + 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, + 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */, + 0 /* < */, 1 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */, + 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */, + 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */, + 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */, + 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */, + 1 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 0 /* ^ */, + 0 /* _ */, 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, + 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, + 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, + 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 0 /* | */, + 0 /* } */, 0 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */, + 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, + 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */, + 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */, + 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, + 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */, + 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */, + 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, + 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */, + 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */, + 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, + 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */, + 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */, + 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, + 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */, + 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */, + 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, + 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */, + 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */, + 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, + 0 /* 0xff */, +}; + +static nghttp3_ssize sf_parse_byteseq(nghttp3_sf_value *dest, + const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + + if (*p++ != ':') { + return -1; + } + + for (; p != end; ++p) { + switch (*p) { + case ':': + if (dest) { + dest->type = NGHTTP3_SF_VALUE_TYPE_BYTESEQ; + dest->s.base = begin + 1; + dest->s.len = (size_t)(p - dest->s.base); } - if (p + 3 > end || *p != '=' || *(p + 1) != '?' || - (*(p + 2) != '0' && *(p + 2) != '1')) { - return NGHTTP3_ERR_INVALID_ARGUMENT; + ++p; + + return p - begin; + default: + if (!SF_BYTESEQ_CHARS[*p]) { + return -1; } + } + } - pri.inc = *(p + 2) == '1'; + return -1; +} - p += 3; +static nghttp3_ssize sf_parse_boolean(nghttp3_sf_value *dest, + const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + int b; - if (p == end) { - goto fin; + if (*p++ != '?') { + return -1; + } + + if (p == end) { + return -1; + } + + switch (*p++) { + case '0': + b = 0; + break; + case '1': + b = 1; + break; + default: + return -1; + } + + if (dest) { + dest->type = NGHTTP3_SF_VALUE_TYPE_BOOLEAN; + dest->b = b; + } + + return p - begin; +} + +static nghttp3_ssize sf_parse_bare_item(nghttp3_sf_value *dest, + const uint8_t *begin, + const uint8_t *end) { + switch (*begin) { + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return sf_parse_integer_or_decimal(dest, begin, end); + case '"': + return sf_parse_string(dest, begin, end); + case '*': + return sf_parse_token(dest, begin, end); + case ':': + return sf_parse_byteseq(dest, begin, end); + case '?': + return sf_parse_boolean(dest, begin, end); + default: + if (('A' <= *begin && *begin <= 'Z') || ('a' <= *begin && *begin <= 'z')) { + return sf_parse_token(dest, begin, end); + } + return -1; + } +} + +#define sf_discard_sp_end_err(BEGIN, END, ERR) \ + for (;; ++(BEGIN)) { \ + if ((BEGIN) == (END)) { \ + return (ERR); \ + } \ + if (*(BEGIN) != ' ') { \ + break; \ + } \ + } + +static nghttp3_ssize sf_parse_params(const uint8_t *begin, const uint8_t *end) { + const uint8_t *p = begin; + nghttp3_ssize slen; + + for (; p != end && *p == ';';) { + ++p; + + sf_discard_sp_end_err(p, end, -1); + + slen = sf_parse_key(p, end); + if (slen < 0) { + return -1; + } + + p += slen; + + if (p == end || *p != '=') { + /* Boolean true */ + } else if (++p == end) { + return -1; + } else { + slen = sf_parse_bare_item(NULL, p, end); + if (slen < 0) { + return -1; } - if (*p++ != ',') { - return NGHTTP3_ERR_INVALID_ARGUMENT; + p += slen; + } + } + + return p - begin; +} + +static nghttp3_ssize sf_parse_item(nghttp3_sf_value *dest, const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + nghttp3_ssize slen; + + slen = sf_parse_bare_item(dest, p, end); + if (slen < 0) { + return -1; + } + + p += slen; + + slen = sf_parse_params(p, end); + if (slen < 0) { + return -1; + } + + p += slen; + + return p - begin; +} + +nghttp3_ssize nghttp3_sf_parse_item(nghttp3_sf_value *dest, + const uint8_t *begin, const uint8_t *end) { + return sf_parse_item(dest, begin, end); +} + +static nghttp3_ssize sf_parse_inner_list(nghttp3_sf_value *dest, + const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + nghttp3_ssize slen; + + if (*p++ != '(') { + return -1; + } + + for (;;) { + sf_discard_sp_end_err(p, end, -1); + + if (*p == ')') { + ++p; + + slen = sf_parse_params(p, end); + if (slen < 0) { + return -1; } - break; - default: + p += slen; + + if (dest) { + dest->type = NGHTTP3_SF_VALUE_TYPE_INNER_LIST; + } + + return p - begin; + } + + slen = sf_parse_item(NULL, p, end); + if (slen < 0) { + return -1; + } + + p += slen; + + if (p == end || (*p != ' ' && *p != ')')) { + return -1; + } + } +} + +nghttp3_ssize nghttp3_sf_parse_inner_list(nghttp3_sf_value *dest, + const uint8_t *begin, + const uint8_t *end) { + return sf_parse_inner_list(dest, begin, end); +} + +static nghttp3_ssize sf_parse_item_or_inner_list(nghttp3_sf_value *dest, + const uint8_t *begin, + const uint8_t *end) { + if (*begin == '(') { + return sf_parse_inner_list(dest, begin, end); + } + + return sf_parse_item(dest, begin, end); +} + +#define sf_discard_ows(BEGIN, END) \ + for (;; ++(BEGIN)) { \ + if ((BEGIN) == (END)) { \ + goto fin; \ + } \ + if (*(BEGIN) != ' ' && *(BEGIN) != '\t') { \ + break; \ + } \ + } + +#define sf_discard_ows_end_err(BEGIN, END, ERR) \ + for (;; ++(BEGIN)) { \ + if ((BEGIN) == (END)) { \ + return (ERR); \ + } \ + if (*(BEGIN) != ' ' && *(BEGIN) != '\t') { \ + break; \ + } \ + } + +int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value, + size_t valuelen) { + const uint8_t *p = value, *end = value + valuelen; + nghttp3_ssize slen; + nghttp3_sf_value val; + nghttp3_pri pri = *dest; + const uint8_t *key; + size_t keylen; + + for (; p != end && *p == ' '; ++p) + ; + + for (; p != end;) { + slen = sf_parse_key(p, end); + if (slen < 0) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + key = p; + keylen = (size_t)slen; + + p += slen; + + if (p == end || *p != '=') { + /* Boolean true */ + val.type = NGHTTP3_SF_VALUE_TYPE_BOOLEAN; + val.b = 1; + + slen = sf_parse_params(p, end); + if (slen < 0) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + } else if (++p == end) { return NGHTTP3_ERR_INVALID_ARGUMENT; + } else { + slen = sf_parse_item_or_inner_list(&val, p, end); + if (slen < 0) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + } + + p += slen; + + if (keylen == 1) { + switch (key[0]) { + case 'i': + if (val.type != NGHTTP3_SF_VALUE_TYPE_BOOLEAN) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + pri.inc = val.b; + + break; + case 'u': + if (val.type != NGHTTP3_SF_VALUE_TYPE_INTEGER || + val.i < NGHTTP3_URGENCY_HIGH || NGHTTP3_URGENCY_LOW < val.i) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + pri.urgency = (uint32_t)val.i; + + break; + } } - if (p == end) { + sf_discard_ows(p, end); + + if (*p++ != ',') { return NGHTTP3_ERR_INVALID_ARGUMENT; } + + sf_discard_ows_end_err(p, end, NGHTTP3_ERR_INVALID_ARGUMENT); } fin: - *dest = pri; return 0; } -static int http_request_on_header(nghttp3_http_state *http, int64_t frame_type, +static int http_request_on_header(nghttp3_http_state *http, nghttp3_qpack_nv *nv, int trailers, int connect_protocol) { nghttp3_pri pri; @@ -229,10 +853,6 @@ static int http_request_on_header(nghttp3_http_state *http, int64_t frame_type, switch (nv->value->base[6]) { case 'T': if (lstreq("CONNECT", nv->value->base, nv->value->len)) { - if (frame_type == NGHTTP3_FRAME_PUSH_PROMISE) { - /* we won't allow CONNECT for push */ - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } http->flags |= NGHTTP3_HTTP_FLAG_METH_CONNECT; } break; @@ -259,8 +879,10 @@ static int http_request_on_header(nghttp3_http_state *http, int64_t frame_type, if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__SCHEME)) { return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; } - if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) || - (nv->value->len == 5 && memieq("https", nv->value->base, 5))) { + /* scheme is case-insensitive: + https://datatracker.ietf.org/doc/html/rfc3986#section-3.1 */ + if (lstrieq("http", nv->value->base, nv->value->len) || + lstrieq("https", nv->value->base, nv->value->len)) { http->flags |= NGHTTP3_HTTP_FLAG_SCHEME_HTTP; } break; @@ -308,11 +930,17 @@ static int http_request_on_header(nghttp3_http_state *http, int64_t frame_type, } break; case NGHTTP3_QPACK_TOKEN_PRIORITY: - pri.urgency = nghttp3_pri_uint8_urgency(http->pri); - pri.inc = nghttp3_pri_uint8_inc(http->pri); - if (nghttp3_http_parse_priority(&pri, nv->value->base, nv->value->len) == - 0) { - http->pri = nghttp3_pri_to_uint8(&pri); + if (!trailers && !(http->flags & NGHTTP3_HTTP_FLAG_BAD_PRIORITY)) { + pri.urgency = nghttp3_pri_uint8_urgency(http->pri); + pri.inc = nghttp3_pri_uint8_inc(http->pri); + if (nghttp3_http_parse_priority(&pri, nv->value->base, nv->value->len) == + 0) { + http->pri = nghttp3_pri_to_uint8(&pri); + http->flags |= NGHTTP3_HTTP_FLAG_PRIORITY; + } else { + http->flags &= ~NGHTTP3_HTTP_FLAG_PRIORITY; + http->flags |= NGHTTP3_HTTP_FLAG_BAD_PRIORITY; + } } break; default: @@ -509,8 +1137,167 @@ static int check_scheme(const uint8_t *value, size_t len) { return 1; } -int nghttp3_http_on_header(nghttp3_http_state *http, int64_t frame_type, - nghttp3_qpack_nv *nv, int request, int trailers) { +/* Generated by genmethodchartbl.py */ +static char VALID_METHOD_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, + 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, + 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */, + 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, + 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, + 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */, + 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */, + 0 /* SPC */, 1 /* ! */, 0 /* " */, 1 /* # */, + 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */, + 0 /* , */, 1 /* - */, 1 /* . */, 0 /* / */, + 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, + 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */, + 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */, + 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, + 0 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */, + 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */, + 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */, + 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, + 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */, + 1 /* X */, 1 /* Y */, 1 /* Z */, 0 /* [ */, + 0 /* \ */, 0 /* ] */, 1 /* ^ */, 1 /* _ */, + 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, + 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */, + 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */, + 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */, + 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, + 1 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */, + 0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */, + 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */, + 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, + 0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, + 0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */, + 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */, + 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, + 0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, + 0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */, + 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */, + 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, + 0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, + 0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */, + 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */, + 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, + 0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, + 0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */, + 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */, + 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, + 0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, + 0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */, + 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */, + 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, + 0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, + 0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */, + 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */ +}; + +static int check_method(const uint8_t *value, size_t len) { + const uint8_t *last; + if (len == 0) { + return 0; + } + for (last = value + len; value != last; ++value) { + if (!VALID_METHOD_CHARS[*value]) { + return 0; + } + } + return 1; +} + +/* Generated by genpathchartbl.py */ +static char VALID_PATH_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, + 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, + 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */, + 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, + 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, + 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */, + 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */, + 0 /* SPC */, 1 /* ! */, 1 /* " */, 1 /* # */, + 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, + 1 /* , */, 1 /* - */, 1 /* . */, 1 /* / */, + 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, + 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */, + 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */, + 1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */, + 1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */, + 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */, + 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */, + 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, + 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */, + 1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */, + 1 /* \ */, 1 /* ] */, 1 /* ^ */, 1 /* _ */, + 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, + 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */, + 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */, + 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */, + 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */, + 1 /* | */, 1 /* } */, 1 /* ~ */, 0 /* DEL */, + 1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */, + 1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */, + 1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */, + 1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */, + 1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */, + 1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */, + 1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */, + 1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */, + 1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */, + 1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */, + 1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */, + 1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */, + 1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */, + 1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */, + 1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */, + 1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */, + 1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */, + 1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */, + 1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */, + 1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */, + 1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */, + 1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */, + 1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */, + 1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */, + 1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */, + 1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */, + 1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */, + 1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */, + 1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */, + 1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */, + 1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */, + 1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */ +}; + +static int check_path(const uint8_t *value, size_t len) { + const uint8_t *last; + for (last = value + len; value != last; ++value) { + if (!VALID_PATH_CHARS[*value]) { + return 0; + } + } + return 1; +} + +int nghttp3_http_on_header(nghttp3_http_state *http, nghttp3_qpack_nv *nv, + int request, int trailers, int connect_protocol) { int rv; size_t i; uint8_t c; @@ -535,12 +1322,27 @@ int nghttp3_http_on_header(nghttp3_http_state *http, int64_t frame_type, assert(nv->name->len > 0); - if (nv->token == NGHTTP3_QPACK_TOKEN__AUTHORITY || - nv->token == NGHTTP3_QPACK_TOKEN_HOST) { - rv = check_authority(nv->value->base, nv->value->len); - } else if (nv->token == NGHTTP3_QPACK_TOKEN__SCHEME) { + switch (nv->token) { + case NGHTTP3_QPACK_TOKEN__METHOD: + rv = check_method(nv->value->base, nv->value->len); + break; + case NGHTTP3_QPACK_TOKEN__SCHEME: rv = check_scheme(nv->value->base, nv->value->len); - } else { + break; + case NGHTTP3_QPACK_TOKEN__AUTHORITY: + case NGHTTP3_QPACK_TOKEN_HOST: + if (request) { + rv = check_authority(nv->value->base, nv->value->len); + } else { + /* The use of host field in response field section is + undefined. */ + rv = nghttp3_check_header_value(nv->value->base, nv->value->len); + } + break; + case NGHTTP3_QPACK_TOKEN__PATH: + rv = check_path(nv->value->base, nv->value->len); + break; + default: rv = nghttp3_check_header_value(nv->value->base, nv->value->len); } @@ -556,8 +1358,7 @@ int nghttp3_http_on_header(nghttp3_http_state *http, int64_t frame_type, } if (request) { - rv = http_request_on_header(http, frame_type, nv, trailers, - /* connect_protocol = */ 0); + rv = http_request_on_header(http, nv, trailers, connect_protocol); } else { rv = http_response_on_header(http, nv, trailers); } @@ -594,7 +1395,7 @@ int nghttp3_http_on_request_headers(nghttp3_http_state *http) { (http->flags & NGHTTP3_HTTP_FLAG__AUTHORITY) == 0)) { return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; } - if (!check_path(http)) { + if (!check_path_flags(http)) { return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; } } @@ -609,15 +1410,14 @@ int nghttp3_http_on_response_headers(nghttp3_http_state *http) { if (http->status_code / 100 == 1) { /* non-final response */ - http->flags = (uint16_t)((http->flags & NGHTTP3_HTTP_FLAG_METH_ALL) | - NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE); + http->flags = (http->flags & NGHTTP3_HTTP_FLAG_METH_ALL) | + NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE; http->content_length = -1; http->status_code = -1; return 0; } - http->flags = - (uint16_t)(http->flags & ~NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE); + http->flags &= ~NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE; if (!expect_response_body(http)) { http->content_length = 0; @@ -629,9 +1429,6 @@ int nghttp3_http_on_response_headers(nghttp3_http_state *http) { } int nghttp3_http_on_remote_end_stream(nghttp3_stream *stream) { - if (stream->flags & NGHTTP3_STREAM_FLAG_RESET) { - return 0; - } if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE) || (stream->rx.http.content_length != -1 && stream->rx.http.content_length != stream->rx.http.recv_content_length)) { @@ -835,6 +1632,19 @@ static const int VALID_HD_VALUE_CHARS[] = { int nghttp3_check_header_value(const uint8_t *value, size_t len) { const uint8_t *last; + + switch (len) { + case 0: + return 1; + case 1: + return !(*value == ' ' || *value == '\t'); + default: + if (*value == ' ' || *value == '\t' || *(value + len - 1) == ' ' || + *(value + len - 1) == '\t') { + return 0; + } + } + for (last = value + len; value != last; ++value) { if (!VALID_HD_VALUE_CHARS[*value]) { return 0; diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_http.h b/deps/ngtcp2/nghttp3/lib/nghttp3_http.h index 65ec91759babdc..1617348ad14d78 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_http.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_http.h @@ -39,48 +39,55 @@ typedef struct nghttp3_http_state nghttp3_http_state; /* HTTP related flags to enforce HTTP semantics */ /* NGHTTP3_HTTP_FLAG_NONE indicates that no flag is set. */ -#define NGHTTP3_HTTP_FLAG_NONE 0x00 +#define NGHTTP3_HTTP_FLAG_NONE 0x00u /* header field seen so far */ -#define NGHTTP3_HTTP_FLAG__AUTHORITY 0x01 -#define NGHTTP3_HTTP_FLAG__PATH 0x02 -#define NGHTTP3_HTTP_FLAG__METHOD 0x04 -#define NGHTTP3_HTTP_FLAG__SCHEME 0x08 +#define NGHTTP3_HTTP_FLAG__AUTHORITY 0x01u +#define NGHTTP3_HTTP_FLAG__PATH 0x02u +#define NGHTTP3_HTTP_FLAG__METHOD 0x04u +#define NGHTTP3_HTTP_FLAG__SCHEME 0x08u /* host is not pseudo header, but we require either host or :authority */ -#define NGHTTP3_HTTP_FLAG_HOST 0x10 -#define NGHTTP3_HTTP_FLAG__STATUS 0x20 +#define NGHTTP3_HTTP_FLAG_HOST 0x10u +#define NGHTTP3_HTTP_FLAG__STATUS 0x20u /* required header fields for HTTP request except for CONNECT method. */ #define NGHTTP3_HTTP_FLAG_REQ_HEADERS \ (NGHTTP3_HTTP_FLAG__METHOD | NGHTTP3_HTTP_FLAG__PATH | \ NGHTTP3_HTTP_FLAG__SCHEME) -#define NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED 0x40 +#define NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED 0x40u /* HTTP method flags */ -#define NGHTTP3_HTTP_FLAG_METH_CONNECT 0x80 -#define NGHTTP3_HTTP_FLAG_METH_HEAD 0x0100 -#define NGHTTP3_HTTP_FLAG_METH_OPTIONS 0x0200 +#define NGHTTP3_HTTP_FLAG_METH_CONNECT 0x80u +#define NGHTTP3_HTTP_FLAG_METH_HEAD 0x0100u +#define NGHTTP3_HTTP_FLAG_METH_OPTIONS 0x0200u #define NGHTTP3_HTTP_FLAG_METH_ALL \ (NGHTTP3_HTTP_FLAG_METH_CONNECT | NGHTTP3_HTTP_FLAG_METH_HEAD | \ NGHTTP3_HTTP_FLAG_METH_OPTIONS) /* :path category */ /* path starts with "/" */ -#define NGHTTP3_HTTP_FLAG_PATH_REGULAR 0x0400 +#define NGHTTP3_HTTP_FLAG_PATH_REGULAR 0x0400u /* path "*" */ -#define NGHTTP3_HTTP_FLAG_PATH_ASTERISK 0x0800 +#define NGHTTP3_HTTP_FLAG_PATH_ASTERISK 0x0800u /* scheme */ /* "http" or "https" scheme */ -#define NGHTTP3_HTTP_FLAG_SCHEME_HTTP 0x1000 +#define NGHTTP3_HTTP_FLAG_SCHEME_HTTP 0x1000u /* set if final response is expected */ -#define NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE 0x2000 +#define NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE 0x2000u /* NGHTTP3_HTTP_FLAG__PROTOCOL is set when :protocol pseudo header field is seen. */ -#define NGHTTP3_HTTP_FLAG__PROTOCOL 0x4000 +#define NGHTTP3_HTTP_FLAG__PROTOCOL 0x4000u +/* NGHTTP3_HTTP_FLAG_PRIORITY is set when priority header field is + processed. */ +#define NGHTTP3_HTTP_FLAG_PRIORITY 0x8000u +/* NGHTTP3_HTTP_FLAG_BAD_PRIORITY is set when an error is encountered + while parsing priority header field. */ +#define NGHTTP3_HTTP_FLAG_BAD_PRIORITY 0x010000u /* - * This function is called when HTTP header field |nv| in a frame of type - * |frame_type| is received for |http|. This function will validate |nv| - * against the current state of stream. Pass nonzero if this is request - * headers. Pass nonzero to |trailers| if |nv| is included in trailers. + * This function is called when HTTP header field |nv| received for + * |http|. This function will validate |nv| against the current state + * of stream. Pass nonzero if this is request headers. Pass nonzero + * to |trailers| if |nv| is included in trailers. |connect_protocol| + * is nonzero if Extended CONNECT Method is enabled. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -91,8 +98,8 @@ typedef struct nghttp3_http_state nghttp3_http_state; * Invalid HTTP header field was received but it can be treated as * if it was not received because of compatibility reasons. */ -int nghttp3_http_on_header(nghttp3_http_state *http, int64_t frame_type, - nghttp3_qpack_nv *nv, int request, int trailers); +int nghttp3_http_on_header(nghttp3_http_state *http, nghttp3_qpack_nv *nv, + int request, int trailers, int connect_protocol); /* * This function is called when request header is received. This @@ -143,4 +150,53 @@ int nghttp3_http_on_data_chunk(nghttp3_stream *stream, size_t n); void nghttp3_http_record_request_method(nghttp3_stream *stream, const nghttp3_nv *nva, size_t nvlen); +/* + * RFC 8941 Structured Field Values. + */ +typedef enum nghttp3_sf_value_type { + NGHTTP3_SF_VALUE_TYPE_BOOLEAN, + NGHTTP3_SF_VALUE_TYPE_INTEGER, + NGHTTP3_SF_VALUE_TYPE_DECIMAL, + NGHTTP3_SF_VALUE_TYPE_STRING, + NGHTTP3_SF_VALUE_TYPE_TOKEN, + NGHTTP3_SF_VALUE_TYPE_BYTESEQ, + NGHTTP3_SF_VALUE_TYPE_INNER_LIST, +} nghttp3_sf_value_type; + +/* + * nghttp3_sf_value stores Structured Field Values item. For Inner + * List, only type is set to NGHTTP3_SF_VALUE_TYPE_INNER_LIST. + */ +typedef struct nghttp3_sf_value { + uint8_t type; + union { + int b; + int64_t i; + double d; + struct { + const uint8_t *base; + size_t len; + } s; + }; +} nghttp3_sf_value; + +/* + * nghttp3_sf_parse_item parses the input sequence [|begin|, |end|) + * and stores the parsed an Item in |dest|. It returns the number of + * bytes consumed if it succeeds, or -1. This function is declared + * here for unit tests. + */ +nghttp3_ssize nghttp3_sf_parse_item(nghttp3_sf_value *dest, + const uint8_t *begin, const uint8_t *end); + +/* + * nghttp3_sf_parse_inner_list parses the input sequence [|begin|, |end|) + * and stores the parsed an Inner List in |dest|. It returns the number of + * bytes consumed if it succeeds, or -1. This function is declared + * here for unit tests. + */ +nghttp3_ssize nghttp3_sf_parse_inner_list(nghttp3_sf_value *dest, + const uint8_t *begin, + const uint8_t *end); + #endif /* NGHTTP3_HTTP_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.c b/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.c index cd8fd82e6b2bb5..dc34841fe0f8ef 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.c @@ -27,18 +27,10 @@ #include -int nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem) { - int rv; - - rv = nghttp3_gaptr_init(&idtr->gap, mem); - if (rv != 0) { - return rv; - } +void nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem) { + nghttp3_gaptr_init(&idtr->gap, mem); idtr->server = server; - idtr->mem = mem; - - return 0; } void nghttp3_idtr_free(nghttp3_idtr *idtr) { diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.h b/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.h index c4d60861ab167c..ea3346c9a964c4 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.h @@ -45,24 +45,15 @@ typedef struct nghttp3_idtr { /* server is nonzero if this object records server initiated stream ID. */ int server; - /* mem is custom memory allocator */ - const nghttp3_mem *mem; } nghttp3_idtr; /* - * nghttp3_idtr_init initializes |idtr|. |chunk| is the size of buffer - * per chunk. + * nghttp3_idtr_init initializes |idtr|. * * If this object records server initiated ID (even number), set * |server| to nonzero. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. */ -int nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem); +void nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem); /* * nghttp3_idtr_free frees resources allocated for |idtr|. diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c index 0896cb693f9989..adea677abe1f1c 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c @@ -34,9 +34,11 @@ #include "nghttp3_mem.h" #include "nghttp3_range.h" +static nghttp3_ksl_blk null_blk = {{{NULL, NULL, 0, 0, {0}}}}; + static size_t ksl_nodelen(size_t keylen) { - return (sizeof(nghttp3_ksl_node) + keylen - sizeof(uint64_t) + 0xf) & - (size_t)~0xf; + return (sizeof(nghttp3_ksl_node) + keylen - sizeof(uint64_t) + 0xfu) & + ~(uintptr_t)0xfu; } static size_t ksl_blklen(size_t nodelen) { @@ -52,31 +54,48 @@ static void ksl_node_set_key(nghttp3_ksl *ksl, nghttp3_ksl_node *node, memcpy(node->key, key, ksl->keylen); } -int nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, size_t keylen, - const nghttp3_mem *mem) { +void nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, + size_t keylen, const nghttp3_mem *mem) { size_t nodelen = ksl_nodelen(keylen); - size_t blklen = ksl_blklen(nodelen); - nghttp3_ksl_blk *head; - ksl->head = nghttp3_mem_malloc(mem, blklen); - if (!ksl->head) { - return NGHTTP3_ERR_NOMEM; - } - ksl->front = ksl->back = ksl->head; + nghttp3_objalloc_init(&ksl->blkalloc, + ((ksl_blklen(nodelen) + 0xfu) & ~(uintptr_t)0xfu) * 8, + mem); + + ksl->head = NULL; + ksl->front = ksl->back = NULL; ksl->compar = compar; ksl->keylen = keylen; ksl->nodelen = nodelen; ksl->n = 0; - ksl->mem = mem; +} + +static nghttp3_ksl_blk *ksl_blk_objalloc_new(nghttp3_ksl *ksl) { + return nghttp3_objalloc_ksl_blk_len_get(&ksl->blkalloc, + ksl_blklen(ksl->nodelen)); +} + +static void ksl_blk_objalloc_del(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) { + nghttp3_objalloc_ksl_blk_release(&ksl->blkalloc, blk); +} + +static int ksl_head_init(nghttp3_ksl *ksl) { + nghttp3_ksl_blk *head = ksl_blk_objalloc_new(ksl); + if (!head) { + return NGHTTP3_ERR_NOMEM; + } - head = ksl->head; head->next = head->prev = NULL; head->n = 0; head->leaf = 1; + ksl->head = head; + ksl->front = ksl->back = head; + return 0; } +#ifdef NOMEMPOOL /* * ksl_free_blk frees |blk| recursively. */ @@ -89,15 +108,20 @@ static void ksl_free_blk(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) { } } - nghttp3_mem_free(ksl->mem, blk); + ksl_blk_objalloc_del(ksl, blk); } +#endif /* NOMEMPOOL */ void nghttp3_ksl_free(nghttp3_ksl *ksl) { - if (!ksl) { + if (!ksl || !ksl->head) { return; } +#ifdef NOMEMPOOL ksl_free_blk(ksl, ksl->head); +#endif /* NOMEMPOOL */ + + nghttp3_objalloc_free(&ksl->blkalloc); } /* @@ -111,7 +135,7 @@ void nghttp3_ksl_free(nghttp3_ksl *ksl) { static nghttp3_ksl_blk *ksl_split_blk(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) { nghttp3_ksl_blk *rblk; - rblk = nghttp3_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); + rblk = ksl_blk_objalloc_new(ksl); if (rblk == NULL) { return NULL; } @@ -197,9 +221,9 @@ static int ksl_split_head(nghttp3_ksl *ksl) { lblk = ksl->head; - nhead = nghttp3_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); + nhead = ksl_blk_objalloc_new(ksl); if (nhead == NULL) { - nghttp3_mem_free(ksl->mem, rblk); + ksl_blk_objalloc_del(ksl, rblk); return NGHTTP3_ERR_NOMEM; } nhead->next = nhead->prev = NULL; @@ -246,29 +270,33 @@ static void ksl_insert_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i, static size_t ksl_bsearch(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, const nghttp3_ksl_key *key, nghttp3_ksl_compar compar) { - nghttp3_ssize left = -1, right = (nghttp3_ssize)blk->n, mid; + size_t i; nghttp3_ksl_node *node; - while (right - left > 1) { - mid = (left + right) >> 1; - node = nghttp3_ksl_nth_node(ksl, blk, (size_t)mid); - if (compar((nghttp3_ksl_key *)node->key, key)) { - left = mid; - } else { - right = mid; - } - } + for (i = 0, node = (nghttp3_ksl_node *)(void *)blk->nodes; + i < blk->n && compar((nghttp3_ksl_key *)node->key, key); + ++i, node = (nghttp3_ksl_node *)(void *)((uint8_t *)node + ksl->nodelen)) + ; - return (size_t)right; + return i; } int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it, const nghttp3_ksl_key *key, void *data) { - nghttp3_ksl_blk *blk = ksl->head; + nghttp3_ksl_blk *blk; nghttp3_ksl_node *node; size_t i; int rv; + if (!ksl->head) { + rv = ksl_head_init(ksl); + if (rv != 0) { + return rv; + } + } + + blk = ksl->head; + if (blk->n == NGHTTP3_KSL_MAX_NBLK) { rv = ksl_split_head(ksl); if (rv != 0) { @@ -380,10 +408,10 @@ static nghttp3_ksl_blk *ksl_merge_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, ksl->back = lblk; } - nghttp3_mem_free(ksl->mem, rblk); + ksl_blk_objalloc_del(ksl, rblk); if (ksl->head == blk && blk->n == 2) { - nghttp3_mem_free(ksl->mem, ksl->head); + ksl_blk_objalloc_del(ksl, ksl->head); ksl->head = lblk; } else { ksl_remove_node(ksl, blk, i + 1); @@ -477,12 +505,42 @@ static int key_equal(nghttp3_ksl_compar compar, const nghttp3_ksl_key *lhs, return !compar(lhs, rhs) && !compar(rhs, lhs); } +int nghttp3_ksl_remove_hint(nghttp3_ksl *ksl, nghttp3_ksl_it *it, + const nghttp3_ksl_it *hint, + const nghttp3_ksl_key *key) { + nghttp3_ksl_blk *blk = hint->blk; + + assert(ksl->head); + + if (blk->n <= NGHTTP3_KSL_MIN_NBLK) { + return nghttp3_ksl_remove(ksl, it, key); + } + + ksl_remove_node(ksl, blk, hint->i); + + --ksl->n; + + if (it) { + if (hint->i == blk->n && blk->next) { + nghttp3_ksl_it_init(it, ksl, blk->next, 0); + } else { + nghttp3_ksl_it_init(it, ksl, blk, hint->i); + } + } + + return 0; +} + int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it, const nghttp3_ksl_key *key) { nghttp3_ksl_blk *blk = ksl->head; nghttp3_ksl_node *node; size_t i; + if (!ksl->head) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + if (!blk->leaf && blk->n == 2 && nghttp3_ksl_nth_node(ksl, blk, 0)->blk->n == NGHTTP3_KSL_MIN_NBLK && nghttp3_ksl_nth_node(ksl, blk, 1)->blk->n == NGHTTP3_KSL_MIN_NBLK) { @@ -558,6 +616,11 @@ nghttp3_ksl_it nghttp3_ksl_lower_bound(nghttp3_ksl *ksl, nghttp3_ksl_it it; size_t i; + if (!blk) { + nghttp3_ksl_it_init(&it, ksl, &null_blk, 0); + return it; + } + for (;;) { i = ksl_bsearch(ksl, blk, key, ksl->compar); @@ -595,6 +658,11 @@ nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(nghttp3_ksl *ksl, nghttp3_ksl_it it; size_t i; + if (!blk) { + nghttp3_ksl_it_init(&it, ksl, &null_blk, 0); + return it; + } + for (;;) { i = ksl_bsearch(ksl, blk, key, compar); @@ -631,6 +699,8 @@ void nghttp3_ksl_update_key(nghttp3_ksl *ksl, const nghttp3_ksl_key *old_key, nghttp3_ksl_node *node; size_t i; + assert(ksl->head); + for (;;) { i = ksl_bsearch(ksl, blk, old_key, ksl->compar); @@ -675,36 +745,49 @@ static void ksl_print(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t level) { size_t nghttp3_ksl_len(nghttp3_ksl *ksl) { return ksl->n; } void nghttp3_ksl_clear(nghttp3_ksl *ksl) { - size_t i; - nghttp3_ksl_blk *head; - - if (!ksl->head->leaf) { - for (i = 0; i < ksl->head->n; ++i) { - ksl_free_blk(ksl, nghttp3_ksl_nth_node(ksl, ksl->head, i)->blk); - } + if (!ksl->head) { + return; } - ksl->front = ksl->back = ksl->head; - ksl->n = 0; +#ifdef NOMEMPOOL + ksl_free_blk(ksl, ksl->head); +#endif /* NOMEMPOOL */ - head = ksl->head; + ksl->front = ksl->back = ksl->head = NULL; + ksl->n = 0; - head->next = head->prev = NULL; - head->n = 0; - head->leaf = 1; + nghttp3_objalloc_clear(&ksl->blkalloc); } -void nghttp3_ksl_print(nghttp3_ksl *ksl) { ksl_print(ksl, ksl->head, 0); } +void nghttp3_ksl_print(nghttp3_ksl *ksl) { + if (!ksl->head) { + return; + } + + ksl_print(ksl, ksl->head, 0); +} nghttp3_ksl_it nghttp3_ksl_begin(const nghttp3_ksl *ksl) { nghttp3_ksl_it it; - nghttp3_ksl_it_init(&it, ksl, ksl->front, 0); + + if (ksl->head) { + nghttp3_ksl_it_init(&it, ksl, ksl->front, 0); + } else { + nghttp3_ksl_it_init(&it, ksl, &null_blk, 0); + } + return it; } nghttp3_ksl_it nghttp3_ksl_end(const nghttp3_ksl *ksl) { nghttp3_ksl_it it; - nghttp3_ksl_it_init(&it, ksl, ksl->back, ksl->back->n); + + if (ksl->head) { + nghttp3_ksl_it_init(&it, ksl, ksl->back, ksl->back->n); + } else { + nghttp3_ksl_it_init(&it, ksl, &null_blk, 0); + } + return it; } @@ -715,11 +798,6 @@ void nghttp3_ksl_it_init(nghttp3_ksl_it *it, const nghttp3_ksl *ksl, it->i = i; } -void *nghttp3_ksl_it_get(const nghttp3_ksl_it *it) { - assert(it->i < it->blk->n); - return nghttp3_ksl_nth_node(it->ksl, it->blk, it->i)->data; -} - void nghttp3_ksl_it_prev(nghttp3_ksl_it *it) { assert(!nghttp3_ksl_it_begin(it)); diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h index 7ba36bb9cb107e..0bc10e846fe418 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h @@ -34,6 +34,8 @@ #include +#include "nghttp3_objalloc.h" + /* * Skip List using single key instead of range. */ @@ -80,25 +82,34 @@ struct nghttp3_ksl_node { * nghttp3_ksl_blk contains nghttp3_ksl_node objects. */ struct nghttp3_ksl_blk { - /* next points to the next block if leaf field is nonzero. */ - nghttp3_ksl_blk *next; - /* prev points to the previous block if leaf field is nonzero. */ - nghttp3_ksl_blk *prev; - /* n is the number of nodes this object contains in nodes. */ - uint32_t n; - /* leaf is nonzero if this block contains leaf nodes. */ - uint32_t leaf; union { - uint64_t align; - /* nodes is a buffer to contain NGHTTP3_KSL_MAX_NBLK - nghttp3_ksl_node objects. Because nghttp3_ksl_node object is - allocated along with the additional variable length key - storage, the size of buffer is unknown until nghttp3_ksl_init - is called. */ - uint8_t nodes[1]; + struct { + /* next points to the next block if leaf field is nonzero. */ + nghttp3_ksl_blk *next; + /* prev points to the previous block if leaf field is + nonzero. */ + nghttp3_ksl_blk *prev; + /* n is the number of nodes this object contains in nodes. */ + uint32_t n; + /* leaf is nonzero if this block contains leaf nodes. */ + uint32_t leaf; + union { + uint64_t align; + /* nodes is a buffer to contain NGHTTP3_KSL_MAX_NBLK + nghttp3_ksl_node objects. Because nghttp3_ksl_node object + is allocated along with the additional variable length key + storage, the size of buffer is unknown until + nghttp3_ksl_init is called. */ + uint8_t nodes[1]; + }; + }; + + nghttp3_opl_entry oplent; }; }; +nghttp3_objalloc_def(ksl_blk, nghttp3_ksl_blk, oplent); + /* * nghttp3_ksl_compar is a function type which returns nonzero if key * |lhs| should be placed before |rhs|. It returns 0 otherwise. @@ -123,6 +134,7 @@ struct nghttp3_ksl_it { * nghttp3_ksl is a deterministic paged skip list. */ struct nghttp3_ksl { + nghttp3_objalloc blkalloc; /* head points to the root block. */ nghttp3_ksl_blk *head; /* front points to the first leaf block. */ @@ -136,21 +148,14 @@ struct nghttp3_ksl { /* nodelen is the actual size of nghttp3_ksl_node including key storage. */ size_t nodelen; - const nghttp3_mem *mem; }; /* * nghttp3_ksl_init initializes |ksl|. |compar| specifies compare * function. |keylen| is the length of key. - * - * It returns 0 if it succeeds, or one of the following negative error - * codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. */ -int nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, size_t keylen, - const nghttp3_mem *mem); +void nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, + size_t keylen, const nghttp3_mem *mem); /* * nghttp3_ksl_free frees resources allocated for |ksl|. If |ksl| is @@ -192,6 +197,17 @@ int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it, int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it, const nghttp3_ksl_key *key); +/* + * nghttp3_ksl_remove_hint removes the |key| from |ksl|. |hint| must + * point to the same node denoted by |key|. |hint| is used to remove + * a node efficiently in some cases. Other than that, it behaves + * exactly like nghttp3_ksl_remove. |it| and |hint| can point to the + * same object. + */ +int nghttp3_ksl_remove_hint(nghttp3_ksl *ksl, nghttp3_ksl_it *it, + const nghttp3_ksl_it *hint, + const nghttp3_ksl_key *key); + /* * nghttp3_ksl_lower_bound returns the iterator which points to the * first node which has the key which is equal to |key| or the last @@ -267,7 +283,8 @@ void nghttp3_ksl_it_init(nghttp3_ksl_it *it, const nghttp3_ksl *ksl, * |it| points to. It is undefined to call this function when * nghttp3_ksl_it_end(it) returns nonzero. */ -void *nghttp3_ksl_it_get(const nghttp3_ksl_it *it); +#define nghttp3_ksl_it_get(IT) \ + nghttp3_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->data /* * nghttp3_ksl_it_next advances the iterator by one. It is undefined diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h b/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h index 6ee704cc47dd98..a44e907661abbf 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h @@ -44,4 +44,8 @@ #define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0) +/* NGHTTP3_MAX_VARINT` is the maximum value which can be encoded in + variable-length integer encoding. */ +#define NGHTTP3_MAX_VARINT ((1ULL << 62) - 1) + #endif /* NGHTTP3_MACRO_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_map.c b/deps/ngtcp2/nghttp3/lib/nghttp3_map.c index a80955ad7a4c83..fcfc31ae41e8e0 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_map.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_map.c @@ -28,172 +28,166 @@ #include #include +#include #include "nghttp3_conv.h" -#define INITIAL_TABLE_LENGTH 256 +#define NGHTTP3_INITIAL_TABLE_LENBITS 4 -int nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem) { +void nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem) { map->mem = mem; - map->tablelen = INITIAL_TABLE_LENGTH; - map->table = - nghttp3_mem_calloc(mem, map->tablelen, sizeof(nghttp3_map_bucket)); - if (map->table == NULL) { - return NGHTTP3_ERR_NOMEM; - } - + map->tablelen = 0; + map->tablelenbits = 0; + map->table = NULL; map->size = 0; - - return 0; } void nghttp3_map_free(nghttp3_map *map) { - size_t i; - nghttp3_map_bucket *bkt; - if (!map) { return; } - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - if (bkt->ksl) { - nghttp3_ksl_free(bkt->ksl); - nghttp3_mem_free(map->mem, bkt->ksl); - } - } - nghttp3_mem_free(map->mem, map->table); } -void nghttp3_map_each_free(nghttp3_map *map, - int (*func)(nghttp3_map_entry *entry, void *ptr), +void nghttp3_map_each_free(nghttp3_map *map, int (*func)(void *data, void *ptr), void *ptr) { uint32_t i; nghttp3_map_bucket *bkt; - nghttp3_ksl_it it; for (i = 0; i < map->tablelen; ++i) { bkt = &map->table[i]; - if (bkt->ptr) { - func(bkt->ptr, ptr); - bkt->ptr = NULL; - assert(bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0); + if (bkt->data == NULL) { continue; } - if (bkt->ksl) { - for (it = nghttp3_ksl_begin(bkt->ksl); !nghttp3_ksl_it_end(&it); - nghttp3_ksl_it_next(&it)) { - func(nghttp3_ksl_it_get(&it), ptr); - } - - nghttp3_ksl_free(bkt->ksl); - nghttp3_mem_free(map->mem, bkt->ksl); - bkt->ksl = NULL; - } + func(bkt->data, ptr); } } -int nghttp3_map_each(nghttp3_map *map, - int (*func)(nghttp3_map_entry *entry, void *ptr), +int nghttp3_map_each(nghttp3_map *map, int (*func)(void *data, void *ptr), void *ptr) { int rv; uint32_t i; nghttp3_map_bucket *bkt; - nghttp3_ksl_it it; + + if (map->size == 0) { + return 0; + } for (i = 0; i < map->tablelen; ++i) { bkt = &map->table[i]; - if (bkt->ptr) { - rv = func(bkt->ptr, ptr); - if (rv != 0) { - return rv; - } - assert(bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0); + if (bkt->data == NULL) { continue; } - if (bkt->ksl) { - for (it = nghttp3_ksl_begin(bkt->ksl); !nghttp3_ksl_it_end(&it); - nghttp3_ksl_it_next(&it)) { - rv = func(nghttp3_ksl_it_get(&it), ptr); - if (rv != 0) { - return rv; - } - } + rv = func(bkt->data, ptr); + if (rv != 0) { + return rv; } } + return 0; } -void nghttp3_map_entry_init(nghttp3_map_entry *entry, key_type key) { - entry->key = key; +static uint32_t hash(nghttp3_map_key_type key) { + return (uint32_t)((key * 11400714819323198485llu) >> 32); } -/* FNV1a hash */ -static uint32_t hash(key_type key, uint32_t mod) { - uint8_t *p, *end; - uint32_t h = 0x811C9DC5u; +static size_t h2idx(uint32_t hash, uint32_t bits) { + return hash >> (32 - bits); +} - key = nghttp3_htonl64(key); - p = (uint8_t *)&key; - end = p + sizeof(key_type); +static size_t distance(uint32_t tablelen, uint32_t tablelenbits, + nghttp3_map_bucket *bkt, size_t idx) { + return (idx - h2idx(bkt->hash, tablelenbits)) & (tablelen - 1); +} - for (; p != end;) { - h ^= *p++; - h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24); - } +static void map_bucket_swap(nghttp3_map_bucket *bkt, uint32_t *phash, + nghttp3_map_key_type *pkey, void **pdata) { + uint32_t h = bkt->hash; + nghttp3_map_key_type key = bkt->key; + void *data = bkt->data; - return h & (mod - 1); + bkt->hash = *phash; + bkt->key = *pkey; + bkt->data = *pdata; + + *phash = h; + *pkey = key; + *pdata = data; } -static int less(const nghttp3_ksl_key *lhs, const nghttp3_ksl_key *rhs) { - return *(key_type *)lhs < *(key_type *)rhs; +static void map_bucket_set_data(nghttp3_map_bucket *bkt, uint32_t hash, + nghttp3_map_key_type key, void *data) { + bkt->hash = hash; + bkt->key = key; + bkt->data = data; } -static int map_insert(nghttp3_map *map, nghttp3_map_bucket *table, - uint32_t tablelen, nghttp3_map_entry *entry) { - uint32_t h = hash(entry->key, tablelen); - nghttp3_map_bucket *bkt = &table[h]; - const nghttp3_mem *mem = map->mem; - int rv; +void nghttp3_map_print_distance(nghttp3_map *map) { + uint32_t i; + size_t idx; + nghttp3_map_bucket *bkt; - if (bkt->ptr == NULL && - (bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0)) { - bkt->ptr = entry; - return 0; - } + for (i = 0; i < map->tablelen; ++i) { + bkt = &map->table[i]; - if (!bkt->ksl) { - bkt->ksl = nghttp3_mem_malloc(mem, sizeof(*bkt->ksl)); - if (bkt->ksl == NULL) { - return NGHTTP3_ERR_NOMEM; + if (bkt->data == NULL) { + fprintf(stderr, "@%u \n", i); + continue; } - nghttp3_ksl_init(bkt->ksl, less, sizeof(key_type), mem); + + idx = h2idx(bkt->hash, map->tablelenbits); + fprintf(stderr, "@%u hash=%08x key=%" PRIu64 " base=%zu distance=%zu\n", i, + bkt->hash, bkt->key, idx, + distance(map->tablelen, map->tablelenbits, bkt, idx)); } +} - if (bkt->ptr) { - rv = nghttp3_ksl_insert(bkt->ksl, NULL, &bkt->ptr->key, bkt->ptr); - if (rv != 0) { - return rv; +static int insert(nghttp3_map_bucket *table, uint32_t tablelen, + uint32_t tablelenbits, uint32_t hash, + nghttp3_map_key_type key, void *data) { + size_t idx = h2idx(hash, tablelenbits); + size_t d = 0, dd; + nghttp3_map_bucket *bkt; + + for (;;) { + bkt = &table[idx]; + + if (bkt->data == NULL) { + map_bucket_set_data(bkt, hash, key, data); + return 0; } - bkt->ptr = NULL; - } + dd = distance(tablelen, tablelenbits, bkt, idx); + if (d > dd) { + map_bucket_swap(bkt, &hash, &key, &data); + d = dd; + } else if (bkt->key == key) { + /* TODO This check is just a waste after first swap or if this + function is called from map_resize. That said, there is no + difference with or without this conditional in performance + wise. */ + return NGHTTP3_ERR_INVALID_ARGUMENT; + } - return nghttp3_ksl_insert(bkt->ksl, NULL, &entry->key, entry); + ++d; + idx = (idx + 1) & (tablelen - 1); + } } -/* new_tablelen must be power of 2 */ -static int map_resize(nghttp3_map *map, uint32_t new_tablelen) { +/* new_tablelen must be power of 2 and new_tablelen == (1 << + new_tablelenbits) must hold. */ +static int map_resize(nghttp3_map *map, uint32_t new_tablelen, + uint32_t new_tablelenbits) { uint32_t i; nghttp3_map_bucket *new_table; nghttp3_map_bucket *bkt; - nghttp3_ksl_it it; int rv; + (void)rv; new_table = nghttp3_mem_calloc(map->mem, new_tablelen, sizeof(nghttp3_map_bucket)); @@ -203,64 +197,46 @@ static int map_resize(nghttp3_map *map, uint32_t new_tablelen) { for (i = 0; i < map->tablelen; ++i) { bkt = &map->table[i]; - - if (bkt->ptr) { - rv = map_insert(map, new_table, new_tablelen, bkt->ptr); - if (rv != 0) { - goto fail; - } - assert(bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0); + if (bkt->data == NULL) { continue; } + rv = insert(new_table, new_tablelen, new_tablelenbits, bkt->hash, bkt->key, + bkt->data); - if (bkt->ksl) { - for (it = nghttp3_ksl_begin(bkt->ksl); !nghttp3_ksl_it_end(&it); - nghttp3_ksl_it_next(&it)) { - rv = map_insert(map, new_table, new_tablelen, nghttp3_ksl_it_get(&it)); - if (rv != 0) { - goto fail; - } - } - } - } - - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - if (bkt->ksl) { - nghttp3_ksl_free(bkt->ksl); - nghttp3_mem_free(map->mem, bkt->ksl); - } + assert(0 == rv); } nghttp3_mem_free(map->mem, map->table); map->tablelen = new_tablelen; + map->tablelenbits = new_tablelenbits; map->table = new_table; return 0; - -fail: - for (i = 0; i < new_tablelen; ++i) { - bkt = &new_table[i]; - if (bkt->ksl) { - nghttp3_ksl_free(bkt->ksl); - nghttp3_mem_free(map->mem, bkt->ksl); - } - } - - return rv; } -int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_entry *new_entry) { +int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_key_type key, void *data) { int rv; + assert(data); + /* Load factor is 0.75 */ if ((map->size + 1) * 4 > map->tablelen * 3) { - rv = map_resize(map, map->tablelen * 2); - if (rv != 0) { - return rv; + if (map->tablelen) { + rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1); + if (rv != 0) { + return rv; + } + } else { + rv = map_resize(map, 1 << NGHTTP3_INITIAL_TABLE_LENBITS, + NGHTTP3_INITIAL_TABLE_LENBITS); + if (rv != 0) { + return rv; + } } } - rv = map_insert(map, map->table, map->tablelen, new_entry); + + rv = insert(map->table, map->tablelen, map->tablelenbits, hash(key), key, + data); if (rv != 0) { return rv; } @@ -268,68 +244,93 @@ int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_entry *new_entry) { return 0; } -nghttp3_map_entry *nghttp3_map_find(nghttp3_map *map, key_type key) { - nghttp3_map_bucket *bkt = &map->table[hash(key, map->tablelen)]; - nghttp3_ksl_it it; +void *nghttp3_map_find(nghttp3_map *map, nghttp3_map_key_type key) { + uint32_t h; + size_t idx; + nghttp3_map_bucket *bkt; + size_t d = 0; - if (bkt->ptr) { - if (bkt->ptr->key == key) { - return bkt->ptr; - } + if (map->size == 0) { return NULL; } - if (bkt->ksl) { - it = nghttp3_ksl_lower_bound(bkt->ksl, &key); - if (nghttp3_ksl_it_end(&it) || - *(key_type *)nghttp3_ksl_it_key(&it) != key) { + h = hash(key); + idx = h2idx(h, map->tablelenbits); + + for (;;) { + bkt = &map->table[idx]; + + if (bkt->data == NULL || + d > distance(map->tablelen, map->tablelenbits, bkt, idx)) { return NULL; } - return nghttp3_ksl_it_get(&it); - } - return NULL; + if (bkt->key == key) { + return bkt->data; + } + + ++d; + idx = (idx + 1) & (map->tablelen - 1); + } } -int nghttp3_map_remove(nghttp3_map *map, key_type key) { - nghttp3_map_bucket *bkt = &map->table[hash(key, map->tablelen)]; - int rv; +int nghttp3_map_remove(nghttp3_map *map, nghttp3_map_key_type key) { + uint32_t h; + size_t idx, didx; + nghttp3_map_bucket *bkt; + size_t d = 0; - if (bkt->ptr) { - if (bkt->ptr->key == key) { - bkt->ptr = NULL; - --map->size; - return 0; - } + if (map->size == 0) { return NGHTTP3_ERR_INVALID_ARGUMENT; } - if (bkt->ksl) { - rv = nghttp3_ksl_remove(bkt->ksl, NULL, &key); - if (rv != 0) { - return rv; + h = hash(key); + idx = h2idx(h, map->tablelenbits); + + for (;;) { + bkt = &map->table[idx]; + + if (bkt->data == NULL || + d > distance(map->tablelen, map->tablelenbits, bkt, idx)) { + return NGHTTP3_ERR_INVALID_ARGUMENT; } - --map->size; - return 0; - } - return NGHTTP3_ERR_INVALID_ARGUMENT; -} + if (bkt->key == key) { + map_bucket_set_data(bkt, 0, 0, NULL); -void nghttp3_map_clear(nghttp3_map *map) { - uint32_t i; - nghttp3_map_bucket *bkt; + didx = idx; + idx = (idx + 1) & (map->tablelen - 1); - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - bkt->ptr = NULL; - if (bkt->ksl) { - nghttp3_ksl_free(bkt->ksl); - nghttp3_mem_free(map->mem, bkt->ksl); - bkt->ksl = NULL; + for (;;) { + bkt = &map->table[idx]; + if (bkt->data == NULL || + distance(map->tablelen, map->tablelenbits, bkt, idx) == 0) { + break; + } + + map->table[didx] = *bkt; + map_bucket_set_data(bkt, 0, 0, NULL); + didx = idx; + + idx = (idx + 1) & (map->tablelen - 1); + } + + --map->size; + + return 0; } + + ++d; + idx = (idx + 1) & (map->tablelen - 1); + } +} + +void nghttp3_map_clear(nghttp3_map *map) { + if (map->tablelen == 0) { + return; } + memset(map->table, 0, sizeof(*map->table) * map->tablelen); map->size = 0; } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_map.h b/deps/ngtcp2/nghttp3/lib/nghttp3_map.h index 2eae2e7cb725c6..79dff0286bc3cc 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_map.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_map.h @@ -34,21 +34,15 @@ #include #include "nghttp3_mem.h" -#include "nghttp3_ksl.h" /* Implementation of unordered map */ -typedef uint64_t key_type; - -typedef struct nghttp3_map_entry nghttp3_map_entry; - -struct nghttp3_map_entry { - key_type key; -}; +typedef uint64_t nghttp3_map_key_type; typedef struct nghttp3_map_bucket { - nghttp3_map_entry *ptr; - nghttp3_ksl *ksl; + uint32_t hash; + nghttp3_map_key_type key; + void *data; } nghttp3_map_bucket; typedef struct nghttp3_map { @@ -56,18 +50,13 @@ typedef struct nghttp3_map { const nghttp3_mem *mem; size_t size; uint32_t tablelen; + uint32_t tablelenbits; } nghttp3_map; /* * Initializes the map |map|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory */ -int nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem); +void nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem); /* * Deallocates any resources allocated for |map|. The stored entries @@ -79,21 +68,14 @@ void nghttp3_map_free(nghttp3_map *map); /* * Deallocates each entries using |func| function and any resources * allocated for |map|. The |func| function is responsible for freeing - * given the |entry| object. The |ptr| will be passed to the |func| as + * given the |data| object. The |ptr| will be passed to the |func| as * send argument. The return value of the |func| will be ignored. */ -void nghttp3_map_each_free(nghttp3_map *map, - int (*func)(nghttp3_map_entry *entry, void *ptr), +void nghttp3_map_each_free(nghttp3_map *map, int (*func)(void *data, void *ptr), void *ptr); /* - * Initializes the |entry| with the |key|. All entries to be inserted - * to the map must be initialized with this function. - */ -void nghttp3_map_entry_init(nghttp3_map_entry *entry, key_type key); - -/* - * Inserts the new |entry| with the key |entry->key| to the map |map|. + * Inserts the new |data| with the |key| to the map |map|. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -103,25 +85,25 @@ void nghttp3_map_entry_init(nghttp3_map_entry *entry, key_type key); * NGHTTP3_ERR_NOMEM * Out of memory */ -int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_entry *entry); +int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_key_type key, void *data); /* - * Returns the entry associated by the key |key|. If there is no such - * entry, this function returns NULL. + * Returns the data associated by the key |key|. If there is no such + * data, this function returns NULL. */ -nghttp3_map_entry *nghttp3_map_find(nghttp3_map *map, key_type key); +void *nghttp3_map_find(nghttp3_map *map, nghttp3_map_key_type key); /* - * Removes the entry associated by the key |key| from the |map|. The - * removed entry is not freed by this function. + * Removes the data associated by the key |key| from the |map|. The + * removed data is not freed by this function. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP3_ERR_INVALID_ARGUMENT - * The entry associated by |key| does not exist. + * The data associated by |key| does not exist. */ -int nghttp3_map_remove(nghttp3_map *map, key_type key); +int nghttp3_map_remove(nghttp3_map *map, nghttp3_map_key_type key); /* * Removes all entries from |map|. @@ -134,21 +116,22 @@ void nghttp3_map_clear(nghttp3_map *map); size_t nghttp3_map_size(nghttp3_map *map); /* - * Applies the function |func| to each entry in the |map| with the + * Applies the function |func| to each data in the |map| with the * optional user supplied pointer |ptr|. * * If the |func| returns 0, this function calls the |func| with the - * next entry. If the |func| returns nonzero, it will not call the + * next data. If the |func| returns nonzero, it will not call the * |func| for further entries and return the return value of the * |func| immediately. Thus, this function returns 0 if all the * invocations of the |func| return 0, or nonzero value which the last * invocation of |func| returns. * - * Don't use this function to free each entry. Use + * Don't use this function to free each data. Use * nghttp3_map_each_free() instead. */ -int nghttp3_map_each(nghttp3_map *map, - int (*func)(nghttp3_map_entry *entry, void *ptr), +int nghttp3_map_each(nghttp3_map *map, int (*func)(void *data, void *ptr), void *ptr); +void nghttp3_map_print_distance(nghttp3_map *map); + #endif /* NGHTTP3_MAP_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_mem.c b/deps/ngtcp2/nghttp3/lib/nghttp3_mem.c index e5f93f10bdabf2..0379e99b59cb74 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_mem.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_mem.c @@ -26,26 +26,28 @@ */ #include "nghttp3_mem.h" -static void *default_malloc(size_t size, void *mem_user_data) { - (void)mem_user_data; +#include + +static void *default_malloc(size_t size, void *user_data) { + (void)user_data; return malloc(size); } -static void default_free(void *ptr, void *mem_user_data) { - (void)mem_user_data; +static void default_free(void *ptr, void *user_data) { + (void)user_data; free(ptr); } -static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) { - (void)mem_user_data; +static void *default_calloc(size_t nmemb, size_t size, void *user_data) { + (void)user_data; return calloc(nmemb, size); } -static void *default_realloc(void *ptr, size_t size, void *mem_user_data) { - (void)mem_user_data; +static void *default_realloc(void *ptr, size_t size, void *user_data) { + (void)user_data; return realloc(ptr, size); } @@ -55,23 +57,68 @@ static nghttp3_mem mem_default = {NULL, default_malloc, default_free, const nghttp3_mem *nghttp3_mem_default(void) { return &mem_default; } +#ifndef MEMDEBUG void *nghttp3_mem_malloc(const nghttp3_mem *mem, size_t size) { - return mem->malloc(size, mem->mem_user_data); + return mem->malloc(size, mem->user_data); } void nghttp3_mem_free(const nghttp3_mem *mem, void *ptr) { - mem->free(ptr, mem->mem_user_data); -} - -void nghttp3_mem_free2(const nghttp3_free free_func, void *ptr, - void *mem_user_data) { - free_func(ptr, mem_user_data); + mem->free(ptr, mem->user_data); } void *nghttp3_mem_calloc(const nghttp3_mem *mem, size_t nmemb, size_t size) { - return mem->calloc(nmemb, size, mem->mem_user_data); + return mem->calloc(nmemb, size, mem->user_data); } void *nghttp3_mem_realloc(const nghttp3_mem *mem, void *ptr, size_t size) { - return mem->realloc(ptr, size, mem->mem_user_data); + return mem->realloc(ptr, size, mem->user_data); +} +#else /* MEMDEBUG */ +void *nghttp3_mem_malloc_debug(const nghttp3_mem *mem, size_t size, + const char *func, const char *file, + size_t line) { + void *nptr = mem->malloc(size, mem->user_data); + + fprintf(stderr, "malloc %p size=%zu in %s at %s:%zu\n", nptr, size, func, + file, line); + + return nptr; +} + +void nghttp3_mem_free_debug(const nghttp3_mem *mem, void *ptr, const char *func, + const char *file, size_t line) { + fprintf(stderr, "free ptr=%p in %s at %s:%zu\n", ptr, func, file, line); + + mem->free(ptr, mem->user_data); +} + +void nghttp3_mem_free2_debug(const nghttp3_free free_func, void *ptr, + void *user_data, const char *func, + const char *file, size_t line) { + fprintf(stderr, "free ptr=%p in %s at %s:%zu\n", ptr, func, file, line); + + free_func(ptr, user_data); +} + +void *nghttp3_mem_calloc_debug(const nghttp3_mem *mem, size_t nmemb, + size_t size, const char *func, const char *file, + size_t line) { + void *nptr = mem->calloc(nmemb, size, mem->user_data); + + fprintf(stderr, "calloc %p nmemb=%zu size=%zu in %s at %s:%zu\n", nptr, nmemb, + size, func, file, line); + + return nptr; +} + +void *nghttp3_mem_realloc_debug(const nghttp3_mem *mem, void *ptr, size_t size, + const char *func, const char *file, + size_t line) { + void *nptr = mem->realloc(ptr, size, mem->user_data); + + fprintf(stderr, "realloc %p ptr=%p size=%zu in %s at %s:%zu\n", nptr, ptr, + size, func, file, line); + + return nptr; } +#endif /* MEMDEBUG */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_mem.h b/deps/ngtcp2/nghttp3/lib/nghttp3_mem.h index 55ef86b4f921c9..d6c3ada6f7e894 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_mem.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_mem.h @@ -35,11 +35,46 @@ /* Convenient wrapper functions to call allocator function in |mem|. */ +#ifndef MEMDEBUG void *nghttp3_mem_malloc(const nghttp3_mem *mem, size_t size); void nghttp3_mem_free(const nghttp3_mem *mem, void *ptr); -void nghttp3_mem_free2(const nghttp3_free free_func, void *ptr, - void *mem_user_data); void *nghttp3_mem_calloc(const nghttp3_mem *mem, size_t nmemb, size_t size); void *nghttp3_mem_realloc(const nghttp3_mem *mem, void *ptr, size_t size); +#else /* MEMDEBUG */ +void *nghttp3_mem_malloc_debug(const nghttp3_mem *mem, size_t size, + const char *func, const char *file, size_t line); + +# define nghttp3_mem_malloc(MEM, SIZE) \ + nghttp3_mem_malloc_debug((MEM), (SIZE), __func__, __FILE__, __LINE__) + +void nghttp3_mem_free_debug(const nghttp3_mem *mem, void *ptr, const char *func, + const char *file, size_t line); + +# define nghttp3_mem_free(MEM, PTR) \ + nghttp3_mem_free_debug((MEM), (PTR), __func__, __FILE__, __LINE__) + +void nghttp3_mem_free2_debug(nghttp3_free free_func, void *ptr, void *user_data, + const char *func, const char *file, size_t line); + +# define nghttp3_mem_free2(FREE_FUNC, PTR, USER_DATA) \ + nghttp3_mem_free2_debug((FREE_FUNC), (PTR), (USER_DATA), __func__, \ + __FILE__, __LINE__) + +void *nghttp3_mem_calloc_debug(const nghttp3_mem *mem, size_t nmemb, + size_t size, const char *func, const char *file, + size_t line); + +# define nghttp3_mem_calloc(MEM, NMEMB, SIZE) \ + nghttp3_mem_calloc_debug((MEM), (NMEMB), (SIZE), __func__, __FILE__, \ + __LINE__) + +void *nghttp3_mem_realloc_debug(const nghttp3_mem *mem, void *ptr, size_t size, + const char *func, const char *file, + size_t line); + +# define nghttp3_mem_realloc(MEM, PTR, SIZE) \ + nghttp3_mem_realloc_debug((MEM), (PTR), (SIZE), __func__, __FILE__, \ + __LINE__) +#endif /* MEMDEBUG */ #endif /* NGHTTP3_MEM_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_objalloc.c b/deps/ngtcp2/nghttp3/lib/nghttp3_objalloc.c new file mode 100644 index 00000000000000..0c97860136ed34 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_objalloc.c @@ -0,0 +1,41 @@ +/* + * nghttp3 + * + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_objalloc.h" + +void nghttp3_objalloc_init(nghttp3_objalloc *objalloc, size_t blklen, + const nghttp3_mem *mem) { + nghttp3_balloc_init(&objalloc->balloc, blklen, mem); + nghttp3_opl_init(&objalloc->opl); +} + +void nghttp3_objalloc_free(nghttp3_objalloc *objalloc) { + nghttp3_balloc_free(&objalloc->balloc); +} + +void nghttp3_objalloc_clear(nghttp3_objalloc *objalloc) { + nghttp3_opl_clear(&objalloc->opl); + nghttp3_balloc_clear(&objalloc->balloc); +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_objalloc.h b/deps/ngtcp2/nghttp3/lib/nghttp3_objalloc.h new file mode 100644 index 00000000000000..da39447a872b02 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_objalloc.h @@ -0,0 +1,141 @@ +/* + * nghttp3 + * + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_OBJALLOC_H +#define NGHTTP3_OBJALLOC_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp3_balloc.h" +#include "nghttp3_opl.h" +#include "nghttp3_macro.h" +#include "nghttp3_mem.h" + +/* + * nghttp3_objalloc combines nghttp3_balloc and nghttp3_opl, and + * provides an object pool with the custom allocator to reduce the + * allocation and deallocation overheads for small objects. + */ +typedef struct nghttp3_objalloc { + nghttp3_balloc balloc; + nghttp3_opl opl; +} nghttp3_objalloc; + +/* + * nghttp3_objalloc_init initializes |objalloc|. |blklen| is directly + * passed to nghttp3_balloc_init. + */ +void nghttp3_objalloc_init(nghttp3_objalloc *objalloc, size_t blklen, + const nghttp3_mem *mem); + +/* + * nghttp3_objalloc_free releases all allocated resources. + */ +void nghttp3_objalloc_free(nghttp3_objalloc *objalloc); + +/* + * nghttp3_objalloc_clear releases all allocated resources and + * initializes its state. + */ +void nghttp3_objalloc_clear(nghttp3_objalloc *objalloc); + +#ifndef NOMEMPOOL +# define nghttp3_objalloc_def(NAME, TYPE, OPLENTFIELD) \ + inline static void nghttp3_objalloc_##NAME##_init( \ + nghttp3_objalloc *objalloc, size_t nmemb, const nghttp3_mem *mem) { \ + nghttp3_objalloc_init( \ + objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \ + } \ + \ + inline static TYPE *nghttp3_objalloc_##NAME##_get( \ + nghttp3_objalloc *objalloc) { \ + nghttp3_opl_entry *oplent = nghttp3_opl_pop(&objalloc->opl); \ + TYPE *obj; \ + int rv; \ + \ + if (!oplent) { \ + rv = nghttp3_balloc_get(&objalloc->balloc, (void **)&obj, \ + sizeof(TYPE)); \ + if (rv != 0) { \ + return NULL; \ + } \ + \ + return obj; \ + } \ + \ + return nghttp3_struct_of(oplent, TYPE, OPLENTFIELD); \ + } \ + \ + inline static TYPE *nghttp3_objalloc_##NAME##_len_get( \ + nghttp3_objalloc *objalloc, size_t len) { \ + nghttp3_opl_entry *oplent = nghttp3_opl_pop(&objalloc->opl); \ + TYPE *obj; \ + int rv; \ + \ + if (!oplent) { \ + rv = nghttp3_balloc_get(&objalloc->balloc, (void **)&obj, len); \ + if (rv != 0) { \ + return NULL; \ + } \ + \ + return obj; \ + } \ + \ + return nghttp3_struct_of(oplent, TYPE, OPLENTFIELD); \ + } \ + \ + inline static void nghttp3_objalloc_##NAME##_release( \ + nghttp3_objalloc *objalloc, TYPE *obj) { \ + nghttp3_opl_push(&objalloc->opl, &obj->OPLENTFIELD); \ + } +#else /* NOMEMPOOL */ +# define nghttp3_objalloc_def(NAME, TYPE, OPLENTFIELD) \ + inline static void nghttp3_objalloc_##NAME##_init( \ + nghttp3_objalloc *objalloc, size_t nmemb, const nghttp3_mem *mem) { \ + nghttp3_objalloc_init( \ + objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \ + } \ + \ + inline static TYPE *nghttp3_objalloc_##NAME##_get( \ + nghttp3_objalloc *objalloc) { \ + return nghttp3_mem_malloc(objalloc->balloc.mem, sizeof(TYPE)); \ + } \ + \ + inline static TYPE *nghttp3_objalloc_##NAME##_len_get( \ + nghttp3_objalloc *objalloc, size_t len) { \ + return nghttp3_mem_malloc(objalloc->balloc.mem, len); \ + } \ + \ + inline static void nghttp3_objalloc_##NAME##_release( \ + nghttp3_objalloc *objalloc, TYPE *obj) { \ + nghttp3_mem_free(objalloc->balloc.mem, obj); \ + } +#endif /* NOMEMPOOL */ + +#endif /* NGHTTP3_OBJALLOC_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_opl.c b/deps/ngtcp2/nghttp3/lib/nghttp3_opl.c new file mode 100644 index 00000000000000..eb8ebdd1854c8f --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_opl.c @@ -0,0 +1,47 @@ +/* + * nghttp3 + * + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_opl.h" + +void nghttp3_opl_init(nghttp3_opl *opl) { opl->head = NULL; } + +void nghttp3_opl_push(nghttp3_opl *opl, nghttp3_opl_entry *ent) { + ent->next = opl->head; + opl->head = ent; +} + +nghttp3_opl_entry *nghttp3_opl_pop(nghttp3_opl *opl) { + nghttp3_opl_entry *ent = opl->head; + + if (!ent) { + return NULL; + } + + opl->head = ent->next; + + return ent; +} + +void nghttp3_opl_clear(nghttp3_opl *opl) { opl->head = NULL; } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_opl.h b/deps/ngtcp2/nghttp3/lib/nghttp3_opl.h new file mode 100644 index 00000000000000..8c8a4f2051b25a --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_opl.h @@ -0,0 +1,66 @@ +/* + * nghttp3 + * + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_OPL_H +#define NGHTTP3_OPL_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +typedef struct nghttp3_opl_entry nghttp3_opl_entry; + +struct nghttp3_opl_entry { + nghttp3_opl_entry *next; +}; + +/* + * nghttp3_opl is an object memory pool. + */ +typedef struct nghttp3_opl { + nghttp3_opl_entry *head; +} nghttp3_opl; + +/* + * nghttp3_opl_init initializes |opl|. + */ +void nghttp3_opl_init(nghttp3_opl *opl); + +/* + * nghttp3_opl_push inserts |ent| to |opl| head. + */ +void nghttp3_opl_push(nghttp3_opl *opl, nghttp3_opl_entry *ent); + +/* + * nghttp3_opl_pop removes the first nghttp3_opl_entry from |opl| and + * returns it. If |opl| does not have any entry, it returns NULL. + */ +nghttp3_opl_entry *nghttp3_opl_pop(nghttp3_opl *opl); + +void nghttp3_opl_clear(nghttp3_opl *opl); + +#endif /* NGHTTP3_OPL_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h b/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h index f1f369231dfcdc..c1a54f505bbd03 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h @@ -111,7 +111,7 @@ size_t nghttp3_pq_size(const nghttp3_pq *pq); typedef int (*nghttp3_pq_item_cb)(nghttp3_pq_entry *item, void *arg); /* - * Applys |fun| to each item in |pq|. The |arg| is passed as arg + * Applies |fun| to each item in |pq|. The |arg| is passed as arg * parameter to callback function. This function must not change the * ordering key. If the return value from callback is nonzero, this * function returns 1 immediately without iterating remaining items. diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c index 6837942687aa88..ddb3dd6d84bf8a 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c @@ -168,8 +168,8 @@ static nghttp3_qpack_static_entry token_stable[] = { /* Make scalar initialization form of nghttp3_qpack_static_entry */ #define MAKE_STATIC_HD(N, V, T) \ { \ - {NULL, NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \ - {NULL, NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, T \ + {NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \ + {NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, T \ } static nghttp3_qpack_static_header stable[] = { @@ -775,12 +775,12 @@ static void qpack_map_remove(nghttp3_qpack_map *map, nghttp3_qpack_entry *ent) { /* * qpack_context_can_reference returns nonzero if dynamic table entry * at |absidx| can be referenced. In other words, it is within - * ctx->max_dtable_size. + * ctx->max_dtable_capacity. */ static int qpack_context_can_reference(nghttp3_qpack_context *ctx, uint64_t absidx) { nghttp3_qpack_entry *ent = nghttp3_qpack_context_dtable_get(ctx, absidx); - return ctx->dtable_sum - ent->sum <= ctx->max_dtable_size; + return ctx->dtable_sum - ent->sum <= ctx->max_dtable_capacity; } /* |*ppb_match| (post-base match), if it is not NULL, is always exact @@ -824,8 +824,14 @@ static void encoder_qpack_map_find(nghttp3_qpack_encoder *encoder, } /* - * qpack_context_init initializes |ctx|. |max_dtable_size| is the - * maximum size of dynamic table. |mem| is a memory allocator. + * qpack_context_init initializes |ctx|. |hard_max_dtable_capacity| + * is the upper bound of the dynamic table capacity. |mem| is a + * memory allocator. + * + * The maximum dynamic table size is governed by + * ctx->max_dtable_capacity and it is initialized to 0. + * |hard_max_dtable_capacity| is the upper bound of + * ctx->max_dtable_capacity. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -834,7 +840,8 @@ static void encoder_qpack_map_find(nghttp3_qpack_encoder *encoder, * Out of memory. */ static int qpack_context_init(nghttp3_qpack_context *ctx, - size_t max_dtable_size, size_t max_blocked, + size_t hard_max_dtable_capacity, + size_t max_blocked_streams, const nghttp3_mem *mem) { int rv; size_t len = 4096 / NGHTTP3_QPACK_ENTRY_OVERHEAD; @@ -852,9 +859,9 @@ static int qpack_context_init(nghttp3_qpack_context *ctx, ctx->mem = mem; ctx->dtable_size = 0; ctx->dtable_sum = 0; - ctx->hard_max_dtable_size = max_dtable_size; - ctx->max_dtable_size = 0; - ctx->max_blocked = max_blocked; + ctx->hard_max_dtable_capacity = hard_max_dtable_capacity; + ctx->max_dtable_capacity = 0; + ctx->max_blocked_streams = max_blocked_streams; ctx->next_absidx = 0; ctx->bad = 0; @@ -896,25 +903,19 @@ static int max_cnt_greater(const nghttp3_ksl_key *lhs, } int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder, - size_t max_dtable_size, size_t max_blocked, + size_t hard_max_dtable_capacity, const nghttp3_mem *mem) { int rv; - rv = qpack_context_init(&encoder->ctx, max_dtable_size, max_blocked, mem); + rv = qpack_context_init(&encoder->ctx, hard_max_dtable_capacity, 0, mem); if (rv != 0) { return rv; } - rv = nghttp3_map_init(&encoder->streams, mem); - if (rv != 0) { - goto streams_init_fail; - } + nghttp3_map_init(&encoder->streams, mem); - rv = nghttp3_ksl_init(&encoder->blocked_streams, max_cnt_greater, - sizeof(nghttp3_blocked_streams_key), mem); - if (rv != 0) { - goto blocked_streams_init_fail; - } + nghttp3_ksl_init(&encoder->blocked_streams, max_cnt_greater, + sizeof(nghttp3_blocked_streams_key), mem); qpack_map_init(&encoder->dtable_map); nghttp3_pq_init(&encoder->min_cnts, ref_min_cnt_less, mem); @@ -929,19 +930,11 @@ int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder, nghttp3_qpack_read_state_reset(&encoder->rstate); return 0; - -blocked_streams_init_fail: - nghttp3_map_free(&encoder->streams); -streams_init_fail: - qpack_context_free(&encoder->ctx); - - return rv; } -static int map_stream_free(nghttp3_map_entry *entry, void *ptr) { +static int map_stream_free(void *data, void *ptr) { const nghttp3_mem *mem = ptr; - nghttp3_qpack_stream *stream = - nghttp3_struct_of(entry, nghttp3_qpack_stream, me); + nghttp3_qpack_stream *stream = data; nghttp3_qpack_stream_del(stream, mem); return 0; } @@ -955,49 +948,27 @@ void nghttp3_qpack_encoder_free(nghttp3_qpack_encoder *encoder) { qpack_context_free(&encoder->ctx); } -int nghttp3_qpack_encoder_set_max_dtable_size(nghttp3_qpack_encoder *encoder, - size_t max_dtable_size) { - if (encoder->ctx.hard_max_dtable_size < max_dtable_size) { - return NGHTTP3_ERR_INVALID_ARGUMENT; - } +void nghttp3_qpack_encoder_set_max_dtable_capacity( + nghttp3_qpack_encoder *encoder, size_t max_dtable_capacity) { + max_dtable_capacity = + nghttp3_min(max_dtable_capacity, encoder->ctx.hard_max_dtable_capacity); - if (encoder->ctx.max_dtable_size == max_dtable_size) { - return 0; + if (encoder->ctx.max_dtable_capacity == max_dtable_capacity) { + return; } encoder->flags |= NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP; - if (encoder->min_dtable_update > max_dtable_size) { - encoder->min_dtable_update = max_dtable_size; - encoder->ctx.max_dtable_size = max_dtable_size; + if (encoder->min_dtable_update > max_dtable_capacity) { + encoder->min_dtable_update = max_dtable_capacity; + encoder->ctx.max_dtable_capacity = max_dtable_capacity; } - encoder->last_max_dtable_update = max_dtable_size; - - return 0; + encoder->last_max_dtable_update = max_dtable_capacity; } -int nghttp3_qpack_encoder_set_hard_max_dtable_size( - nghttp3_qpack_encoder *encoder, size_t hard_max_dtable_size) { - /* TODO This is not ideal. */ - if (encoder->ctx.hard_max_dtable_size) { - return NGHTTP3_ERR_INVALID_STATE; - } - - encoder->ctx.hard_max_dtable_size = hard_max_dtable_size; - - return 0; -} - -int nghttp3_qpack_encoder_set_max_blocked(nghttp3_qpack_encoder *encoder, - size_t max_blocked) { - /* TODO This is not ideal. */ - if (encoder->ctx.max_blocked) { - return NGHTTP3_ERR_INVALID_STATE; - } - - encoder->ctx.max_blocked = max_blocked; - - return 0; +void nghttp3_qpack_encoder_set_max_blocked_streams( + nghttp3_qpack_encoder *encoder, size_t max_blocked_streams) { + encoder->ctx.max_blocked_streams = max_blocked_streams; } uint64_t nghttp3_qpack_encoder_get_min_cnt(nghttp3_qpack_encoder *encoder) { @@ -1015,7 +986,7 @@ void nghttp3_qpack_encoder_shrink_dtable(nghttp3_qpack_encoder *encoder) { size_t len; nghttp3_qpack_entry *ent; - if (encoder->ctx.dtable_size <= encoder->ctx.max_dtable_size) { + if (encoder->ctx.dtable_size <= encoder->ctx.max_dtable_capacity) { return; } @@ -1023,7 +994,7 @@ void nghttp3_qpack_encoder_shrink_dtable(nghttp3_qpack_encoder *encoder) { min_cnt = nghttp3_qpack_encoder_get_min_cnt(encoder); } - for (; encoder->ctx.dtable_size > encoder->ctx.max_dtable_size;) { + for (; encoder->ctx.dtable_size > encoder->ctx.max_dtable_capacity;) { len = nghttp3_ringbuf_len(dtable); ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(dtable, len - 1); if (ent->absidx + 1 == min_cnt) { @@ -1069,7 +1040,8 @@ static int qpack_encoder_add_stream_ref(nghttp3_qpack_encoder *encoder, assert(rv == NGHTTP3_ERR_NOMEM); return rv; } - rv = nghttp3_map_insert(&encoder->streams, &stream->me); + rv = nghttp3_map_insert(&encoder->streams, + (nghttp3_map_key_type)stream->stream_id, stream); if (rv != 0) { assert(rv == NGHTTP3_ERR_NOMEM); nghttp3_qpack_stream_del(stream, mem); @@ -1110,7 +1082,8 @@ static void qpack_encoder_remove_stream(nghttp3_qpack_encoder *encoder, size_t i, len; nghttp3_qpack_header_block_ref *ref; - nghttp3_map_remove(&encoder->streams, stream->me.key); + nghttp3_map_remove(&encoder->streams, + (nghttp3_map_key_type)stream->stream_id); len = nghttp3_ringbuf_len(&stream->refs); for (i = 0; i < len; ++i) { @@ -1190,8 +1163,8 @@ int nghttp3_qpack_encoder_encode(nghttp3_qpack_encoder *encoder, blocked_stream = stream && nghttp3_qpack_encoder_stream_is_blocked(encoder, stream); allow_blocking = - blocked_stream || - encoder->ctx.max_blocked > nghttp3_ksl_len(&encoder->blocked_streams); + blocked_stream || encoder->ctx.max_blocked_streams > + nghttp3_ksl_len(&encoder->blocked_streams); DEBUGF("qpack::encode: stream %ld blocked=%d allow_blocking=%d\n", stream_id, blocked_stream, allow_blocking); @@ -1265,7 +1238,7 @@ int nghttp3_qpack_encoder_process_dtable_update(nghttp3_qpack_encoder *encoder, nghttp3_qpack_encoder_shrink_dtable(encoder); - if (encoder->ctx.max_dtable_size < encoder->ctx.dtable_size || + if (encoder->ctx.max_dtable_capacity < encoder->ctx.dtable_size || !(encoder->flags & NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP)) { return 0; } @@ -1286,7 +1259,7 @@ int nghttp3_qpack_encoder_process_dtable_update(nghttp3_qpack_encoder *encoder, encoder->flags &= (uint8_t)~NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP; encoder->min_dtable_update = SIZE_MAX; - encoder->ctx.max_dtable_size = encoder->last_max_dtable_update; + encoder->ctx.max_dtable_capacity = encoder->last_max_dtable_update; return 0; } @@ -1300,9 +1273,7 @@ int nghttp3_qpack_encoder_write_set_dtable_cap(nghttp3_qpack_encoder *encoder, nghttp3_qpack_stream * nghttp3_qpack_encoder_find_stream(nghttp3_qpack_encoder *encoder, int64_t stream_id) { - nghttp3_map_entry *me = - nghttp3_map_find(&encoder->streams, (uint64_t)stream_id); - return me == NULL ? NULL : nghttp3_struct_of(me, nghttp3_qpack_stream, me); + return nghttp3_map_find(&encoder->streams, (nghttp3_map_key_type)stream_id); } int nghttp3_qpack_encoder_stream_is_blocked(nghttp3_qpack_encoder *encoder, @@ -1352,7 +1323,7 @@ qpack_encoder_decide_indexing_mode(nghttp3_qpack_encoder *encoder, } if (table_space(nv->namelen, nv->valuelen) > - encoder->ctx.max_dtable_size * 3 / 4) { + encoder->ctx.max_dtable_capacity * 3 / 4) { return NGHTTP3_QPACK_INDEXING_MODE_LITERAL; } @@ -1372,8 +1343,8 @@ static int qpack_encoder_can_index(nghttp3_qpack_encoder *encoder, size_t need, nghttp3_qpack_entry *min_ent, *last_ent; nghttp3_ringbuf *dtable = &encoder->ctx.dtable; - if (encoder->ctx.max_dtable_size > encoder->ctx.dtable_size) { - avail = encoder->ctx.max_dtable_size - encoder->ctx.dtable_size; + if (encoder->ctx.max_dtable_capacity > encoder->ctx.dtable_size) { + avail = encoder->ctx.max_dtable_capacity - encoder->ctx.dtable_size; if (need <= avail) { return 1; } @@ -1385,7 +1356,7 @@ static int qpack_encoder_can_index(nghttp3_qpack_encoder *encoder, size_t need, } if (min_cnt == UINT64_MAX) { - return encoder->ctx.max_dtable_size >= need; + return encoder->ctx.max_dtable_capacity >= need; } min_ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, min_cnt - 1); @@ -1434,8 +1405,8 @@ static int qpack_encoder_can_index_duplicate(nghttp3_qpack_encoder *encoder, */ static int qpack_context_check_draining(nghttp3_qpack_context *ctx, uint64_t absidx) { - const size_t safe = - ctx->max_dtable_size - nghttp3_min(512, ctx->max_dtable_size * 1 / 8); + const size_t safe = ctx->max_dtable_capacity - + nghttp3_min(512, ctx->max_dtable_capacity * 1 / 8); nghttp3_qpack_entry *ent = nghttp3_qpack_context_dtable_get(ctx, absidx); return ctx->dtable_sum - ent->sum > safe; @@ -1714,7 +1685,7 @@ int nghttp3_qpack_stream_new(nghttp3_qpack_stream **pstream, int64_t stream_id, nghttp3_pq_init(&stream->max_cnts, ref_max_cnt_greater, mem); - stream->me.key = (uint64_t)stream_id; + stream->stream_id = stream_id; *pstream = stream; @@ -2059,9 +2030,9 @@ int nghttp3_qpack_context_dtable_add(nghttp3_qpack_context *ctx, space = table_space(qnv->name->len, qnv->value->len); - assert(space <= ctx->max_dtable_size); + assert(space <= ctx->max_dtable_capacity); - while (ctx->dtable_size + space > ctx->max_dtable_size) { + while (ctx->dtable_size + space > ctx->max_dtable_capacity) { i = nghttp3_ringbuf_len(&ctx->dtable); assert(i); ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i - 1); @@ -2265,7 +2236,7 @@ int nghttp3_qpack_encoder_block_stream(nghttp3_qpack_encoder *encoder, nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts), nghttp3_qpack_header_block_ref, max_cnts_pe) ->max_cnt, - stream->me.key}; + (uint64_t)stream->stream_id}; return nghttp3_ksl_insert(&encoder->blocked_streams, NULL, &bsk, stream); } @@ -2276,7 +2247,7 @@ void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder, nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts), nghttp3_qpack_header_block_ref, max_cnts_pe) ->max_cnt, - stream->me.key}; + (uint64_t)stream->stream_id}; nghttp3_ksl_it it; /* This is purely debugging purpose only */ @@ -2285,7 +2256,7 @@ void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder, assert(!nghttp3_ksl_it_end(&it)); assert(nghttp3_ksl_it_get(&it) == stream); - nghttp3_ksl_remove(&encoder->blocked_streams, NULL, &bsk); + nghttp3_ksl_remove_hint(&encoder->blocked_streams, NULL, &it, &bsk); } void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder, @@ -2297,21 +2268,19 @@ void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder, for (; !nghttp3_ksl_it_end(&it);) { bsk = *(nghttp3_blocked_streams_key *)nghttp3_ksl_it_key(&it); - nghttp3_ksl_remove(&encoder->blocked_streams, &it, &bsk); + nghttp3_ksl_remove_hint(&encoder->blocked_streams, &it, &it, &bsk); } } -void nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder, - int64_t stream_id) { +int nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder, + int64_t stream_id) { nghttp3_qpack_stream *stream = nghttp3_qpack_encoder_find_stream(encoder, stream_id); const nghttp3_mem *mem = encoder->ctx.mem; nghttp3_qpack_header_block_ref *ref; if (stream == NULL) { - /* This might be NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR, but we - don't create stream which does not use dynamic table. */ - return; + return NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR; } assert(nghttp3_ringbuf_len(&stream->refs)); @@ -2338,16 +2307,17 @@ void nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder, nghttp3_qpack_header_block_ref_del(ref, mem); if (nghttp3_ringbuf_len(&stream->refs)) { - return; + return 0; } qpack_encoder_remove_stream(encoder, stream); nghttp3_qpack_stream_del(stream, mem); + + return 0; } -int nghttp3_qpack_encoder_add_insert_count(nghttp3_qpack_encoder *encoder, - uint64_t n) { +int nghttp3_qpack_encoder_add_icnt(nghttp3_qpack_encoder *encoder, uint64_t n) { if (n == 0 || encoder->ctx.next_absidx - encoder->krcnt < n) { return NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR; } @@ -2365,6 +2335,7 @@ void nghttp3_qpack_encoder_ack_everything(nghttp3_qpack_encoder *encoder) { nghttp3_pq_clear(&encoder->min_cnts); nghttp3_map_each_free(&encoder->streams, map_stream_free, (void *)encoder->ctx.mem); + nghttp3_map_clear(&encoder->streams); } void nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder, @@ -2386,7 +2357,8 @@ void nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder, nghttp3_qpack_stream_del(stream, mem); } -size_t nghttp3_qpack_encoder_get_num_blocked(nghttp3_qpack_encoder *encoder) { +size_t +nghttp3_qpack_encoder_get_num_blocked_streams(nghttp3_qpack_encoder *encoder) { return nghttp3_ksl_len(&encoder->blocked_streams); } @@ -2394,7 +2366,7 @@ int nghttp3_qpack_encoder_write_field_section_prefix( nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, uint64_t ricnt, uint64_t base) { size_t max_ents = - encoder->ctx.hard_max_dtable_size / NGHTTP3_QPACK_ENTRY_OVERHEAD; + encoder->ctx.hard_max_dtable_capacity / NGHTTP3_QPACK_ENTRY_OVERHEAD; uint64_t encricnt = ricnt == 0 ? 0 : (ricnt % (2 * max_ents)) + 1; int sign = base < ricnt; uint64_t delta_base = sign ? ricnt - base - 1 : base - ricnt; @@ -2563,15 +2535,17 @@ nghttp3_ssize nghttp3_qpack_encoder_read_decoder(nghttp3_qpack_encoder *encoder, switch (encoder->opcode) { case NGHTTP3_QPACK_DS_OPCODE_ICNT_INCREMENT: - rv = nghttp3_qpack_encoder_add_insert_count(encoder, - encoder->rstate.left); + rv = nghttp3_qpack_encoder_add_icnt(encoder, encoder->rstate.left); if (rv != 0) { goto fail; } break; case NGHTTP3_QPACK_DS_OPCODE_SECTION_ACK: - nghttp3_qpack_encoder_ack_header(encoder, - (int64_t)encoder->rstate.left); + rv = nghttp3_qpack_encoder_ack_header(encoder, + (int64_t)encoder->rstate.left); + if (rv != 0) { + goto fail; + } break; case NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL: nghttp3_qpack_encoder_cancel_stream(encoder, @@ -2661,11 +2635,13 @@ void nghttp3_qpack_read_state_reset(nghttp3_qpack_read_state *rstate) { } int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder, - size_t max_dtable_size, size_t max_blocked, + size_t hard_max_dtable_capacity, + size_t max_blocked_streams, const nghttp3_mem *mem) { int rv; - rv = qpack_context_init(&decoder->ctx, max_dtable_size, max_blocked, mem); + rv = qpack_context_init(&decoder->ctx, hard_max_dtable_capacity, + max_blocked_streams, mem); if (rv != 0) { return rv; } @@ -2741,7 +2717,7 @@ static nghttp3_ssize qpack_read_string(nghttp3_qpack_read_state *rstate, /* * qpack_decoder_validate_index checks rstate->absidx is acceptable. * - * It returns 0 if it suceeds, or one of the following negative error + * It returns 0 if it succeeds, or one of the following negative error * codes: * * NGHTTP3_ERR_QPACK_FATAL @@ -2843,14 +2819,14 @@ nghttp3_ssize nghttp3_qpack_decoder_read_encoder(nghttp3_qpack_decoder *decoder, } if (decoder->opcode == NGHTTP3_QPACK_ES_OPCODE_SET_DTABLE_CAP) { - if (decoder->rstate.left > decoder->ctx.hard_max_dtable_size) { + DEBUGF("qpack::decode: Set dtable capacity to %" PRIu64 "\n", + decoder->rstate.left); + rv = nghttp3_qpack_decoder_set_max_dtable_capacity( + decoder, (size_t)decoder->rstate.left); + if (rv != 0) { rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; goto fail; } - DEBUGF("qpack::decode: Set dtable capacity to %" PRIu64 "\n", - decoder->rstate.left); - nghttp3_qpack_decoder_set_dtable_cap(decoder, - (size_t)decoder->rstate.left); decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE; nghttp3_qpack_read_state_reset(&decoder->rstate); @@ -3036,6 +3012,7 @@ nghttp3_ssize nghttp3_qpack_decoder_read_encoder(nghttp3_qpack_decoder *decoder, default: /* Unreachable */ assert(0); + abort(); } if (rv != 0) { goto fail; @@ -3070,6 +3047,7 @@ nghttp3_ssize nghttp3_qpack_decoder_read_encoder(nghttp3_qpack_decoder *decoder, default: /* Unreachable */ assert(0); + abort(); } if (rv != 0) { goto fail; @@ -3088,16 +3066,20 @@ nghttp3_ssize nghttp3_qpack_decoder_read_encoder(nghttp3_qpack_decoder *decoder, return rv; } -void nghttp3_qpack_decoder_set_dtable_cap(nghttp3_qpack_decoder *decoder, - size_t cap) { +int nghttp3_qpack_decoder_set_max_dtable_capacity( + nghttp3_qpack_decoder *decoder, size_t max_dtable_capacity) { nghttp3_qpack_entry *ent; size_t i; nghttp3_qpack_context *ctx = &decoder->ctx; const nghttp3_mem *mem = ctx->mem; - ctx->max_dtable_size = cap; + if (max_dtable_capacity > decoder->ctx.hard_max_dtable_capacity) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + ctx->max_dtable_capacity = max_dtable_capacity; - while (ctx->dtable_size > cap) { + while (ctx->dtable_size > max_dtable_capacity) { i = nghttp3_ringbuf_len(&ctx->dtable); assert(i); ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i - 1); @@ -3108,6 +3090,8 @@ void nghttp3_qpack_decoder_set_dtable_cap(nghttp3_qpack_decoder *decoder, nghttp3_qpack_entry_free(ent); nghttp3_mem_free(mem, ent); } + + return 0; } int nghttp3_qpack_decoder_dtable_indexed_add(nghttp3_qpack_decoder *decoder) { @@ -3131,7 +3115,7 @@ int nghttp3_qpack_decoder_dtable_static_add(nghttp3_qpack_decoder *decoder) { shd = &stable[decoder->rstate.absidx]; if (table_space(shd->name.len, decoder->rstate.value->len) > - decoder->ctx.max_dtable_size) { + decoder->ctx.max_dtable_capacity) { return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; } @@ -3155,7 +3139,7 @@ int nghttp3_qpack_decoder_dtable_dynamic_add(nghttp3_qpack_decoder *decoder) { ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, decoder->rstate.absidx); if (table_space(ent->nv.name->len, decoder->rstate.value->len) > - decoder->ctx.max_dtable_size) { + decoder->ctx.max_dtable_capacity) { return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; } @@ -3185,7 +3169,7 @@ int nghttp3_qpack_decoder_dtable_duplicate_add(nghttp3_qpack_decoder *decoder) { ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, decoder->rstate.absidx); if (table_space(ent->nv.name->len, ent->nv.value->len) > - decoder->ctx.max_dtable_size) { + decoder->ctx.max_dtable_capacity) { return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; } @@ -3210,7 +3194,7 @@ int nghttp3_qpack_decoder_dtable_literal_add(nghttp3_qpack_decoder *decoder) { (int)decoder->rstate.value->len, decoder->rstate.value->base); if (table_space(decoder->rstate.name->len, decoder->rstate.value->len) > - decoder->ctx.max_dtable_size) { + decoder->ctx.max_dtable_capacity) { return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; } @@ -3328,7 +3312,7 @@ nghttp3_qpack_decoder_read_request(nghttp3_qpack_decoder *decoder, } if (sctx->dbase_sign) { - if (sctx->ricnt < sctx->rstate.left) { + if (sctx->ricnt <= sctx->rstate.left) { rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; goto fail; } @@ -3595,7 +3579,11 @@ nghttp3_qpack_decoder_read_request(nghttp3_qpack_decoder *decoder, switch (sctx->opcode) { case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME: case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB: - nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv); + rv = nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv); + if (rv != 0) { + goto fail; + } + break; case NGHTTP3_QPACK_RS_OPCODE_LITERAL: nghttp3_qpack_decoder_emit_literal(decoder, sctx, nv); @@ -3629,7 +3617,11 @@ nghttp3_qpack_decoder_read_request(nghttp3_qpack_decoder *decoder, switch (sctx->opcode) { case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME: case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB: - nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv); + rv = nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv); + if (rv != 0) { + goto fail; + } + break; case NGHTTP3_QPACK_RS_OPCODE_LITERAL: nghttp3_qpack_decoder_emit_literal(decoder, sctx, nv); @@ -3733,6 +3725,7 @@ void nghttp3_qpack_decoder_write_decoder(nghttp3_qpack_decoder *decoder, uint8_t *p; uint64_t n = 0; size_t len = 0; + (void)len; if (decoder->written_icnt < decoder->ctx.next_absidx) { n = decoder->ctx.next_absidx - decoder->written_icnt; @@ -3789,7 +3782,8 @@ int nghttp3_qpack_decoder_reconstruct_ricnt(nghttp3_qpack_decoder *decoder, return 0; } - max_ents = decoder->ctx.hard_max_dtable_size / NGHTTP3_QPACK_ENTRY_OVERHEAD; + max_ents = + decoder->ctx.hard_max_dtable_capacity / NGHTTP3_QPACK_ENTRY_OVERHEAD; full = 2 * max_ents; if (encricnt > full) { @@ -3938,13 +3932,19 @@ qpack_decoder_emit_static_indexed_name(nghttp3_qpack_decoder *decoder, sctx->rstate.value = NULL; } -static void +static int qpack_decoder_emit_dynamic_indexed_name(nghttp3_qpack_decoder *decoder, nghttp3_qpack_stream_context *sctx, nghttp3_qpack_nv *nv) { - nghttp3_qpack_entry *ent = - nghttp3_qpack_context_dtable_get(&decoder->ctx, sctx->rstate.absidx); - (void)decoder; + nghttp3_qpack_entry *ent; + + /* A broken encoder might change dtable capacity while processing + request stream instruction. Check the absidx again. */ + if (qpack_decoder_validate_index(decoder, &sctx->rstate) != 0) { + return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + } + + ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, sctx->rstate.absidx); nv->name = ent->nv.name; nv->value = sctx->rstate.value; @@ -3955,11 +3955,13 @@ qpack_decoder_emit_dynamic_indexed_name(nghttp3_qpack_decoder *decoder, nghttp3_rcbuf_incref(nv->name); sctx->rstate.value = NULL; + + return 0; } -void nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder, - nghttp3_qpack_stream_context *sctx, - nghttp3_qpack_nv *nv) { +int nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv) { (void)decoder; DEBUGF("qpack::decode: Indexed name (%s) absidx=%" PRIu64 " value=%*s\n", @@ -3967,10 +3969,12 @@ void nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder, (int)sctx->rstate.value->len, sctx->rstate.value->base); if (sctx->rstate.dynamic) { - qpack_decoder_emit_dynamic_indexed_name(decoder, sctx, nv); - } else { - qpack_decoder_emit_static_indexed_name(decoder, sctx, nv); + return qpack_decoder_emit_dynamic_indexed_name(decoder, sctx, nv); } + + qpack_decoder_emit_static_indexed_name(decoder, sctx, nv); + + return 0; } void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder, @@ -3993,7 +3997,7 @@ void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder, } int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder, - size_t max_dtable_size, size_t max_blocked, + size_t hard_max_dtable_capacity, const nghttp3_mem *mem) { int rv; nghttp3_qpack_encoder *p; @@ -4003,7 +4007,7 @@ int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder, return NGHTTP3_ERR_NOMEM; } - rv = nghttp3_qpack_encoder_init(p, max_dtable_size, max_blocked, mem); + rv = nghttp3_qpack_encoder_init(p, hard_max_dtable_capacity, mem); if (rv != 0) { return rv; } @@ -4057,7 +4061,8 @@ void nghttp3_qpack_stream_context_del(nghttp3_qpack_stream_context *sctx) { } int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder, - size_t max_dtable_size, size_t max_blocked, + size_t hard_max_dtable_capacity, + size_t max_blocked_streams, const nghttp3_mem *mem) { int rv; nghttp3_qpack_decoder *p; @@ -4067,7 +4072,8 @@ int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder, return NGHTTP3_ERR_NOMEM; } - rv = nghttp3_qpack_decoder_init(p, max_dtable_size, max_blocked, mem); + rv = nghttp3_qpack_decoder_init(p, hard_max_dtable_capacity, + max_blocked_streams, mem); if (rv != 0) { return rv; } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h index 429c55a78081f1..804969e14d6091 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h @@ -118,7 +118,7 @@ void nghttp3_qpack_header_block_ref_del(nghttp3_qpack_header_block_ref *ref, const nghttp3_mem *mem); typedef struct nghttp3_qpack_stream { - nghttp3_map_entry me; + int64_t stream_id; /* refs is an array of pointer to nghttp3_qpack_header_block_ref in the order of the time they are encoded. HTTP/3 allows multiple header blocks (e.g., non-final response headers, final response @@ -154,18 +154,15 @@ typedef struct nghttp3_qpack_context { NGHTTP3_QPACK_ENTRY_OVERHEAD bytes overhead per each entry. */ size_t dtable_size; size_t dtable_sum; - /* hard_max_dtable_size is the maximum size of dynamic table. In - HTTP/3, it is notified by decoder as - SETTINGS_QPACK_MAX_TABLE_CAPACITY. Any value lower than or equal - to SETTINGS_QPACK_MAX_TABLE_CAPACITY is OK because encoder has - the authority to decide how many entries are inserted into - dynamic table. */ - size_t hard_max_dtable_size; - /* max_dtable_size is the effective maximum size of dynamic table. */ - size_t max_dtable_size; - /* max_blocked is the maximum number of stream which can be + /* hard_max_dtable_capacity is the upper bound of + max_dtable_capacity. */ + size_t hard_max_dtable_capacity; + /* max_dtable_capacity is the maximum capacity of the dynamic + table. */ + size_t max_dtable_capacity; + /* max_blocked_streams is the maximum number of stream which can be blocked. */ - size_t max_blocked; + size_t max_blocked_streams; /* next_absidx is the next absolute index for nghttp3_qpack_entry. It is equivalent to insert count. */ uint64_t next_absidx; @@ -218,10 +215,10 @@ typedef enum nghttp3_qpack_decoder_stream_opcode { /* QPACK encoder flags */ /* NGHTTP3_QPACK_ENCODER_FLAG_NONE indicates that no flag is set. */ -#define NGHTTP3_QPACK_ENCODER_FLAG_NONE 0x00 +#define NGHTTP3_QPACK_ENCODER_FLAG_NONE 0x00u /* NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP indicates that Set Dynamic Table Capacity is required. */ -#define NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP 0x01 +#define NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP 0x01u struct nghttp3_qpack_encoder { nghttp3_qpack_context ctx; @@ -260,9 +257,8 @@ struct nghttp3_qpack_encoder { /* * nghttp3_qpack_encoder_init initializes |encoder|. - * |max_dtable_size| is the maximum size of dynamic table. - * |max_blocked| is the maximum number of stream which can be blocked. - * |mem| is a memory allocator. + * |hard_max_dtable_capacity| is the upper bound of the dynamic table + * capacity. |mem| is a memory allocator. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -271,7 +267,7 @@ struct nghttp3_qpack_encoder { * Out of memory. */ int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder, - size_t max_dtable_size, size_t max_blocked, + size_t hard_max_dtable_capacity, const nghttp3_mem *mem); /* @@ -628,6 +624,44 @@ int nghttp3_qpack_encoder_dtable_literal_add(nghttp3_qpack_encoder *encoder, const nghttp3_nv *nv, int32_t token, uint32_t hash); +/* + * `nghttp3_qpack_encoder_ack_header` tells |encoder| that header + * block for a stream denoted by |stream_id| was acknowledged by + * decoder. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR` + * Section Acknowledgement for a stream denoted by |stream_id| is + * unexpected. + */ +int nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder, + int64_t stream_id); + +/* + * `nghttp3_qpack_encoder_add_icnt` increments known received count of + * |encoder| by |n|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + * Out of memory. + * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR` + * |n| is too large. + */ +int nghttp3_qpack_encoder_add_icnt(nghttp3_qpack_encoder *encoder, uint64_t n); + +/* + * `nghttp3_qpack_encoder_cancel_stream` tells |encoder| that stream + * denoted by |stream_id| is cancelled. This function is provided for + * debugging purpose only. In HTTP/3, |encoder| knows this by reading + * decoder stream with `nghttp3_qpack_encoder_read_decoder()`. + */ +void nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder, + int64_t stream_id); + /* * nghttp3_qpack_context_dtable_get returns dynamic table entry whose * absolute index is |absidx|. This function assumes that such entry @@ -749,9 +783,9 @@ struct nghttp3_qpack_decoder { /* * nghttp3_qpack_decoder_init initializes |decoder|. - * |max_dtable_size| is the maximum size of dynamic table. - * |max_blocked| is the maximum number of stream which can be blocked. - * |mem| is a memory allocator. + * |hard_max_dtable_capacity| is the upper bound of the dynamic table + * capacity. |max_blocked_streams| is the maximum number of stream + * which can be blocked. |mem| is a memory allocator. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -760,7 +794,8 @@ struct nghttp3_qpack_decoder { * Out of memory. */ int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder, - size_t max_dtable_size, size_t max_blocked, + size_t hard_max_dtable_capacity, + size_t max_blocked_streams, const nghttp3_mem *mem); /* @@ -935,9 +970,9 @@ void nghttp3_qpack_decoder_emit_indexed(nghttp3_qpack_decoder *decoder, nghttp3_qpack_stream_context *sctx, nghttp3_qpack_nv *nv); -void nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder, - nghttp3_qpack_stream_context *sctx, - nghttp3_qpack_nv *nv); +int nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv); void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder, nghttp3_qpack_stream_context *sctx, diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.c b/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.c index 1c31ecebf608d5..9e9dab51390696 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.c @@ -41,8 +41,7 @@ int nghttp3_rcbuf_new(nghttp3_rcbuf **rcbuf_ptr, size_t size, *rcbuf_ptr = (void *)p; - (*rcbuf_ptr)->mem_user_data = mem->mem_user_data; - (*rcbuf_ptr)->free = mem->free; + (*rcbuf_ptr)->mem = mem; (*rcbuf_ptr)->base = p + sizeof(nghttp3_rcbuf); (*rcbuf_ptr)->len = size; (*rcbuf_ptr)->ref = 1; @@ -76,7 +75,7 @@ int nghttp3_rcbuf_new2(nghttp3_rcbuf **rcbuf_ptr, const uint8_t *src, * Frees |rcbuf| itself, regardless of its reference cout. */ void nghttp3_rcbuf_del(nghttp3_rcbuf *rcbuf) { - nghttp3_mem_free2(rcbuf->free, rcbuf, rcbuf->mem_user_data); + nghttp3_mem_free(rcbuf->mem, rcbuf); } void nghttp3_rcbuf_incref(nghttp3_rcbuf *rcbuf) { diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.h b/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.h index feea8040005422..f589c377bf6ea7 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.h @@ -33,10 +33,9 @@ #include struct nghttp3_rcbuf { - /* custom memory allocator belongs to the mem parameter when - creating this object. */ - void *mem_user_data; - nghttp3_free free; + /* mem is the memory allocator that allocates memory for this + object. */ + const nghttp3_mem *mem; /* The pointer to the underlying buffer */ uint8_t *base; /* Size of buffer pointed by |base|. */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c index 96d60fe82f9a76..e655a7ec01d10b 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c @@ -34,6 +34,7 @@ #include "nghttp3_conn.h" #include "nghttp3_str.h" #include "nghttp3_http.h" +#include "nghttp3_vec.h" /* NGHTTP3_STREAM_MAX_COPY_THRES is the maximum size of buffer which makes a copy to outq. */ @@ -44,45 +45,36 @@ int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id, uint64_t seq, const nghttp3_stream_callbacks *callbacks, + nghttp3_objalloc *out_chunk_objalloc, + nghttp3_objalloc *stream_objalloc, const nghttp3_mem *mem) { - int rv; - nghttp3_stream *stream = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_stream)); + nghttp3_stream *stream = nghttp3_objalloc_stream_get(stream_objalloc); nghttp3_node_id nid; if (stream == NULL) { return NGHTTP3_ERR_NOMEM; } + memset(stream, 0, sizeof(*stream)); + + stream->out_chunk_objalloc = out_chunk_objalloc; + stream->stream_objalloc = stream_objalloc; + nghttp3_tnode_init( &stream->node, nghttp3_node_id_init(&nid, NGHTTP3_NODE_ID_TYPE_STREAM, stream_id), seq, NGHTTP3_DEFAULT_URGENCY); - rv = nghttp3_ringbuf_init(&stream->frq, 0, sizeof(nghttp3_frame_entry), mem); - if (rv != 0) { - goto frq_init_fail; - } - - rv = nghttp3_ringbuf_init(&stream->chunks, 0, sizeof(nghttp3_buf), mem); - if (rv != 0) { - goto chunks_init_fail; - } - - rv = nghttp3_ringbuf_init(&stream->outq, 0, sizeof(nghttp3_typed_buf), mem); - if (rv != 0) { - goto outq_init_fail; - } - - rv = nghttp3_ringbuf_init(&stream->inq, 0, sizeof(nghttp3_buf), mem); - if (rv != 0) { - goto inq_init_fail; - } + nghttp3_ringbuf_init(&stream->frq, 0, sizeof(nghttp3_frame_entry), mem); + nghttp3_ringbuf_init(&stream->chunks, 0, sizeof(nghttp3_buf), mem); + nghttp3_ringbuf_init(&stream->outq, 0, sizeof(nghttp3_typed_buf), mem); + nghttp3_ringbuf_init(&stream->inq, 0, sizeof(nghttp3_buf), mem); nghttp3_qpack_stream_context_init(&stream->qpack_sctx, stream_id, mem); - stream->me.key = (key_type)stream_id; stream->qpack_blocked_pe.index = NGHTTP3_PQ_BAD_INDEX; stream->mem = mem; + stream->tx.offset = 0; stream->rx.http.status_code = -1; stream->rx.http.content_length = -1; stream->rx.http.pri = NGHTTP3_DEFAULT_URGENCY; @@ -95,17 +87,6 @@ int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id, *pstream = stream; return 0; - -inq_init_fail: - nghttp3_ringbuf_free(&stream->outq); -outq_init_fail: - nghttp3_ringbuf_free(&stream->chunks); -chunks_init_fail: - nghttp3_ringbuf_free(&stream->frq); -frq_init_fail: - nghttp3_mem_free(mem, stream); - - return rv; } static void delete_outq(nghttp3_ringbuf *outq, const nghttp3_mem *mem) { @@ -134,6 +115,27 @@ static void delete_chunks(nghttp3_ringbuf *chunks, const nghttp3_mem *mem) { nghttp3_ringbuf_free(chunks); } +static void delete_out_chunks(nghttp3_ringbuf *chunks, + nghttp3_objalloc *out_chunk_objalloc, + const nghttp3_mem *mem) { + nghttp3_buf *buf; + size_t i, len = nghttp3_ringbuf_len(chunks); + + for (i = 0; i < len; ++i) { + buf = nghttp3_ringbuf_get(chunks, i); + + if (nghttp3_buf_cap(buf) == NGHTTP3_STREAM_MIN_CHUNK_SIZE) { + nghttp3_objalloc_chunk_release(out_chunk_objalloc, + (nghttp3_chunk *)(void *)buf->begin); + continue; + } + + nghttp3_buf_free(buf, mem); + } + + nghttp3_ringbuf_free(chunks); +} + static void delete_frq(nghttp3_ringbuf *frq, const nghttp3_mem *mem) { nghttp3_frame_entry *frent; size_t i, len = nghttp3_ringbuf_len(frq); @@ -144,9 +146,6 @@ static void delete_frq(nghttp3_ringbuf *frq, const nghttp3_mem *mem) { case NGHTTP3_FRAME_HEADERS: nghttp3_frame_headers_free(&frent->fr.headers, mem); break; - case NGHTTP3_FRAME_PUSH_PROMISE: - nghttp3_frame_push_promise_free(&frent->fr.push_promise, mem); - break; default: break; } @@ -163,11 +162,11 @@ void nghttp3_stream_del(nghttp3_stream *stream) { nghttp3_qpack_stream_context_free(&stream->qpack_sctx); delete_chunks(&stream->inq, stream->mem); delete_outq(&stream->outq, stream->mem); - delete_chunks(&stream->chunks, stream->mem); + delete_out_chunks(&stream->chunks, stream->out_chunk_objalloc, stream->mem); delete_frq(&stream->frq, stream->mem); nghttp3_tnode_free(&stream->node); - nghttp3_mem_free(stream->mem, stream); + nghttp3_objalloc_stream_release(stream->stream_objalloc, stream); } void nghttp3_varint_read_state_reset(nghttp3_varint_read_state *rvint) { @@ -267,19 +266,6 @@ int nghttp3_stream_fill_outq(nghttp3_stream *stream) { } nghttp3_frame_headers_free(&frent->fr.headers, stream->mem); break; - case NGHTTP3_FRAME_PUSH_PROMISE: - rv = nghttp3_stream_write_push_promise(stream, frent); - if (rv != 0) { - return rv; - } - nghttp3_frame_push_promise_free(&frent->fr.push_promise, stream->mem); - break; - case NGHTTP3_FRAME_CANCEL_PUSH: - rv = nghttp3_stream_write_cancel_push(stream, frent); - if (rv != 0) { - return rv; - } - break; case NGHTTP3_FRAME_DATA: rv = nghttp3_stream_write_data(stream, &data_eof, frent); if (rv != 0) { @@ -292,11 +278,14 @@ int nghttp3_stream_fill_outq(nghttp3_stream *stream) { return 0; } break; - case NGHTTP3_FRAME_MAX_PUSH_ID: - if (stream->conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) { - break; + case NGHTTP3_FRAME_GOAWAY: + rv = nghttp3_stream_write_goaway(stream, frent); + if (rv != 0) { + return rv; } - rv = nghttp3_stream_write_max_push_id(stream, frent); + break; + case NGHTTP3_FRAME_PRIORITY_UPDATE: + rv = nghttp3_stream_write_priority_update(stream, frent); if (rv != 0) { return rv; } @@ -338,34 +327,6 @@ int nghttp3_stream_write_stream_type(nghttp3_stream *stream) { return nghttp3_stream_outq_add(stream, &tbuf); } -int nghttp3_stream_write_stream_type_push_id(nghttp3_stream *stream) { - size_t len; - nghttp3_buf *chunk; - nghttp3_typed_buf tbuf; - int rv; - nghttp3_push_promise *pp = stream->pp; - - assert(stream->type == NGHTTP3_STREAM_TYPE_PUSH); - assert(pp); - - len = nghttp3_put_varint_len((int64_t)stream->type) + - nghttp3_put_varint_len(pp->node.nid.id); - - rv = nghttp3_stream_ensure_chunk(stream, len); - if (rv != 0) { - return rv; - } - - chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); - - chunk->last = nghttp3_put_varint(chunk->last, (int64_t)stream->type); - chunk->last = nghttp3_put_varint(chunk->last, pp->node.nid.id); - tbuf.buf.last = chunk->last; - - return nghttp3_stream_outq_add(stream, &tbuf); -} - int nghttp3_stream_write_settings(nghttp3_stream *stream, nghttp3_frame_entry *frent) { size_t len; @@ -386,10 +347,16 @@ int nghttp3_stream_write_settings(nghttp3_stream *stream, iv[0].id = NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE; iv[0].value = local_settings->max_field_section_size; iv[1].id = NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY; - iv[1].value = local_settings->qpack_max_table_capacity; + iv[1].value = local_settings->qpack_max_dtable_capacity; iv[2].id = NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS; iv[2].value = local_settings->qpack_blocked_streams; + if (local_settings->enable_connect_protocol) { + ++fr.settings.niv; + iv[3].id = NGHTTP3_SETTINGS_ID_ENABLE_CONNECT_PROTOCOL; + iv[3].value = 1; + } + len = nghttp3_frame_write_settings_len(&fr.settings.hd.length, &fr.settings); rv = nghttp3_stream_ensure_chunk(stream, len); @@ -407,15 +374,15 @@ int nghttp3_stream_write_settings(nghttp3_stream *stream, return nghttp3_stream_outq_add(stream, &tbuf); } -int nghttp3_stream_write_cancel_push(nghttp3_stream *stream, - nghttp3_frame_entry *frent) { - nghttp3_frame_cancel_push *fr = &frent->fr.cancel_push; +int nghttp3_stream_write_goaway(nghttp3_stream *stream, + nghttp3_frame_entry *frent) { + nghttp3_frame_goaway *fr = &frent->fr.goaway; size_t len; int rv; nghttp3_buf *chunk; nghttp3_typed_buf tbuf; - len = nghttp3_frame_write_cancel_push_len(&fr->hd.length, fr); + len = nghttp3_frame_write_goaway_len(&fr->hd.length, fr); rv = nghttp3_stream_ensure_chunk(stream, len); if (rv != 0) { @@ -425,30 +392,22 @@ int nghttp3_stream_write_cancel_push(nghttp3_stream *stream, chunk = nghttp3_stream_get_chunk(stream); typed_buf_shared_init(&tbuf, chunk); - chunk->last = nghttp3_frame_write_cancel_push(chunk->last, fr); + chunk->last = nghttp3_frame_write_goaway(chunk->last, fr); tbuf.buf.last = chunk->last; return nghttp3_stream_outq_add(stream, &tbuf); } -int nghttp3_stream_write_max_push_id(nghttp3_stream *stream, - nghttp3_frame_entry *frent) { - nghttp3_frame_max_push_id *fr = &frent->fr.max_push_id; - nghttp3_conn *conn = stream->conn; +int nghttp3_stream_write_priority_update(nghttp3_stream *stream, + nghttp3_frame_entry *frent) { + nghttp3_frame_priority_update *fr = &frent->fr.priority_update; size_t len; int rv; nghttp3_buf *chunk; nghttp3_typed_buf tbuf; - assert(conn); - assert(conn->flags & NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED); - - fr->push_id = (int64_t)conn->remote.uni.unsent_max_pushes - 1; - conn->remote.uni.max_pushes = conn->remote.uni.unsent_max_pushes; - conn->flags &= (uint16_t)~NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED; - - len = nghttp3_frame_write_max_push_id_len(&fr->hd.length, fr); + len = nghttp3_frame_write_priority_update_len(&fr->hd.length, fr); rv = nghttp3_stream_ensure_chunk(stream, len); if (rv != 0) { @@ -458,7 +417,7 @@ int nghttp3_stream_write_max_push_id(nghttp3_stream *stream, chunk = nghttp3_stream_get_chunk(stream); typed_buf_shared_init(&tbuf, chunk); - chunk->last = nghttp3_frame_write_max_push_id(chunk->last, fr); + chunk->last = nghttp3_frame_write_priority_update(chunk->last, fr); tbuf.buf.last = chunk->last; @@ -474,35 +433,21 @@ int nghttp3_stream_write_headers(nghttp3_stream *stream, return nghttp3_stream_write_header_block( stream, &conn->qenc, conn->tx.qenc, &conn->tx.qpack.rbuf, - &conn->tx.qpack.ebuf, NGHTTP3_FRAME_HEADERS, 0, fr->nva, fr->nvlen); -} - -int nghttp3_stream_write_push_promise(nghttp3_stream *stream, - nghttp3_frame_entry *frent) { - nghttp3_frame_push_promise *fr = &frent->fr.push_promise; - nghttp3_conn *conn = stream->conn; - - assert(conn); - - return nghttp3_stream_write_header_block( - stream, &conn->qenc, conn->tx.qenc, &conn->tx.qpack.rbuf, - &conn->tx.qpack.ebuf, NGHTTP3_FRAME_PUSH_PROMISE, fr->push_id, fr->nva, - fr->nvlen); + &conn->tx.qpack.ebuf, NGHTTP3_FRAME_HEADERS, fr->nva, fr->nvlen); } int nghttp3_stream_write_header_block(nghttp3_stream *stream, nghttp3_qpack_encoder *qenc, nghttp3_stream *qenc_stream, nghttp3_buf *rbuf, nghttp3_buf *ebuf, - int64_t frame_type, int64_t push_id, - const nghttp3_nv *nva, size_t nvlen) { + int64_t frame_type, const nghttp3_nv *nva, + size_t nvlen) { nghttp3_buf pbuf; int rv; size_t len; nghttp3_buf *chunk; nghttp3_typed_buf tbuf; nghttp3_frame_hd hd; - size_t push_idlen = 0; uint8_t raw_pbuf[16]; size_t pbuflen, rbuflen, ebuflen; @@ -518,14 +463,10 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream, rbuflen = nghttp3_buf_len(rbuf); ebuflen = nghttp3_buf_len(ebuf); - if (frame_type == NGHTTP3_FRAME_PUSH_PROMISE) { - push_idlen = nghttp3_put_varint_len(push_id); - } - hd.type = frame_type; - hd.length = (int64_t)(pbuflen + rbuflen + push_idlen); + hd.length = (int64_t)(pbuflen + rbuflen); - len = nghttp3_frame_write_hd_len(&hd) + push_idlen + pbuflen; + len = nghttp3_frame_write_hd_len(&hd) + pbuflen; if (rbuflen <= NGHTTP3_STREAM_MAX_COPY_THRES) { len += rbuflen; @@ -541,10 +482,6 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream, chunk->last = nghttp3_frame_write_hd(chunk->last, &hd); - if (push_idlen) { - chunk->last = nghttp3_put_varint(chunk->last, push_id); - } - chunk->last = nghttp3_cpymem(chunk->last, pbuf.pos, pbuflen); nghttp3_buf_init(&pbuf); @@ -623,7 +560,7 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, nghttp3_buf *chunk; nghttp3_read_data_callback read_data = frent->aux.data.dr.read_data; nghttp3_conn *conn = stream->conn; - size_t datalen; + int64_t datalen; uint32_t flags = 0; nghttp3_frame_hd hd; nghttp3_vec vec[8]; @@ -647,7 +584,10 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, return NGHTTP3_ERR_CALLBACK_FAILURE; } - datalen = nghttp3_vec_len(vec, (size_t)sveccnt); + datalen = nghttp3_vec_len_varint(vec, (size_t)sveccnt); + if (datalen == -1) { + return NGHTTP3_ERR_STREAM_DATA_OVERFLOW; + } assert(datalen || flags & NGHTTP3_DATA_FLAG_EOF); @@ -677,7 +617,7 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, } hd.type = NGHTTP3_FRAME_DATA; - hd.length = (int64_t)datalen; + hd.length = datalen; len = nghttp3_frame_write_hd_len(&hd); @@ -762,8 +702,14 @@ int nghttp3_stream_outq_add(nghttp3_stream *stream, int rv; nghttp3_typed_buf *dest; size_t len = nghttp3_ringbuf_len(outq); + size_t buflen = nghttp3_buf_len(&tbuf->buf); + + if (buflen > NGHTTP3_MAX_VARINT - stream->tx.offset) { + return NGHTTP3_ERR_STREAM_DATA_OVERFLOW; + } - stream->unsent_bytes += nghttp3_buf_len(&tbuf->buf); + stream->tx.offset += buflen; + stream->unsent_bytes += buflen; if (len) { dest = nghttp3_ringbuf_get(outq, len - 1); @@ -816,7 +762,12 @@ int nghttp3_stream_ensure_chunk(nghttp3_stream *stream, size_t need) { for (; n < need; n *= 2) ; - p = nghttp3_mem_malloc(stream->mem, n); + if (n == NGHTTP3_STREAM_MIN_CHUNK_SIZE) { + p = (uint8_t *)nghttp3_objalloc_chunk_len_get(stream->out_chunk_objalloc, + n); + } else { + p = nghttp3_mem_malloc(stream->mem, n); + } if (p == NULL) { return NGHTTP3_ERR_NOMEM; } @@ -863,7 +814,7 @@ nghttp3_ssize nghttp3_stream_writev(nghttp3_stream *stream, int *pfin, nghttp3_ringbuf *outq = &stream->outq; size_t len = nghttp3_ringbuf_len(outq); size_t i; - size_t offset = stream->outq_offset; + uint64_t offset = stream->outq_offset; size_t buflen; nghttp3_vec *vbegin = vec, *vend = vec + veccnt; nghttp3_typed_buf *tbuf; @@ -879,7 +830,7 @@ nghttp3_ssize nghttp3_stream_writev(nghttp3_stream *stream, int *pfin, } vec->base = tbuf->buf.pos + offset; - vec->len = buflen - offset; + vec->len = (size_t)(buflen - offset); ++vec; ++i; break; @@ -903,7 +854,7 @@ int nghttp3_stream_add_outq_offset(nghttp3_stream *stream, size_t n) { nghttp3_ringbuf *outq = &stream->outq; size_t i; size_t len = nghttp3_ringbuf_len(outq); - size_t offset = stream->outq_offset + n; + uint64_t offset = stream->outq_offset + n; size_t buflen; nghttp3_typed_buf *tbuf; @@ -945,7 +896,7 @@ static int stream_pop_outq_entry(nghttp3_stream *stream, break; case NGHTTP3_BUF_TYPE_ALIEN: break; - default: + case NGHTTP3_BUF_TYPE_SHARED: assert(nghttp3_ringbuf_len(chunks)); chunk = nghttp3_ringbuf_get(chunks, 0); @@ -954,9 +905,18 @@ static int stream_pop_outq_entry(nghttp3_stream *stream, assert(chunk->end == tbuf->buf.end); if (chunk->last == tbuf->buf.last) { - nghttp3_buf_free(chunk, stream->mem); + if (nghttp3_buf_cap(chunk) == NGHTTP3_STREAM_MIN_CHUNK_SIZE) { + nghttp3_objalloc_chunk_release(stream->out_chunk_objalloc, + (nghttp3_chunk *)(void *)chunk->begin); + } else { + nghttp3_buf_free(chunk, stream->mem); + } nghttp3_ringbuf_pop_front(chunks); } + break; + default: + assert(0); + abort(); }; nghttp3_ringbuf_pop_front(&stream->outq); @@ -969,7 +929,7 @@ int nghttp3_stream_add_ack_offset(nghttp3_stream *stream, uint64_t n) { uint64_t offset = stream->ack_offset + n; size_t buflen; size_t npopped = 0; - size_t nack; + uint64_t nack; nghttp3_typed_buf *tbuf; int rv; @@ -978,7 +938,7 @@ int nghttp3_stream_add_ack_offset(nghttp3_stream *stream, uint64_t n) { buflen = nghttp3_buf_len(&tbuf->buf); if (tbuf->type == NGHTTP3_BUF_TYPE_ALIEN) { - nack = (size_t)nghttp3_min(offset, (uint64_t)buflen) - stream->ack_done; + nack = nghttp3_min(offset, (uint64_t)buflen) - stream->ack_done; if (stream->callbacks.acked_data) { rv = stream->callbacks.acked_data(stream, stream->node.nid.id, nack, stream->user_data); @@ -1110,16 +1070,16 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) { return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; } - rv = nghttp3_http_on_remote_end_stream(stream); - if (rv != 0) { - return rv; - } stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN; return 0; case NGHTTP3_HTTP_EVENT_DATA_BEGIN: stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN; return 0; case NGHTTP3_HTTP_EVENT_MSG_END: + rv = nghttp3_http_on_remote_end_stream(stream); + if (rv != 0) { + return rv; + } stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END; return 0; default: @@ -1141,13 +1101,13 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) { return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; } + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN; + return 0; + case NGHTTP3_HTTP_EVENT_MSG_END: rv = nghttp3_http_on_remote_end_stream(stream); if (rv != 0) { return rv; } - stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN; - return 0; - case NGHTTP3_HTTP_EVENT_MSG_END: stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END; return 0; default: @@ -1165,6 +1125,10 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, spec. */ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; } + rv = nghttp3_http_on_remote_end_stream(stream); + if (rv != 0) { + return rv; + } stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END; return 0; case NGHTTP3_HTTP_STATE_REQ_END: @@ -1192,10 +1156,6 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, stream->rx.http.status_code / 100 == 2) { return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; } - rv = nghttp3_http_on_remote_end_stream(stream); - if (rv != 0) { - return rv; - } stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN; return 0; case NGHTTP3_HTTP_EVENT_DATA_BEGIN: @@ -1205,6 +1165,10 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN; return 0; case NGHTTP3_HTTP_EVENT_MSG_END: + rv = nghttp3_http_on_remote_end_stream(stream); + if (rv != 0) { + return rv; + } stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END; return 0; default: @@ -1226,13 +1190,13 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, stream->rx.http.status_code / 100 == 2) { return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; } + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN; + return 0; + case NGHTTP3_HTTP_EVENT_MSG_END: rv = nghttp3_http_on_remote_end_stream(stream); if (rv != 0) { return rv; } - stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN; - return 0; - case NGHTTP3_HTTP_EVENT_MSG_END: stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END; return 0; default: @@ -1248,12 +1212,17 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, if (event != NGHTTP3_HTTP_EVENT_MSG_END) { return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; } + rv = nghttp3_http_on_remote_end_stream(stream); + if (rv != 0) { + return rv; + } stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END; return 0; case NGHTTP3_HTTP_STATE_RESP_END: return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; default: assert(0); + abort(); } } @@ -1267,11 +1236,6 @@ int nghttp3_stream_empty_headers_allowed(nghttp3_stream *stream) { } } -int nghttp3_stream_bidi_or_push(nghttp3_stream *stream) { - return (!nghttp3_stream_uni(stream->node.nid.id) || - stream->type == NGHTTP3_STREAM_TYPE_PUSH); -} - int nghttp3_stream_uni(int64_t stream_id) { return (stream_id & 0x2) != 0; } int nghttp3_client_stream_bidi(int64_t stream_id) { diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h index f7e375c85eb7f5..06292738a17e93 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h @@ -37,6 +37,7 @@ #include "nghttp3_buf.h" #include "nghttp3_frame.h" #include "nghttp3_qpack.h" +#include "nghttp3_objalloc.h" #define NGHTTP3_STREAM_MIN_CHUNK_SIZE 256 @@ -60,13 +61,14 @@ typedef enum nghttp3_stream_type { typedef enum nghttp3_ctrl_stream_state { NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE, NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH, - NGHTTP3_CTRL_STREAM_STATE_CANCEL_PUSH, NGHTTP3_CTRL_STREAM_STATE_SETTINGS, NGHTTP3_CTRL_STREAM_STATE_GOAWAY, NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID, NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME, NGHTTP3_CTRL_STREAM_STATE_SETTINGS_ID, NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE, + NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE_PRI_ELEM_ID, + NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE, } nghttp3_ctrl_stream_state; typedef enum nghttp3_req_stream_state { @@ -74,23 +76,10 @@ typedef enum nghttp3_req_stream_state { NGHTTP3_REQ_STREAM_STATE_FRAME_LENGTH, NGHTTP3_REQ_STREAM_STATE_DATA, NGHTTP3_REQ_STREAM_STATE_HEADERS, - NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE_PUSH_ID, - NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE, - NGHTTP3_REQ_STREAM_STATE_IGN_PUSH_PROMISE, NGHTTP3_REQ_STREAM_STATE_IGN_FRAME, NGHTTP3_REQ_STREAM_STATE_IGN_REST, } nghttp3_req_stream_state; -typedef enum nghttp3_push_stream_state { - NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE, - NGHTTP3_PUSH_STREAM_STATE_FRAME_LENGTH, - NGHTTP3_PUSH_STREAM_STATE_DATA, - NGHTTP3_PUSH_STREAM_STATE_HEADERS, - NGHTTP3_PUSH_STREAM_STATE_IGN_FRAME, - NGHTTP3_PUSH_STREAM_STATE_PUSH_ID, - NGHTTP3_PUSH_STREAM_STATE_IGN_REST, -} nghttp3_push_stream_state; - typedef struct nghttp3_varint_read_state { int64_t acc; size_t left; @@ -104,38 +93,46 @@ typedef struct nghttp3_stream_read_state { } nghttp3_stream_read_state; /* NGHTTP3_STREAM_FLAG_NONE indicates that no flag is set. */ -#define NGHTTP3_STREAM_FLAG_NONE 0x0000 +#define NGHTTP3_STREAM_FLAG_NONE 0x0000u /* NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED is set when a unidirectional stream type is identified. */ -#define NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED 0x0001 +#define NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED 0x0001u /* NGHTTP3_STREAM_FLAG_FC_BLOCKED indicates that stream is blocked by QUIC flow control. */ -#define NGHTTP3_STREAM_FLAG_FC_BLOCKED 0x0002 +#define NGHTTP3_STREAM_FLAG_FC_BLOCKED 0x0002u /* NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED indicates that application is temporarily unable to provide data. */ -#define NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED 0x0004 +#define NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED 0x0004u /* NGHTTP3_STREAM_FLAG_WRITE_END_STREAM indicates that application finished to feed outgoing data. */ -#define NGHTTP3_STREAM_FLAG_WRITE_END_STREAM 0x0008 +#define NGHTTP3_STREAM_FLAG_WRITE_END_STREAM 0x0008u /* NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED indicates that stream is blocked due to QPACK decoding. */ -#define NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED 0x0010 +#define NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED 0x0010u /* NGHTTP3_STREAM_FLAG_READ_EOF indicates that remote endpoint sent fin. */ -#define NGHTTP3_STREAM_FLAG_READ_EOF 0x0020 +#define NGHTTP3_STREAM_FLAG_READ_EOF 0x0020u /* NGHTTP3_STREAM_FLAG_CLOSED indicates that QUIC stream was closed. nghttp3_stream object can still alive because it might be blocked by QPACK decoder. */ -#define NGHTTP3_STREAM_FLAG_CLOSED 0x0040 -/* NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED indicates that stream is - blocked because the corresponding PUSH_PROMISE has not been - received yet. */ -#define NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED 0x0080 +#define NGHTTP3_STREAM_FLAG_CLOSED 0x0040u /* NGHTTP3_STREAM_FLAG_SHUT_WR indicates that any further write operation to a stream is prohibited. */ -#define NGHTTP3_STREAM_FLAG_SHUT_WR 0x0100 -/* NGHTTP3_STREAM_FLAG_RESET indicates that stream is reset. */ -#define NGHTTP3_STREAM_FLAG_RESET 0x0200 +#define NGHTTP3_STREAM_FLAG_SHUT_WR 0x0100u +/* NGHTTP3_STREAM_FLAG_SHUT_RD indicates that a read-side stream is + closed abruptly and any incoming and pending stream data is just + discarded for a stream. */ +#define NGHTTP3_STREAM_FLAG_SHUT_RD 0x0200u +/* NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET indicates that server + overrides stream priority. */ +#define NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET 0x0400u +/* NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED indicates that server + received PRIORITY_UPDATE frame for this stream. */ +#define NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED 0x0800u +/* NGHTTP3_STREAM_FLAG_HTTP_ERROR indicates that + NGHTTP3_ERR_MALFORMED_HTTP_HEADER error is encountered while + processing incoming HTTP fields. */ +#define NGHTTP3_STREAM_FLAG_HTTP_ERROR 0x1000u typedef enum nghttp3_stream_http_state { NGHTTP3_HTTP_STATE_NONE, @@ -164,15 +161,11 @@ typedef enum nghttp3_stream_http_event { NGHTTP3_HTTP_EVENT_DATA_END, NGHTTP3_HTTP_EVENT_HEADERS_BEGIN, NGHTTP3_HTTP_EVENT_HEADERS_END, - NGHTTP3_HTTP_EVENT_PUSH_PROMISE_BEGIN, - NGHTTP3_HTTP_EVENT_PUSH_PROMISE_END, NGHTTP3_HTTP_EVENT_MSG_END, } nghttp3_stream_http_event; typedef struct nghttp3_stream nghttp3_stream; -typedef struct nghttp3_push_promise nghttp3_push_promise; - /* * nghttp3_stream_acked_data is a callback function which is invoked * when data sent on stream denoted by |stream_id| supplied from @@ -185,7 +178,7 @@ typedef struct nghttp3_push_promise nghttp3_push_promise; * NGHTTP3_ERR_CALLBACK_FAILURE. */ typedef int (*nghttp3_stream_acked_data)(nghttp3_stream *stream, - int64_t stream_id, size_t datalen, + int64_t stream_id, uint64_t datalen, void *user_data); typedef struct nghttp3_stream_callbacks { @@ -202,65 +195,70 @@ typedef struct nghttp3_http_state { /* recv_content_length is the number of body bytes received so far. */ int64_t recv_content_length; - uint16_t flags; + uint32_t flags; /* pri is a stream priority produced by nghttp3_pri_to_uint8. */ uint8_t pri; } nghttp3_http_state; struct nghttp3_stream { - const nghttp3_mem *mem; - nghttp3_map_entry me; - /* node is a node in dependency tree. For server initiated - unidirectional stream (push), scheduling is done via - corresponding nghttp3_push_promise object pointed by pp. */ - nghttp3_tnode node; - nghttp3_pq_entry qpack_blocked_pe; - nghttp3_stream_callbacks callbacks; - nghttp3_ringbuf frq; - nghttp3_ringbuf chunks; - nghttp3_ringbuf outq; - /* inq stores the stream raw data which cannot be read because - stream is blocked by QPACK decoder. */ - nghttp3_ringbuf inq; - nghttp3_qpack_stream_context qpack_sctx; - /* conn is a reference to underlying connection. It could be NULL - if stream is not a request/push stream. */ - nghttp3_conn *conn; - void *user_data; - /* unsent_bytes is the number of bytes in outq not written yet */ - size_t unsent_bytes; - /* outq_idx is an index into outq where next write is made. */ - size_t outq_idx; - /* outq_offset is write offset relative to the element at outq_idx - in outq. */ - size_t outq_offset; - /* ack_offset is offset acknowledged by peer relative to the first - element in outq. */ - uint64_t ack_offset; - /* ack_done is the number of bytes notified to an application that - they are acknowledged inside the first outq element if it is of - type NGHTTP3_BUF_TYPE_ALIEN. */ - size_t ack_done; - size_t unscheduled_nwrite; - nghttp3_stream_type type; - nghttp3_stream_read_state rstate; - /* pp is nghttp3_push_promise that this stream fulfills. */ - nghttp3_push_promise *pp; - /* error_code indicates the reason of closure of this stream. */ - uint64_t error_code; - - struct { - nghttp3_stream_http_state hstate; - } tx; - - struct { - nghttp3_stream_http_state hstate; - nghttp3_http_state http; - } rx; - - uint16_t flags; + union { + struct { + const nghttp3_mem *mem; + nghttp3_objalloc *out_chunk_objalloc; + nghttp3_objalloc *stream_objalloc; + nghttp3_tnode node; + nghttp3_pq_entry qpack_blocked_pe; + nghttp3_stream_callbacks callbacks; + nghttp3_ringbuf frq; + nghttp3_ringbuf chunks; + nghttp3_ringbuf outq; + /* inq stores the stream raw data which cannot be read because + stream is blocked by QPACK decoder. */ + nghttp3_ringbuf inq; + nghttp3_qpack_stream_context qpack_sctx; + /* conn is a reference to underlying connection. It could be NULL + if stream is not a request stream. */ + nghttp3_conn *conn; + void *user_data; + /* unsent_bytes is the number of bytes in outq not written yet */ + uint64_t unsent_bytes; + /* outq_idx is an index into outq where next write is made. */ + size_t outq_idx; + /* outq_offset is write offset relative to the element at outq_idx + in outq. */ + uint64_t outq_offset; + /* ack_offset is offset acknowledged by peer relative to the first + element in outq. */ + uint64_t ack_offset; + /* ack_done is the number of bytes notified to an application that + they are acknowledged inside the first outq element if it is of + type NGHTTP3_BUF_TYPE_ALIEN. */ + uint64_t ack_done; + uint64_t unscheduled_nwrite; + nghttp3_stream_type type; + nghttp3_stream_read_state rstate; + /* error_code indicates the reason of closure of this stream. */ + uint64_t error_code; + + struct { + uint64_t offset; + nghttp3_stream_http_state hstate; + } tx; + + struct { + nghttp3_stream_http_state hstate; + nghttp3_http_state http; + } rx; + + uint16_t flags; + }; + + nghttp3_opl_entry oplent; + }; }; +nghttp3_objalloc_def(stream, nghttp3_stream, oplent); + typedef struct nghttp3_frame_entry { nghttp3_frame fr; union { @@ -275,6 +273,8 @@ typedef struct nghttp3_frame_entry { int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id, uint64_t seq, const nghttp3_stream_callbacks *callbacks, + nghttp3_objalloc *out_chunk_objalloc, + nghttp3_objalloc *stream_objalloc, const nghttp3_mem *mem); void nghttp3_stream_del(nghttp3_stream *stream); @@ -293,8 +293,6 @@ int nghttp3_stream_fill_outq(nghttp3_stream *stream); int nghttp3_stream_write_stream_type(nghttp3_stream *stream); -int nghttp3_stream_write_stream_type_push_id(nghttp3_stream *stream); - nghttp3_ssize nghttp3_stream_writev(nghttp3_stream *stream, int *pfin, nghttp3_vec *vec, size_t veccnt); @@ -308,15 +306,12 @@ int nghttp3_stream_outq_add(nghttp3_stream *stream, int nghttp3_stream_write_headers(nghttp3_stream *stream, nghttp3_frame_entry *frent); -int nghttp3_stream_write_push_promise(nghttp3_stream *stream, - nghttp3_frame_entry *frent); - int nghttp3_stream_write_header_block(nghttp3_stream *stream, nghttp3_qpack_encoder *qenc, nghttp3_stream *qenc_stream, nghttp3_buf *rbuf, nghttp3_buf *ebuf, - int64_t frame_type, int64_t push_id, - const nghttp3_nv *nva, size_t nvlen); + int64_t frame_type, const nghttp3_nv *nva, + size_t nvlen); int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, nghttp3_frame_entry *frent); @@ -324,11 +319,11 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, int nghttp3_stream_write_settings(nghttp3_stream *stream, nghttp3_frame_entry *frent); -int nghttp3_stream_write_cancel_push(nghttp3_stream *stream, - nghttp3_frame_entry *frent); +int nghttp3_stream_write_goaway(nghttp3_stream *stream, + nghttp3_frame_entry *frent); -int nghttp3_stream_write_max_push_id(nghttp3_stream *stream, - nghttp3_frame_entry *frent); +int nghttp3_stream_write_priority_update(nghttp3_stream *stream, + nghttp3_frame_entry *frent); int nghttp3_stream_ensure_chunk(nghttp3_stream *stream, size_t need); @@ -374,12 +369,6 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, int nghttp3_stream_empty_headers_allowed(nghttp3_stream *stream); -/* - * nghttp3_stream_bidi_or_push returns nonzero if |stream| is - * bidirectional or push stream. - */ -int nghttp3_stream_bidi_or_push(nghttp3_stream *stream); - /* * nghttp3_stream_uni returns nonzero if stream identified by * |stream_id| is unidirectional. diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.c b/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.c index 94dca7dbf76325..36e738c3469aca 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.c @@ -82,7 +82,7 @@ static uint64_t pq_get_first_cycle(nghttp3_pq *pq) { } int nghttp3_tnode_schedule(nghttp3_tnode *tnode, nghttp3_pq *pq, - size_t nwrite) { + uint64_t nwrite) { uint64_t penalty = nwrite / NGHTTP3_STREAM_MIN_WRITELEN; if (tnode->pe.index == NGHTTP3_PQ_BAD_INDEX) { diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.h b/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.h index 817aec034c03a4..f71dcf5ee31ad6 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.h @@ -72,7 +72,8 @@ void nghttp3_tnode_unschedule(nghttp3_tnode *tnode, nghttp3_pq *pq); * If |tnode| has already been scheduled, it is rescheduled by the * amount of |nwrite|. */ -int nghttp3_tnode_schedule(nghttp3_tnode *tnode, nghttp3_pq *pq, size_t nwrite); +int nghttp3_tnode_schedule(nghttp3_tnode *tnode, nghttp3_pq *pq, + uint64_t nwrite); /* * nghttp3_tnode_is_scheduled returns nonzero if |tnode| is scheduled. diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_vec.c b/deps/ngtcp2/nghttp3/lib/nghttp3_vec.c index ab292ac8ae31d2..ab58ff5832bea9 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_vec.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_vec.c @@ -26,9 +26,9 @@ #include "nghttp3_vec.h" #include "nghttp3_macro.h" -size_t nghttp3_vec_len(const nghttp3_vec *vec, size_t n) { +uint64_t nghttp3_vec_len(const nghttp3_vec *vec, size_t n) { size_t i; - size_t res = 0; + uint64_t res = 0; for (i = 0; i < n; ++i) { res += vec[i].len; @@ -36,3 +36,20 @@ size_t nghttp3_vec_len(const nghttp3_vec *vec, size_t n) { return res; } + +int64_t nghttp3_vec_len_varint(const nghttp3_vec *vec, size_t n) { + uint64_t res = 0; + size_t len; + size_t i; + + for (i = 0; i < n; ++i) { + len = vec[i].len; + if (len > NGHTTP3_MAX_VARINT - res) { + return -1; + } + + res += len; + } + + return (int64_t)res; +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_vec.h b/deps/ngtcp2/nghttp3/lib/nghttp3_vec.h index c1a928e3e1d1ab..473d1467310062 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_vec.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_vec.h @@ -32,4 +32,10 @@ #include +/* + * nghttp3_vec_len_varint is similar to nghttp3_vec_len, but it + * returns -1 if the sum of the length exceeds NGHTTP3_MAX_VARINT. + */ +int64_t nghttp3_vec_len_varint(const nghttp3_vec *vec, size_t n); + #endif /* NGHTTP3_VEC_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_version.c b/deps/ngtcp2/nghttp3/lib/nghttp3_version.c index dfad4793c4bc11..c460cc72835b1d 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_version.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_version.c @@ -31,7 +31,7 @@ static nghttp3_info version = {NGHTTP3_VERSION_AGE, NGHTTP3_VERSION_NUM, NGHTTP3_VERSION}; -nghttp3_info *nghttp3_version(int least_version) { +const nghttp3_info *nghttp3_version(int least_version) { if (least_version > NGHTTP3_VERSION_NUM) { return NULL; } diff --git a/deps/ngtcp2/ngtcp2.gyp b/deps/ngtcp2/ngtcp2.gyp index 9ea93be2091ac4..a47a791610e86a 100644 --- a/deps/ngtcp2/ngtcp2.gyp +++ b/deps/ngtcp2/ngtcp2.gyp @@ -6,6 +6,9 @@ 'ngtcp2_sources': [ 'ngtcp2/lib/ngtcp2_acktr.c', 'ngtcp2/lib/ngtcp2_addr.c', + 'ngtcp2/lib/ngtcp2_balloc.c', + 'ngtcp2/lib/ngtcp2_bbr.c', + 'ngtcp2/lib/ngtcp2_bbr2.c', 'ngtcp2/lib/ngtcp2_buf.c', 'ngtcp2/lib/ngtcp2_cc.c', 'ngtcp2/lib/ngtcp2_cid.c', @@ -19,8 +22,11 @@ 'ngtcp2/lib/ngtcp2_log.c', 'ngtcp2/lib/ngtcp2_map.c', 'ngtcp2/lib/ngtcp2_mem.c', + 'ngtcp2/lib/ngtcp2_objalloc.c', + 'ngtcp2/lib/ngtcp2_opl.c', 'ngtcp2/lib/ngtcp2_path.c', 'ngtcp2/lib/ngtcp2_pkt.c', + 'ngtcp2/lib/ngtcp2_pmtud.c', 'ngtcp2/lib/ngtcp2_ppe.c', 'ngtcp2/lib/ngtcp2_pq.c', 'ngtcp2/lib/ngtcp2_pv.c', @@ -34,7 +40,8 @@ 'ngtcp2/lib/ngtcp2_strm.c', 'ngtcp2/lib/ngtcp2_vec.c', 'ngtcp2/lib/ngtcp2_version.c', - 'ngtcp2/crypto/shared.c', + 'ngtcp2/lib/ngtcp2_window_filter.c', + 'ngtcp2/crypto/shared.c' ], 'ngtcp2_sources_openssl': [ 'ngtcp2/crypto/openssl/openssl.c' @@ -43,6 +50,7 @@ 'ngtcp2/crypto/boringssl/boringssl.c' ], 'nghttp3_sources': [ + 'nghttp3/lib/nghttp3_balloc.c', 'nghttp3/lib/nghttp3_buf.c', 'nghttp3/lib/nghttp3_conn.c', 'nghttp3/lib/nghttp3_conv.c', @@ -55,6 +63,8 @@ 'nghttp3/lib/nghttp3_ksl.c', 'nghttp3/lib/nghttp3_map.c', 'nghttp3/lib/nghttp3_mem.c', + 'nghttp3/lib/nghttp3_objalloc.c', + 'nghttp3/lib/nghttp3_opl.c', 'nghttp3/lib/nghttp3_pq.c', 'nghttp3/lib/nghttp3_qpack.c', 'nghttp3/lib/nghttp3_qpack_huffman.c', @@ -117,6 +127,7 @@ '', 'ngtcp2/lib/includes', 'ngtcp2/crypto/includes', + 'ngtcp2/crypto', ] }, 'sources': [ diff --git a/deps/ngtcp2/ngtcp2/crypto/boringssl/boringssl.c b/deps/ngtcp2/ngtcp2/crypto/boringssl/boringssl.c index 8f75b485e3e56b..015032d41ca0ce 100644 --- a/deps/ngtcp2/ngtcp2/crypto/boringssl/boringssl.c +++ b/deps/ngtcp2/ngtcp2/crypto/boringssl/boringssl.c @@ -35,15 +35,15 @@ #include #include #include +#include #include +#include #include "shared.h" -/* Define cipher types because BoringSSL does not implement EVP - interface for chacha20. */ typedef enum ngtcp2_crypto_boringssl_cipher_type { - NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_128_CTR, - NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_256_CTR, + NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128, + NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256, NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20, } ngtcp2_crypto_boringssl_cipher_type; @@ -51,22 +51,31 @@ typedef struct ngtcp2_crypto_boringssl_cipher { ngtcp2_crypto_boringssl_cipher_type type; } ngtcp2_crypto_boringssl_cipher; -static ngtcp2_crypto_boringssl_cipher crypto_cipher_evp_aes_128_ctr = { - NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_128_CTR, +static ngtcp2_crypto_boringssl_cipher crypto_cipher_aes_128 = { + NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128, }; -static ngtcp2_crypto_boringssl_cipher crypto_cipher_evp_aes_256_ctr = { - NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_256_CTR, +static ngtcp2_crypto_boringssl_cipher crypto_cipher_aes_256 = { + NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256, }; static ngtcp2_crypto_boringssl_cipher crypto_cipher_chacha20 = { NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20, }; +ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) { + return ngtcp2_crypto_aead_init(aead, (void *)EVP_aead_aes_128_gcm()); +} + +ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) { + md->native_handle = (void *)EVP_sha256(); + return md; +} + ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) { ngtcp2_crypto_aead_init(&ctx->aead, (void *)EVP_aead_aes_128_gcm()); ctx->md.native_handle = (void *)EVP_sha256(); - ctx->hp.native_handle = (void *)&crypto_cipher_evp_aes_128_ctr; + ctx->hp.native_handle = (void *)&crypto_cipher_aes_128; ctx->max_encryption = 0; ctx->max_decryption_failure = 0; return ctx; @@ -123,9 +132,9 @@ static uint64_t crypto_ssl_get_aead_max_decryption_failure(SSL *ssl) { static const ngtcp2_crypto_boringssl_cipher *crypto_ssl_get_hp(SSL *ssl) { switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { case TLS1_CK_AES_128_GCM_SHA256: - return &crypto_cipher_evp_aes_128_ctr; + return &crypto_cipher_aes_128; case TLS1_CK_AES_256_GCM_SHA384: - return &crypto_cipher_evp_aes_256_ctr; + return &crypto_cipher_aes_256; case TLS1_CK_CHACHA20_POLY1305_SHA256: return &crypto_cipher_chacha20; default: @@ -216,19 +225,15 @@ void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) { } } -typedef enum ngtcp2_crypto_boringssl_cipher_ctx_type { - NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP, - NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_CHACHA20, -} ngtcp2_crypto_boringssl_cipher_ctx_type; - typedef struct ngtcp2_crypto_boringssl_cipher_ctx { - ngtcp2_crypto_boringssl_cipher_ctx_type type; + ngtcp2_crypto_boringssl_cipher_type type; union { - /* ctx is EVP_CIPHER_CTX used when type == - NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP. */ - EVP_CIPHER_CTX *ctx; + /* aes_key is an encryption key when type is either + NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128 or + NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256. */ + AES_KEY aes_key; /* key contains an encryption key when type == - NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_CHACHA20. */ + NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20. */ uint8_t key[32]; }; } ngtcp2_crypto_boringssl_cipher_ctx; @@ -238,70 +243,41 @@ int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx, const uint8_t *key) { ngtcp2_crypto_boringssl_cipher *hp_cipher = cipher->native_handle; ngtcp2_crypto_boringssl_cipher_ctx *ctx; - EVP_CIPHER_CTX *actx; - const EVP_CIPHER *evp_cipher; - - switch (hp_cipher->type) { - case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_128_CTR: - evp_cipher = EVP_aes_128_ctr(); - break; - case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_256_CTR: - evp_cipher = EVP_aes_256_ctr(); - break; - case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20: - ctx = malloc(sizeof(*ctx)); - if (ctx == NULL) { - return -1; - } - - ctx->type = NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_CHACHA20; - memcpy(ctx->key, key, sizeof(ctx->key)); - - cipher_ctx->native_handle = ctx; - - return 0; - default: - assert(0); - }; - - actx = EVP_CIPHER_CTX_new(); - if (actx == NULL) { - return -1; - } - - if (!EVP_EncryptInit_ex(actx, evp_cipher, NULL, key, NULL)) { - EVP_CIPHER_CTX_free(actx); - return -1; - } + int rv; + (void)rv; ctx = malloc(sizeof(*ctx)); if (ctx == NULL) { - EVP_CIPHER_CTX_free(actx); return -1; } - ctx->type = NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP; - ctx->ctx = actx; - + ctx->type = hp_cipher->type; cipher_ctx->native_handle = ctx; - return 0; + switch (hp_cipher->type) { + case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128: + rv = AES_set_encrypt_key(key, 128, &ctx->aes_key); + assert(0 == rv); + return 0; + case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256: + rv = AES_set_encrypt_key(key, 256, &ctx->aes_key); + assert(0 == rv); + return 0; + case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20: + memcpy(ctx->key, key, sizeof(ctx->key)); + return 0; + default: + assert(0); + abort(); + }; } void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) { - ngtcp2_crypto_boringssl_cipher_ctx *ctx; - if (!cipher_ctx->native_handle) { return; } - ctx = cipher_ctx->native_handle; - - if (ctx->type == NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP) { - EVP_CIPHER_CTX_free(ctx->ctx); - } - - free(ctx); + free(cipher_ctx->native_handle); } int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md, @@ -331,18 +307,32 @@ int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen, return 0; } +int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, const uint8_t *secret, + size_t secretlen, const uint8_t *salt, size_t saltlen, + const uint8_t *info, size_t infolen) { + const EVP_MD *prf = md->native_handle; + + if (HKDF(dest, destlen, prf, secret, secretlen, salt, saltlen, info, + infolen) != 1) { + return -1; + } + + return 0; +} + int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *plaintext, size_t plaintextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen) { + const uint8_t *aad, size_t aadlen) { const EVP_AEAD *cipher = aead->native_handle; EVP_AEAD_CTX *actx = aead_ctx->native_handle; size_t max_outlen = plaintextlen + EVP_AEAD_max_overhead(cipher); size_t outlen; if (EVP_AEAD_CTX_seal(actx, dest, &outlen, max_outlen, nonce, noncelen, - plaintext, plaintextlen, ad, adlen) != 1) { + plaintext, plaintextlen, aad, aadlen) != 1) { return -1; } @@ -353,15 +343,21 @@ int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *ciphertext, size_t ciphertextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen) { + const uint8_t *aad, size_t aadlen) { + const EVP_AEAD *cipher = aead->native_handle; EVP_AEAD_CTX *actx = aead_ctx->native_handle; - size_t max_outlen = ciphertextlen; + size_t max_overhead = EVP_AEAD_max_overhead(cipher); + size_t max_outlen; size_t outlen; - (void)aead; + if (ciphertextlen < max_overhead) { + return -1; + } + + max_outlen = ciphertextlen - max_overhead; if (EVP_AEAD_CTX_open(actx, dest, &outlen, max_outlen, nonce, noncelen, - ciphertext, ciphertextlen, ad, adlen) != 1) { + ciphertext, ciphertextlen, aad, aadlen) != 1) { return -1; } @@ -373,23 +369,16 @@ int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp, const uint8_t *sample) { static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00"; ngtcp2_crypto_boringssl_cipher_ctx *ctx = hp_ctx->native_handle; - EVP_CIPHER_CTX *actx; - int len; uint32_t counter; (void)hp; switch (ctx->type) { - case NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP: - actx = ctx->ctx; - if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, sample) || - !EVP_EncryptUpdate(actx, dest, &len, PLAINTEXT, - sizeof(PLAINTEXT) - 1) || - !EVP_EncryptFinal_ex(actx, dest + sizeof(PLAINTEXT) - 1, &len)) { - return -1; - } + case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128: + case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256: + AES_ecb_encrypt(sample, dest, &ctx->aes_key, 1); return 0; - case NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_CHACHA20: + case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20: #if defined(WORDS_BIGENDIAN) counter = (uint32_t)sample[0] + (uint32_t)(sample[1] << 8) + (uint32_t)(sample[2] << 16) + (uint32_t)(sample[3] << 24); @@ -434,10 +423,7 @@ int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn, SSL_reset_early_data_reject(ssl); - rv = ngtcp2_conn_early_data_rejected(conn); - if (rv != 0) { - return -1; - } + ngtcp2_conn_early_data_rejected(conn); goto retry; default: @@ -472,24 +458,13 @@ int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn, int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) { SSL *ssl = tls; - ngtcp2_transport_params_type exttype = - ngtcp2_conn_is_server(conn) - ? NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO - : NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS; const uint8_t *tp; size_t tplen; - ngtcp2_transport_params params; int rv; SSL_get_peer_quic_transport_params(ssl, &tp, &tplen); - rv = ngtcp2_decode_transport_params(¶ms, exttype, tp, tplen); - if (rv != 0) { - ngtcp2_conn_set_tls_error(conn, rv); - return -1; - } - - rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms); + rv = ngtcp2_conn_decode_remote_transport_params(conn, tp, tplen); if (rv != 0) { ngtcp2_conn_set_tls_error(conn, rv); return -1; @@ -520,6 +495,7 @@ ngtcp2_crypto_level ngtcp2_crypto_boringssl_from_ssl_encryption_level( return NGTCP2_CRYPTO_LEVEL_APPLICATION; default: assert(0); + abort(); } } @@ -536,5 +512,116 @@ enum ssl_encryption_level_t ngtcp2_crypto_boringssl_from_ngtcp2_crypto_level( return ssl_encryption_early_data; default: assert(0); + abort(); + } +} + +int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data, + void *user_data) { + (void)conn; + (void)user_data; + + if (RAND_bytes(data, NGTCP2_PATH_CHALLENGE_DATALEN) != 1) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +int ngtcp2_crypto_random(uint8_t *data, size_t datalen) { + if (RAND_bytes(data, datalen) != 1) { + return -1; + } + + return 0; +} + +static int set_read_secret(SSL *ssl, enum ssl_encryption_level_t bssl_level, + const SSL_CIPHER *cipher, const uint8_t *secret, + size_t secretlen) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + ngtcp2_crypto_level level = + ngtcp2_crypto_boringssl_from_ssl_encryption_level(bssl_level); + (void)cipher; + + if (ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level, + secret, secretlen) != 0) { + return 0; + } + + return 1; +} + +static int set_write_secret(SSL *ssl, enum ssl_encryption_level_t bssl_level, + const SSL_CIPHER *cipher, const uint8_t *secret, + size_t secretlen) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + ngtcp2_crypto_level level = + ngtcp2_crypto_boringssl_from_ssl_encryption_level(bssl_level); + (void)cipher; + + if (ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level, + secret, secretlen) != 0) { + return 0; } + + return 1; +} + +static int add_handshake_data(SSL *ssl, enum ssl_encryption_level_t bssl_level, + const uint8_t *data, size_t datalen) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + ngtcp2_crypto_level level = + ngtcp2_crypto_boringssl_from_ssl_encryption_level(bssl_level); + int rv; + + rv = ngtcp2_conn_submit_crypto_data(conn, level, data, datalen); + if (rv != 0) { + ngtcp2_conn_set_tls_error(conn, rv); + return 0; + } + + return 1; +} + +static int flush_flight(SSL *ssl) { + (void)ssl; + return 1; +} + +static int send_alert(SSL *ssl, enum ssl_encryption_level_t bssl_level, + uint8_t alert) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + (void)bssl_level; + + ngtcp2_conn_set_tls_alert(conn, alert); + + return 1; +} + +static SSL_QUIC_METHOD quic_method = { + set_read_secret, set_write_secret, add_handshake_data, + flush_flight, send_alert, +}; + +static void crypto_boringssl_configure_context(SSL_CTX *ssl_ctx) { + SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION); + SSL_CTX_set_quic_method(ssl_ctx, &quic_method); +} + +int ngtcp2_crypto_boringssl_configure_server_context(SSL_CTX *ssl_ctx) { + crypto_boringssl_configure_context(ssl_ctx); + + return 0; +} + +int ngtcp2_crypto_boringssl_configure_client_context(SSL_CTX *ssl_ctx) { + crypto_boringssl_configure_context(ssl_ctx); + + return 0; } diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h index 23901d18c1646e..4736b51c3cb48d 100644 --- a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h +++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h @@ -31,6 +31,13 @@ extern "C" { #endif +#ifdef WIN32 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +#endif /* WIN32 */ + /** * @macro * @@ -55,22 +62,13 @@ extern "C" { */ #define NGTCP2_CRYPTO_INITIAL_IVLEN 12 -/** - * @function - * - * `ngtcp2_crypto_ctx_initial` initializes |ctx| for Initial packet - * encryption and decryption. - */ -NGTCP2_EXTERN ngtcp2_crypto_ctx * -ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx); - /** * @function * * `ngtcp2_crypto_ctx_tls` initializes |ctx| by extracting negotiated * ciphers and message digests from native TLS session * |tls_native_handle|. This is used for encrypting/decrypting - * Handshake and Short packets. + * Handshake and Short header packets. * * If libngtcp2_crypto_openssl is linked, |tls_native_handle| must be * a pointer to SSL object. @@ -92,33 +90,6 @@ NGTCP2_EXTERN ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx, NGTCP2_EXTERN ngtcp2_crypto_ctx * ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx, void *tls_native_handle); -/** - * @function - * - * `ngtcp2_crypto_aead_init` initializes |aead| with the provided - * |aead_native_handle| which is an underlying AEAD object. - * - * If libngtcp2_crypto_openssl is linked, |aead_native_handle| must be - * a pointer to EVP_CIPHER. - * - * If libngtcp2_crypto_gnutls is linked, |aead_native_handle| must be - * gnutls_cipher_algorithm_t casted to ``void *``. - * - * If libngtcp2_crypto_boringssl is linked, |aead_native_handle| must - * be a pointer to EVP_AEAD. - */ -NGTCP2_EXTERN ngtcp2_crypto_aead * -ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead, void *aead_native_handle); - -/** - * @function - * - * `ngtcp2_crypto_aead_retry` initializes |aead| with the AEAD cipher - * AEAD_AES_128_GCM for Retry packet integrity protection. - */ -NGTCP2_EXTERN ngtcp2_crypto_aead * -ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead); - /** * @function * @@ -191,6 +162,20 @@ NGTCP2_EXTERN int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen, const uint8_t *info, size_t infolen); +/** + * @function + * + * `ngtcp2_crypto_hkdf` performs HKDF operation. The result is + * |destlen| bytes long and is stored to the buffer pointed by |dest|. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, + const uint8_t *secret, size_t secretlen, + const uint8_t *salt, size_t saltlen, + const uint8_t *info, size_t infolen); + /** * @function * @@ -235,39 +220,16 @@ typedef enum ngtcp2_crypto_side { NGTCP2_EXTERN size_t ngtcp2_crypto_packet_protection_ivlen(const ngtcp2_crypto_aead *aead); -/** - * @function - * - * `ngtcp2_crypto_derive_packet_protection_key` derives packet - * protection key. This function writes packet protection key into - * the buffer pointed by |key|. |key| must point to the buffer which - * is at least ngtcp2_crypto_aead_keylen(aead) bytes long. This - * function writes packet protection IV into |iv|. |iv| must point to - * the buffer which is at least - * ngtcp2_crypto_packet_protection_ivlen(aead). |key| is - * ngtcp2_crypto_aead_keylen(aead) bytes long. |iv| is - * ngtcp2_crypto_packet_protection_ivlen(aead) bytes long. - * - * If |hp| is not NULL, this function also derives packet header - * protection key and writes the key into the buffer pointed by |hp|. - * The length of key is ngtcp2_crypto_aead_keylen(aead) bytes long. - * |hp|, if not NULL, must have enough capacity to store the key. - * - * This function returns 0 if it succeeds, or -1. - */ -NGTCP2_EXTERN int ngtcp2_crypto_derive_packet_protection_key( - uint8_t *key, uint8_t *iv, uint8_t *hp, const ngtcp2_crypto_aead *aead, - const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen); - /** * @function * * `ngtcp2_crypto_encrypt` encrypts |plaintext| of length * |plaintextlen| and writes the ciphertext into the buffer pointed by * |dest|. The length of ciphertext is plaintextlen + - * ngtcp2_crypto_aead_max_overhead(aead) bytes long. |dest| must have - * enough capacity to store the ciphertext. It is allowed to specify - * the same value to |dest| and |plaintext|. + * :member:`aead->max_overhead ` + * bytes long. |dest| must have enough capacity to store the + * ciphertext. It is allowed to specify the same value to |dest| and + * |plaintext|. * * This function returns 0 if it succeeds, or -1. */ @@ -277,14 +239,14 @@ NGTCP2_EXTERN int ngtcp2_crypto_encrypt(uint8_t *dest, const uint8_t *plaintext, size_t plaintextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen); + const uint8_t *aad, size_t aadlen); /** * @function * * `ngtcp2_crypto_encrypt_cb` is a wrapper function around * `ngtcp2_crypto_encrypt`. It can be directly passed to - * :member:`ngtcp2_conn_callbacks.encrypt` field. + * :member:`ngtcp2_callbacks.encrypt` field. * * This function returns 0 if it succeeds, or * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. @@ -294,7 +256,7 @@ ngtcp2_crypto_encrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *plaintext, size_t plaintextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen); + const uint8_t *aad, size_t aadlen); /** * @function @@ -302,9 +264,10 @@ ngtcp2_crypto_encrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead, * `ngtcp2_crypto_decrypt` decrypts |ciphertext| of length * |ciphertextlen| and writes the plaintext into the buffer pointed by * |dest|. The length of plaintext is ciphertextlen - - * ngtcp2_crypto_aead_max_overhead(aead) bytes long. |dest| must have - * enough capacity to store the plaintext. It is allowed to specify - * the same value to |dest| and |ciphertext|. + * :member:`aead->max_overhead ` + * bytes long. |dest| must have enough capacity to store the + * plaintext. It is allowed to specify the same value to |dest| and + * |ciphertext|. * * This function returns 0 if it succeeds, or -1. */ @@ -314,14 +277,14 @@ NGTCP2_EXTERN int ngtcp2_crypto_decrypt(uint8_t *dest, const uint8_t *ciphertext, size_t ciphertextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen); + const uint8_t *aad, size_t aadlen); /** * @function * * `ngtcp2_crypto_decrypt_cb` is a wrapper function around * `ngtcp2_crypto_decrypt`. It can be directly passed to - * :member:`ngtcp2_conn_callbacks.decrypt` field. + * :member:`ngtcp2_callbacks.decrypt` field. * * This function returns 0 if it succeeds, or * :macro:`NGTCP2_ERR_TLS_DECRYPT`. @@ -331,15 +294,19 @@ ngtcp2_crypto_decrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *ciphertext, size_t ciphertextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen); + const uint8_t *aad, size_t aadlen); /** * @function * * `ngtcp2_crypto_hp_mask` generates mask which is used in packet * header encryption. The mask is written to the buffer pointed by - * |dest|. The length of mask is 5 bytes. |dest| must have enough - * capacity to store the mask. + * |dest|. The sample is passed as |sample| which is + * :macro:`NGTCP2_HP_SAMPLELEN` bytes long. The length of mask must + * be at least :macro:`NGTCP2_HP_MASKLEN`. The library only uses the + * first :macro:`NGTCP2_HP_MASKLEN` bytes of the produced mask. The + * buffer pointed by |dest| must have at least + * :macro:`NGTCP2_HP_SAMPLELEN` bytes available. * * This function returns 0 if it succeeds, or -1. */ @@ -353,7 +320,7 @@ NGTCP2_EXTERN int ngtcp2_crypto_hp_mask(uint8_t *dest, * * `ngtcp2_crypto_hp_mask_cb` is a wrapper function around * `ngtcp2_crypto_hp_mask`. It can be directly passed to - * :member:`ngtcp2_conn_callbacks.hp_mask` field. + * :member:`ngtcp2_callbacks.hp_mask` field. * * This function returns 0 if it succeeds, or * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. @@ -379,16 +346,30 @@ ngtcp2_crypto_hp_mask_cb(uint8_t *dest, const ngtcp2_crypto_cipher *hp, * |secretlen| specifies the length of |secret|. * * The length of packet protection key and header protection key is - * ngtcp2_crypto_aead(ctx->aead), and the length of packet protection - * IV is ngtcp2_crypto_packet_protection_ivlen(ctx->aead) where ctx - * can be obtained by `ngtcp2_crypto_ctx_tls`. + * `ngtcp2_crypto_aead_keylen(ctx->aead) `, + * and the length of packet protection IV is + * `ngtcp2_crypto_packet_protection_ivlen(ctx->aead) + * ` where ctx is obtained by + * `ngtcp2_crypto_ctx_tls` (or `ngtcp2_crypto_ctx_tls_early` if + * |level| == :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`). * * In the first call of this function, it calls - * `ngtcp2_conn_set_crypto_ctx` to set negotiated AEAD and message - * digest algorithm. After the successful call of this function, - * application can use `ngtcp2_conn_get_crypto_ctx` to get the object. - * It also calls `ngtcp2_conn_set_aead_overhead` to set AEAD tag - * length. + * `ngtcp2_conn_set_crypto_ctx` (or `ngtcp2_conn_set_early_crypto_ctx` + * if |level| == + * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to set + * negotiated AEAD and message digest algorithm. After the successful + * call of this function, application can use + * `ngtcp2_conn_get_crypto_ctx` (or `ngtcp2_conn_get_early_crypto_ctx` + * if |level| == + * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to get + * :type:`ngtcp2_crypto_ctx`. + * + * If |conn| is initialized as client, and |level| is + * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_APPLICATION`, this + * function retrieves a remote QUIC transport parameters extension + * from an object obtained by `ngtcp2_conn_get_tls_native_handle` and + * sets it to |conn| by calling + * `ngtcp2_conn_decode_remote_transport_params`. * * This function returns 0 if it succeeds, or -1. */ @@ -412,20 +393,30 @@ NGTCP2_EXTERN int ngtcp2_crypto_derive_and_install_rx_key( * |secretlen| specifies the length of |secret|. * * The length of packet protection key and header protection key is - * ngtcp2_crypto_aead(ctx->aead), and the length of packet protection - * IV is ngtcp2_crypto_packet_protection_ivlen(ctx->aead) where ctx - * can be obtained by `ngtcp2_crypto_ctx_tls`. + * `ngtcp2_crypto_aead_keylen(ctx->aead) `, + * and the length of packet protection IV is + * `ngtcp2_crypto_packet_protection_ivlen(ctx->aead) + * ` where ctx is obtained by + * `ngtcp2_crypto_ctx_tls` (or `ngtcp2_crypto_ctx_tls_early` if + * |level| == :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`). * * In the first call of this function, it calls - * `ngtcp2_conn_set_crypto_ctx` to set negotiated AEAD and message - * digest algorithm. After the successful call of this function, - * application can use `ngtcp2_conn_get_crypto_ctx` to get the object. - * It also calls `ngtcp2_conn_set_aead_overhead` to set AEAD tag - * length. - * - * If |level| is NGTCP2_CRYPTO_LEVEL_APP, this function retrieves a - * remote QUIC transport parameters extension from |tls| and sets it - * to |conn|. + * `ngtcp2_conn_set_crypto_ctx` (or `ngtcp2_conn_set_early_crypto_ctx` + * if |level| == + * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to set + * negotiated AEAD and message digest algorithm. After the successful + * call of this function, application can use + * `ngtcp2_conn_get_crypto_ctx` (or `ngtcp2_conn_get_early_crypto_ctx` + * if |level| == + * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to get + * :type:`ngtcp2_crypto_ctx`. + * + * If |conn| is initialized as server, and |level| is + * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_APPLICATION`, this + * function retrieves a remote QUIC transport parameters extension + * from an object obtained by `ngtcp2_conn_get_tls_native_handle` and + * sets it to |conn| by calling + * `ngtcp2_conn_decode_remote_transport_params`. * * This function returns 0 if it succeeds, or -1. */ @@ -461,9 +452,11 @@ NGTCP2_EXTERN int ngtcp2_crypto_derive_and_install_tx_key( * length of |rx_secret| and |tx_secret|. * * The length of packet protection key and header protection key is - * ngtcp2_crypto_aead(ctx->aead), and the length of packet protection - * IV is ngtcp2_crypto_packet_protection_ivlen(ctx->aead) where ctx - * can be obtained by `ngtcp2_conn_get_crypto_ctx`. + * `ngtcp2_crypto_aead_keylen(ctx->aead) `, + * and the length of packet protection IV is + * `ngtcp2_crypto_packet_protection_ivlen(ctx->aead) + * ` where ctx is obtained by + * `ngtcp2_crypto_ctx_tls`. * * This function returns 0 if it succeeds, or -1. */ @@ -479,7 +472,7 @@ NGTCP2_EXTERN int ngtcp2_crypto_update_key( * * `ngtcp2_crypto_update_key_cb` is a wrapper function around * `ngtcp2_crypto_update_key`. It can be directly passed to - * :member:`ngtcp2_conn_callbacks.update_key` field. + * :member:`ngtcp2_callbacks.update_key` field. * * This function returns 0 if it succeeds, or * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. @@ -498,8 +491,8 @@ NGTCP2_EXTERN int ngtcp2_crypto_update_key_cb( * encryption keys and sets QUIC transport parameters. * * This function can be directly passed to - * :member:`ngtcp2_conn_callbacks.client_initial` field. It is only - * used by client. + * :member:`ngtcp2_callbacks.client_initial` field. It is only used + * by client. * * This function returns 0 if it succeeds, or * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. @@ -514,7 +507,7 @@ NGTCP2_EXTERN int ngtcp2_crypto_client_initial_cb(ngtcp2_conn *conn, * response to incoming Retry packet. * * This function can be directly passed to - * :member:`ngtcp2_conn_callbacks.recv_retry` field. It is only used + * :member:`ngtcp2_callbacks.recv_retry` field. It is only used * by client. * * This function returns 0 if it succeeds, or @@ -532,7 +525,7 @@ NGTCP2_EXTERN int ngtcp2_crypto_recv_retry_cb(ngtcp2_conn *conn, * transport parameters. * * This function can be directly passed to - * :member:`ngtcp2_conn_callbacks.recv_client_initial` field. It is + * :member:`ngtcp2_callbacks.recv_client_initial` field. It is * only used by server. * * This function returns 0 if it succeeds, or @@ -549,8 +542,8 @@ NGTCP2_EXTERN int ngtcp2_crypto_recv_client_initial_cb(ngtcp2_conn *conn, * length |datalen| in encryption level |crypto_level| and may feed * outgoing CRYPTO data to |conn|. This function can drive handshake. * This function can be also used after handshake completes. It is - * allowed to call this function with datalen == 0. In this case, no - * additional read operation is done. + * allowed to call this function with |datalen| == 0. In this case, + * no additional read operation is done. * * This function returns 0 if it succeeds, or a negative error code. * The generic error code is -1 if a specific error code is not @@ -563,34 +556,185 @@ ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, const uint8_t *data, size_t datalen); +/** + * @function + * + * `ngtcp2_crypto_recv_crypto_data_cb` is a wrapper function around + * `ngtcp2_crypto_read_write_crypto_data`. It can be directly passed + * to :member:`ngtcp2_callbacks.recv_crypto_data` field. + * + * If this function is used, the TLS implementation specific error + * codes described in `ngtcp2_crypto_read_write_crypto_data` are + * treated as if it returns -1. Do not use this function if an + * application wishes to use the TLS implementation specific error + * codes. + */ +NGTCP2_EXTERN int ngtcp2_crypto_recv_crypto_data_cb( + ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, uint64_t offset, + const uint8_t *data, size_t datalen, void *user_data); + /** * @function * * `ngtcp2_crypto_generate_stateless_reset_token` generates a - * stateless reset token using HKDF extraction with |md| using the - * given |cid| and static key |secret| as input. The token will be - * written to the buffer pointed by |token| and it must have a - * capacity of at least :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` - * bytes. + * stateless reset token using HKDF extraction using the given |cid| + * and static key |secret| as input. The token will be written to + * the buffer pointed by |token| and it must have a capacity of at + * least :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` bytes. * * This function returns 0 if it succeeds, or -1. */ NGTCP2_EXTERN int ngtcp2_crypto_generate_stateless_reset_token( - uint8_t *token, const ngtcp2_crypto_md *md, const uint8_t *secret, - size_t secretlen, const ngtcp2_cid *cid); + uint8_t *token, const uint8_t *secret, size_t secretlen, + const ngtcp2_cid *cid); + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_TOKEN_RAND_DATALEN` is the length of random + * data added to a token generated by + * `ngtcp2_crypto_generate_retry_token` or + * `ngtcp2_crypto_generate_regular_token`. + */ +#define NGTCP2_CRYPTO_TOKEN_RAND_DATALEN 32 + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY` is the magic byte for + * Retry token generated by `ngtcp2_crypto_generate_retry_token`. + */ +#define NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY 0xb6 + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR` is the magic byte for a + * token generated by `ngtcp2_crypto_generate_regular_token`. + */ +#define NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR 0x36 + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN` is the maximum length of + * a token generated by `ngtcp2_crypto_generate_retry_token`. + */ +#define NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN \ + (/* magic = */ 1 + /* cid len = */ 1 + NGTCP2_MAX_CIDLEN + \ + sizeof(ngtcp2_tstamp) + /* aead tag = */ 16 + \ + NGTCP2_CRYPTO_TOKEN_RAND_DATALEN) + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN` is the maximum length + * of a token generated by `ngtcp2_crypto_generate_regular_token`. + */ +#define NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN \ + (/* magic = */ 1 + sizeof(ngtcp2_tstamp) + /* aead tag = */ 16 + \ + NGTCP2_CRYPTO_TOKEN_RAND_DATALEN) + +/** + * @function + * + * `ngtcp2_crypto_generate_retry_token` generates a token in the + * buffer pointed by |token| that is sent with Retry packet. The + * buffer pointed by |token| must have at least + * :macro:`NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN` bytes long. The + * successfully generated token starts with + * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY`. |secret| of length + * |secretlen| is an initial keying material to generate keys to + * encrypt the token. |version| is QUIC version. |remote_addr| of + * length |remote_addrlen| is an address of client. |retry_scid| is a + * Source Connection ID chosen by server and set in Retry packet. + * |odcid| is a Destination Connection ID in Initial packet sent by + * client. |ts| is the timestamp when the token is generated. + * + * This function returns the length of generated token if it succeeds, + * or -1. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_retry_token( + uint8_t *token, const uint8_t *secret, size_t secretlen, uint32_t version, + const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen, + const ngtcp2_cid *retry_scid, const ngtcp2_cid *odcid, ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_crypto_verify_retry_token` verifies Retry token stored in + * the buffer pointed by |token| of length |tokenlen|. |secret| of + * length |secretlen| is an initial keying material to generate keys + * to decrypt the token. |version| is QUIC version of the Initial + * packet that contains this token. |remote_addr| of length + * |remote_addrlen| is an address of client. |dcid| is a Destination + * Connection ID in Initial packet sent by client. |timeout| is the + * period during which the token is valid. |ts| is the current + * timestamp. When validation succeeds, the extracted Destination + * Connection ID (which is the Destination Connection ID in Initial + * packet sent by client that triggered Retry packet) is stored to the + * buffer pointed by |odcid|. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_verify_retry_token( + ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen, + const uint8_t *secret, size_t secretlen, uint32_t version, + const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen, + const ngtcp2_cid *dcid, ngtcp2_duration timeout, ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_crypto_generate_regular_token` generates a token in the + * buffer pointed by |token| that is sent with NEW_TOKEN frame. The + * buffer pointed by |token| must have at least + * :macro:`NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN` bytes long. The + * successfully generated token starts with + * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR`. |secret| of length + * |secretlen| is an initial keying material to generate keys to + * encrypt the token. |remote_addr| of length |remote_addrlen| is an + * address of client. |ts| is the timestamp when the token is + * generated. + * + * This function returns the length of generated token if it succeeds, + * or -1. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_regular_token( + uint8_t *token, const uint8_t *secret, size_t secretlen, + const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen, + ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_crypto_verify_regular_token` verifies a regular token + * stored in the buffer pointed by |token| of length |tokenlen|. + * |secret| of length |secretlen| is an initial keying material to + * generate keys to decrypt the token. |remote_addr| of length + * |remote_addrlen| is an address of client. |timeout| is the period + * during which the token is valid. |ts| is the current timestamp. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_verify_regular_token( + const uint8_t *token, size_t tokenlen, const uint8_t *secret, + size_t secretlen, const ngtcp2_sockaddr *remote_addr, + ngtcp2_socklen remote_addrlen, ngtcp2_duration timeout, ngtcp2_tstamp ts); /** * @function * * `ngtcp2_crypto_write_connection_close` writes Initial packet - * containing CONNECTION_CLOSE with the given |error_code| to the - * buffer pointed by |dest| of length |destlen|. This function is - * designed for server to close connection without committing the - * state when validating Retry token fails. This function must not be - * used by client. The |dcid| must be the Source Connection ID in - * Initial packet from client. The |scid| must be the Destination - * Connection ID in Initial packet from client. |scid| is used to - * derive initial keying materials. + * containing CONNECTION_CLOSE with the given |error_code| and the + * optional |reason| of length |reasonlen| to the buffer pointed by + * |dest| of length |destlen|. This function is designed for server + * to close connection without committing the state when validating + * Retry token fails. This function must not be used by client. The + * |dcid| must be the Source Connection ID in Initial packet from + * client. The |scid| must be the Destination Connection ID in + * Initial packet from client. |scid| is used to derive initial + * keying materials. * * This function wraps around `ngtcp2_pkt_write_connection_close` for * easier use. @@ -599,7 +743,8 @@ NGTCP2_EXTERN int ngtcp2_crypto_generate_stateless_reset_token( */ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_write_connection_close( uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, uint64_t error_code); + const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason, + size_t reasonlen); /** * @function @@ -664,7 +809,7 @@ ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx); * `ngtcp2_crypto_delete_crypto_aead_ctx_cb` deletes the given |aead_ctx|. * * This function can be directly passed to - * :member:`ngtcp2_conn_callbacks.delete_crypto_aead_ctx` field. + * :member:`ngtcp2_callbacks.delete_crypto_aead_ctx` field. */ NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_aead_ctx_cb( ngtcp2_conn *conn, ngtcp2_crypto_aead_ctx *aead_ctx, void *user_data); @@ -676,11 +821,71 @@ NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_aead_ctx_cb( * |cipher_ctx|. * * This function can be directly passed to - * :member:`ngtcp2_conn_callbacks.delete_crypto_cipher_ctx` field. + * :member:`ngtcp2_callbacks.delete_crypto_cipher_ctx` field. */ NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_cipher_ctx_cb( ngtcp2_conn *conn, ngtcp2_crypto_cipher_ctx *cipher_ctx, void *user_data); +/** + * @function + * + * `ngtcp2_crypto_get_path_challenge_data_cb` writes unpredictable + * sequence of :macro:`NGTCP2_PATH_CHALLENGE_DATALEN` bytes to |data| + * which is sent with PATH_CHALLENGE frame. + * + * This function can be directly passed to + * :member:`ngtcp2_callbacks.get_path_challenge_data` field. + */ +NGTCP2_EXTERN int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, + uint8_t *data, + void *user_data); + +/** + * @function + * + * `ngtcp2_crypto_version_negotiation_cb` installs Initial keys for + * |version| which is negotiated or being negotiated. |client_dcid| + * is the destination connection ID in first Initial packet of client. + * + * This function can be directly passed to + * :member:`ngtcp2_callbacks.version_negotiation` field. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_version_negotiation_cb(ngtcp2_conn *conn, uint32_t version, + const ngtcp2_cid *client_dcid, + void *user_data); + +typedef struct ngtcp2_crypto_conn_ref ngtcp2_crypto_conn_ref; + +/** + * @functypedef + * + * :type:`ngtcp2_crypto_get_conn` is a callback function to get a + * pointer to :type:`ngtcp2_conn` from |conn_ref|. The implementation + * must return non-NULL :type:`ngtcp2_conn` object. + */ +typedef ngtcp2_conn *(*ngtcp2_crypto_get_conn)( + ngtcp2_crypto_conn_ref *conn_ref); + +/** + * @struct + * + * :type:`ngtcp2_crypto_conn_ref` is a structure to get a pointer to + * :type:`ngtcp2_conn`. It is meant to be set to TLS native handle as + * an application specific data (e.g. SSL_set_app_data in OpenSSL). + */ +typedef struct ngtcp2_crypto_conn_ref { + /** + * :member:`get_conn` is a callback function to get a pointer to + * :type:`ngtcp2_conn` object. + */ + ngtcp2_crypto_get_conn get_conn; + /** + * :member:`user_data` is a pointer to arbitrary user data. + */ + void *user_data; +} ngtcp2_crypto_conn_ref; + #ifdef __cplusplus } #endif diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h index 65f8414249a0c5..6497c09e79840d 100644 --- a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h +++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h @@ -55,6 +55,48 @@ NGTCP2_EXTERN enum ssl_encryption_level_t ngtcp2_crypto_boringssl_from_ngtcp2_crypto_level( ngtcp2_crypto_level crypto_level); +/** + * @function + * + * `ngtcp2_crypto_boringssl_configure_server_context` configures + * |ssl_ctx| for server side QUIC connection. It performs the + * following modifications: + * + * - Set minimum and maximum TLS version to TLSv1.3. + * - Set SSL_QUIC_METHOD by calling SSL_CTX_set_quic_method. + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * SSL object by calling SSL_set_app_data, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_boringssl_configure_server_context(SSL_CTX *ssl_ctx); + +/** + * @function + * + * `ngtcp2_crypto_boringssl_configure_client_context` configures + * |ssl_ctx| for client side QUIC connection. It performs the + * following modifications: + * + * - Set minimum and maximum TLS version to TLSv1.3. + * - Set SSL_QUIC_METHOD by calling SSL_CTX_set_quic_method. + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * SSL object by calling SSL_set_app_data, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_boringssl_configure_client_context(SSL_CTX *ssl_ctx); + #ifdef __cplusplus } #endif diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h index 10a8e712cfd63c..844081bfa8b055 100644 --- a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h +++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h @@ -83,6 +83,48 @@ NGTCP2_EXTERN OSSL_ENCRYPTION_LEVEL ngtcp2_crypto_openssl_from_ngtcp2_crypto_level( ngtcp2_crypto_level crypto_level); +/** + * @function + * + * `ngtcp2_crypto_openssl_configure_server_context` configures + * |ssl_ctx| for server side QUIC connection. It performs the + * following modifications: + * + * - Set minimum and maximum TLS version to TLSv1.3. + * - Set SSL_QUIC_METHOD by calling SSL_CTX_set_quic_method. + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * SSL object by calling SSL_set_app_data, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_openssl_configure_server_context(SSL_CTX *ssl_ctx); + +/** + * @function + * + * `ngtcp2_crypto_openssl_configure_client_context` configures + * |ssl_ctx| for client side QUIC connection. It performs the + * following modifications: + * + * - Set minimum and maximum TLS version to TLSv1.3. + * - Set SSL_QUIC_METHOD by calling SSL_CTX_set_quic_method. + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * SSL object by calling SSL_set_app_data, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_openssl_configure_client_context(SSL_CTX *ssl_ctx); + #ifdef __cplusplus } #endif diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h new file mode 100644 index 00000000000000..d4b551c382fd69 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h @@ -0,0 +1,246 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_CRYPTO_PICOTLS_H +#define NGTCP2_CRYPTO_PICOTLS_H + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @struct + * + * :type:`ngtcp2_crypto_picotls_ctx` contains per-connection state + * of Picotls objects and must be an object to bet set to + * `ngtcp2_conn_set_tls_native_handle`. + */ +typedef struct ngtcp2_crypto_picotls_ctx { + /** + * :member:`ptls` is a pointer to ptls_t object. + */ + ptls_t *ptls; + /** + * :member:`handshake_properties` is a set of configurations used + * during this particular TLS handshake. + */ + ptls_handshake_properties_t handshake_properties; +} ngtcp2_crypto_picotls_ctx; + +/** + * @function + * + * `ngtcp2_crypto_picotls_ctx_init` initializes the object pointed by + * |cptls|. |cptls| must not be NULL. + */ +NGTCP2_EXTERN void +ngtcp2_crypto_picotls_ctx_init(ngtcp2_crypto_picotls_ctx *cptls); + +/** + * @function + * + * `ngtcp2_crypto_picotls_from_epoch` translates |epoch| to + * :type:`ngtcp2_crypto_level`. This function is only available for + * Picotls backend. + */ +NGTCP2_EXTERN ngtcp2_crypto_level +ngtcp2_crypto_picotls_from_epoch(size_t epoch); + +/** + * @function + * + * `ngtcp2_crypto_picotls_from_ngtcp2_crypto_level` translates + * |crypto_level| to epoch. This function is only available for + * Picotls backend. + */ +NGTCP2_EXTERN size_t ngtcp2_crypto_picotls_from_ngtcp2_crypto_level( + ngtcp2_crypto_level crypto_level); + +/** + * @function + * + * `ngtcp2_crypto_picotls_configure_server_context` configures |ctx| + * for server side QUIC connection. It performs the following + * modifications: + * + * - Set max_early_data_size to UINT32_MAX. + * - Set omit_end_of_early_data to 1. + * - Set update_traffic_key callback. + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * ptls_t object by assigning the pointer using ptls_get_data_ptr, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_picotls_configure_server_context(ptls_context_t *ctx); + +/** + * @function + * + * `ngtcp2_crypto_picotls_configure_client_context` configures |ctx| + * for client side QUIC connection. It performs the following + * modifications: + * + * - Set omit_end_of_early_data to 1. + * - Set update_traffic_key callback. + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * ptls_t object by assigning the pointer using ptls_get_data_ptr, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_picotls_configure_client_context(ptls_context_t *ctx); + +/** + * @function + * + * `ngtcp2_crypto_picotls_configure_server_session` configures |cptls| + * for server side QUIC connection. It performs the following + * modifications: + * + * - Set handshake_properties.collect_extension to + * `ngtcp2_crypto_picotls_collect_extension`. + * - Set handshake_properties.collected_extensions to + * `ngtcp2_crypto_picotls_collected_extensions`. + * + * The callbacks set by this function only handle QUIC Transport + * Parameters TLS extension. If an application needs to handle the + * other TLS extensions, set its own callbacks and call + * `ngtcp2_crypto_picotls_collect_extension` and + * `ngtcp2_crypto_picotls_collected_extensions` form them. + * + * During the QUIC handshake, the first element of + * handshake_properties.additional_extensions is assigned to send QUIC + * Transport Parameter TLS extension. Therefore, an application must + * allocate at least 2 elements for + * handshake_properties.additional_extensions. + * + * Call `ngtcp2_crypto_picotls_deconfigure_session` to free up the + * resources. + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * ptls_t object by assigning the pointer using ptls_get_data_ptr, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_picotls_configure_server_session( + ngtcp2_crypto_picotls_ctx *cptls); + +/** + * @function + * + * `ngtcp2_crypto_picotls_configure_client_session` configures |cptls| + * for client side QUIC connection. It performs the following + * modifications: + * + * - Set handshake_properties.max_early_data_size to a pointer to + * uint32_t, which is allocated dynamically by this function. + * - Set handshake_properties.collect_extension to + * `ngtcp2_crypto_picotls_collect_extension`. + * - Set handshake_properties.collected_extensions to + * `ngtcp2_crypto_picotls_collected_extensions`. + * - Set handshake_properties.additional_extensions[0].data to the + * dynamically allocated buffer which contains QUIC Transport + * Parameters TLS extension. An application must allocate at least + * 2 elements for handshake_properties.additional_extensions. + * + * The callbacks set by this function only handle QUIC Transport + * Parameters TLS extension. If an application needs to handle the + * other TLS extensions, set its own callbacks and call + * `ngtcp2_crypto_picotls_collect_extension` and + * `ngtcp2_crypto_picotls_collected_extensions` form them. + * + * Call `ngtcp2_crypto_picotls_deconfigure_session` to free up the + * resources. + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * ptls_t object by assigning the pointer using ptls_get_data_ptr, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_picotls_configure_client_session(ngtcp2_crypto_picotls_ctx *cptls, + ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_crypto_picotls_deconfigure_session` frees the resources + * allocated for |cptls| during QUIC connection. It frees the + * following data using :manpage:`free(3)`. + * + * - handshake_properties.max_early_data_size + * - handshake_properties.additional_extensions[0].data.base + * + * If |cptls| is NULL, this function does nothing. + */ +NGTCP2_EXTERN void +ngtcp2_crypto_picotls_deconfigure_session(ngtcp2_crypto_picotls_ctx *cptls); + +/** + * @function + * + * `ngtcp2_crypto_picotls_collect_extension` is a callback function + * which only returns nonzero if |type| == + * :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1`. + */ +NGTCP2_EXTERN int ngtcp2_crypto_picotls_collect_extension( + ptls_t *ptls, struct st_ptls_handshake_properties_t *properties, + uint16_t type); + +/** + * @function + * + * `ngtcp2_crypto_picotls_collected_extensions` is a callback function + * which only handles the extension of type + * :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1`. The other + * extensions are ignored. + */ +NGTCP2_EXTERN int ngtcp2_crypto_picotls_collected_extensions( + ptls_t *ptls, struct st_ptls_handshake_properties_t *properties, + ptls_raw_extension_t *extensions); + +#ifdef __cplusplus +} +#endif + +#endif /* NGTCP2_CRYPTO_PICOTLS_H */ diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_wolfssl.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_wolfssl.h new file mode 100644 index 00000000000000..3b10802c25b5e8 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_wolfssl.h @@ -0,0 +1,106 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_CRYPTO_WOLFSSL_H +#define NGTCP2_CRYPTO_WOLFSSL_H + +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @function + * + * `ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level` translates + * |wolfssl_level| to :type:`ngtcp2_crypto_level`. This function is only + * available for wolfSSL backend. + */ +NGTCP2_EXTERN ngtcp2_crypto_level +ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level( + WOLFSSL_ENCRYPTION_LEVEL wolfssl_level); + +/** + * @function + * + * `ngtcp2_crypto_wolfssl_from_ngtcp2_crypto_level` translates + * |crypto_level| to WOLFSSL_ENCRYPTION_LEVEL. This function is only + * available for wolfSSL backend. + */ +NGTCP2_EXTERN WOLFSSL_ENCRYPTION_LEVEL +ngtcp2_crypto_wolfssl_from_ngtcp2_crypto_level( + ngtcp2_crypto_level crypto_level); + +/** + * @function + * + * `ngtcp2_crypto_wolfssl_configure_server_context` configures + * |ssl_ctx| for server side QUIC connection. It performs the + * following modifications: + * + * - Set minimum and maximum TLS version to TLSv1.3. + * - Set WOLFSSL_QUIC_METHOD by calling wolfSSL_CTX_set_quic_method. + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * WOLFSSL object by calling wolfSSL_set_app_data, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_wolfssl_configure_server_context(WOLFSSL_CTX *ssl_ctx); + +/** + * @function + * + * `ngtcp2_crypto_wolfssl_configure_client_context` configures + * |ssl_ctx| for client side QUIC connection. It performs the + * following modifications: + * + * - Set minimum and maximum TLS version to TLSv1.3. + * - Set WOLFSSL_QUIC_METHOD by calling wolfSSL_CTX_set_quic_method. + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * SSL object by calling wolfSSL_set_app_data, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_wolfssl_configure_client_context(WOLFSSL_CTX *ssl_ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* NGTCP2_CRYPTO_WOLFSSL_H */ diff --git a/deps/ngtcp2/ngtcp2/crypto/openssl/openssl.c b/deps/ngtcp2/ngtcp2/crypto/openssl/openssl.c index d4b9e57f27581e..466d9e11ca6415 100644 --- a/deps/ngtcp2/ngtcp2/crypto/openssl/openssl.c +++ b/deps/ngtcp2/ngtcp2/crypto/openssl/openssl.c @@ -34,6 +34,11 @@ #include #include #include +#include + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +# include +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ #include "shared.h" @@ -48,9 +53,19 @@ static size_t crypto_aead_max_overhead(const EVP_CIPHER *aead) { return EVP_CCM_TLS_TAG_LEN; default: assert(0); + abort(); /* if NDEBUG is set */ } } +ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) { + return ngtcp2_crypto_aead_init(aead, (void *)EVP_aes_128_gcm()); +} + +ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) { + md->native_handle = (void *)EVP_sha256(); + return md; +} + ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) { ngtcp2_crypto_aead_init(&ctx->aead, (void *)EVP_aes_128_gcm()); ctx->md.native_handle = (void *)EVP_sha256(); @@ -187,18 +202,37 @@ int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, const EVP_CIPHER *cipher = aead->native_handle; int cipher_nid = EVP_CIPHER_nid(cipher); EVP_CIPHER_CTX *actx; + size_t taglen = crypto_aead_max_overhead(cipher); +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + OSSL_PARAM params[3]; +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ actx = EVP_CIPHER_CTX_new(); if (actx == NULL) { return -1; } +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_IVLEN, &noncelen); + + if (cipher_nid == NID_aes_128_ccm) { + params[1] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + NULL, taglen); + params[2] = OSSL_PARAM_construct_end(); + } else { + params[1] = OSSL_PARAM_construct_end(); + } +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + if (!EVP_EncryptInit_ex(actx, cipher, NULL, NULL, NULL) || +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + !EVP_CIPHER_CTX_set_params(actx, params) || +#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_IVLEN, (int)noncelen, NULL) || (cipher_nid == NID_aes_128_ccm && - !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, - (int)crypto_aead_max_overhead(cipher), NULL)) || + !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen, NULL)) || +#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ !EVP_EncryptInit_ex(actx, NULL, NULL, key, NULL)) { EVP_CIPHER_CTX_free(actx); return -1; @@ -215,18 +249,37 @@ int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, const EVP_CIPHER *cipher = aead->native_handle; int cipher_nid = EVP_CIPHER_nid(cipher); EVP_CIPHER_CTX *actx; + size_t taglen = crypto_aead_max_overhead(cipher); +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + OSSL_PARAM params[3]; +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ actx = EVP_CIPHER_CTX_new(); if (actx == NULL) { return -1; } +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_IVLEN, &noncelen); + + if (cipher_nid == NID_aes_128_ccm) { + params[1] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + NULL, taglen); + params[2] = OSSL_PARAM_construct_end(); + } else { + params[1] = OSSL_PARAM_construct_end(); + } +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + if (!EVP_DecryptInit_ex(actx, cipher, NULL, NULL, NULL) || +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + !EVP_CIPHER_CTX_set_params(actx, params) || +#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_IVLEN, (int)noncelen, NULL) || (cipher_nid == NID_aes_128_ccm && - !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, - (int)crypto_aead_max_overhead(cipher), NULL)) || + !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen, NULL)) || +#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ !EVP_DecryptInit_ex(actx, NULL, NULL, key, NULL)) { EVP_CIPHER_CTX_free(actx); return -1; @@ -272,6 +325,33 @@ void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) { int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen, const uint8_t *salt, size_t saltlen) { +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + const EVP_MD *prf = md->native_handle; + EVP_KDF *kdf = EVP_KDF_fetch(NULL, "hkdf", NULL); + EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf); + int mode = EVP_KDF_HKDF_MODE_EXTRACT_ONLY; + OSSL_PARAM params[] = { + OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode), + OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, + (char *)EVP_MD_get0_name(prf), 0), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret, + secretlen), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, (void *)salt, + saltlen), + OSSL_PARAM_construct_end(), + }; + int rv = 0; + + EVP_KDF_free(kdf); + + if (EVP_KDF_derive(kctx, dest, (size_t)EVP_MD_size(prf), params) <= 0) { + rv = -1; + } + + EVP_KDF_CTX_free(kctx); + + return rv; +#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ const EVP_MD *prf = md->native_handle; int rv = 0; EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); @@ -293,12 +373,40 @@ int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md, EVP_PKEY_CTX_free(pctx); return rv; +#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ } int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen, const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen, const uint8_t *info, size_t infolen) { +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + const EVP_MD *prf = md->native_handle; + EVP_KDF *kdf = EVP_KDF_fetch(NULL, "hkdf", NULL); + EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf); + int mode = EVP_KDF_HKDF_MODE_EXPAND_ONLY; + OSSL_PARAM params[] = { + OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode), + OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, + (char *)EVP_MD_get0_name(prf), 0), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret, + secretlen), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, (void *)info, + infolen), + OSSL_PARAM_construct_end(), + }; + int rv = 0; + + EVP_KDF_free(kdf); + + if (EVP_KDF_derive(kctx, dest, destlen, params) <= 0) { + rv = -1; + } + + EVP_KDF_CTX_free(kctx); + + return rv; +#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ const EVP_MD *prf = md->native_handle; int rv = 0; EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); @@ -309,7 +417,7 @@ int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen, if (EVP_PKEY_derive_init(pctx) != 1 || EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) != 1 || EVP_PKEY_CTX_set_hkdf_md(pctx, prf) != 1 || - EVP_PKEY_CTX_set1_hkdf_salt(pctx, "", 0) != 1 || + EVP_PKEY_CTX_set1_hkdf_salt(pctx, (const unsigned char *)"", 0) != 1 || EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, (int)secretlen) != 1 || EVP_PKEY_CTX_add1_hkdf_info(pctx, info, (int)infolen) != 1 || EVP_PKEY_derive(pctx, dest, &destlen) != 1) { @@ -319,29 +427,97 @@ int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen, EVP_PKEY_CTX_free(pctx); return rv; +#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ +} + +int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, const uint8_t *secret, + size_t secretlen, const uint8_t *salt, size_t saltlen, + const uint8_t *info, size_t infolen) { +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + const EVP_MD *prf = md->native_handle; + EVP_KDF *kdf = EVP_KDF_fetch(NULL, "hkdf", NULL); + EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf); + OSSL_PARAM params[] = { + OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, + (char *)EVP_MD_get0_name(prf), 0), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret, + secretlen), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, (void *)salt, + saltlen), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, (void *)info, + infolen), + OSSL_PARAM_construct_end(), + }; + int rv = 0; + + EVP_KDF_free(kdf); + + if (EVP_KDF_derive(kctx, dest, destlen, params) <= 0) { + rv = -1; + } + + EVP_KDF_CTX_free(kctx); + + return rv; +#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ + const EVP_MD *prf = md->native_handle; + int rv = 0; + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + if (pctx == NULL) { + return -1; + } + + if (EVP_PKEY_derive_init(pctx) != 1 || + EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND) != + 1 || + EVP_PKEY_CTX_set_hkdf_md(pctx, prf) != 1 || + EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, (int)saltlen) != 1 || + EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, (int)secretlen) != 1 || + EVP_PKEY_CTX_add1_hkdf_info(pctx, info, (int)infolen) != 1 || + EVP_PKEY_derive(pctx, dest, &destlen) != 1) { + rv = -1; + } + + EVP_PKEY_CTX_free(pctx); + + return rv; +#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ } int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *plaintext, size_t plaintextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen) { + const uint8_t *aad, size_t aadlen) { const EVP_CIPHER *cipher = aead->native_handle; size_t taglen = crypto_aead_max_overhead(cipher); int cipher_nid = EVP_CIPHER_nid(cipher); EVP_CIPHER_CTX *actx = aead_ctx->native_handle; int len; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + OSSL_PARAM params[] = { + OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + dest + plaintextlen, taglen), + OSSL_PARAM_construct_end(), + }; +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ (void)noncelen; if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, nonce) || (cipher_nid == NID_aes_128_ccm && !EVP_EncryptUpdate(actx, NULL, &len, NULL, (int)plaintextlen)) || - !EVP_EncryptUpdate(actx, NULL, &len, ad, (int)adlen) || + !EVP_EncryptUpdate(actx, NULL, &len, aad, (int)aadlen) || !EVP_EncryptUpdate(actx, dest, &len, plaintext, (int)plaintextlen) || !EVP_EncryptFinal_ex(actx, dest + len, &len) || +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + !EVP_CIPHER_CTX_get_params(actx, params) +#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_GET_TAG, (int)taglen, - dest + plaintextlen)) { + dest + plaintextlen) +#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ + ) { return -1; } @@ -352,13 +528,16 @@ int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *ciphertext, size_t ciphertextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen) { + const uint8_t *aad, size_t aadlen) { const EVP_CIPHER *cipher = aead->native_handle; size_t taglen = crypto_aead_max_overhead(cipher); int cipher_nid = EVP_CIPHER_nid(cipher); EVP_CIPHER_CTX *actx = aead_ctx->native_handle; int len; const uint8_t *tag; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + OSSL_PARAM params[2]; +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ (void)noncelen; @@ -369,12 +548,22 @@ int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, ciphertextlen -= taglen; tag = ciphertext + ciphertextlen; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + (void *)tag, taglen); + params[1] = OSSL_PARAM_construct_end(); +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + if (!EVP_DecryptInit_ex(actx, NULL, NULL, NULL, nonce) || +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + !EVP_CIPHER_CTX_set_params(actx, params) || +#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen, (uint8_t *)tag) || +#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ (cipher_nid == NID_aes_128_ccm && !EVP_DecryptUpdate(actx, NULL, &len, NULL, (int)ciphertextlen)) || - !EVP_DecryptUpdate(actx, NULL, &len, ad, (int)adlen) || + !EVP_DecryptUpdate(actx, NULL, &len, aad, (int)aadlen) || !EVP_DecryptUpdate(actx, dest, &len, ciphertext, (int)ciphertextlen) || (cipher_nid != NID_aes_128_ccm && !EVP_DecryptFinal_ex(actx, dest + ciphertextlen, &len))) { @@ -457,24 +646,13 @@ int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn, int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) { SSL *ssl = tls; - ngtcp2_transport_params_type exttype = - ngtcp2_conn_is_server(conn) - ? NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO - : NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS; const uint8_t *tp; size_t tplen; - ngtcp2_transport_params params; int rv; SSL_get_peer_quic_transport_params(ssl, &tp, &tplen); - rv = ngtcp2_decode_transport_params(¶ms, exttype, tp, tplen); - if (rv != 0) { - ngtcp2_conn_set_tls_error(conn, rv); - return -1; - } - - rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms); + rv = ngtcp2_conn_decode_remote_transport_params(conn, tp, tplen); if (rv != 0) { ngtcp2_conn_set_tls_error(conn, rv); return -1; @@ -505,6 +683,7 @@ ngtcp2_crypto_level ngtcp2_crypto_openssl_from_ossl_encryption_level( return NGTCP2_CRYPTO_LEVEL_APPLICATION; default: assert(0); + abort(); /* if NDEBUG is set */ } } @@ -522,5 +701,107 @@ ngtcp2_crypto_openssl_from_ngtcp2_crypto_level( return ssl_encryption_early_data; default: assert(0); + abort(); /* if NDEBUG is set */ } } + +int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data, + void *user_data) { + (void)conn; + (void)user_data; + + if (RAND_bytes(data, NGTCP2_PATH_CHALLENGE_DATALEN) != 1) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +int ngtcp2_crypto_random(uint8_t *data, size_t datalen) { + if (RAND_bytes(data, (int)datalen) != 1) { + return -1; + } + + return 0; +} + +static int set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level, + const uint8_t *rx_secret, + const uint8_t *tx_secret, size_t secretlen) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + ngtcp2_crypto_level level = + ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level); + + if (rx_secret && + ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level, + rx_secret, secretlen) != 0) { + return 0; + } + + if (tx_secret && + ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level, + tx_secret, secretlen) != 0) { + return 0; + } + + return 1; +} + +static int add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level, + const uint8_t *data, size_t datalen) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + ngtcp2_crypto_level level = + ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level); + int rv; + + rv = ngtcp2_conn_submit_crypto_data(conn, level, data, datalen); + if (rv != 0) { + ngtcp2_conn_set_tls_error(conn, rv); + return 0; + } + + return 1; +} + +static int flush_flight(SSL *ssl) { + (void)ssl; + return 1; +} + +static int send_alert(SSL *ssl, enum ssl_encryption_level_t level, + uint8_t alert) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + (void)level; + + ngtcp2_conn_set_tls_alert(conn, alert); + + return 1; +} + +static SSL_QUIC_METHOD quic_method = { + set_encryption_secrets, + add_handshake_data, + flush_flight, + send_alert, +}; + +static void crypto_openssl_configure_context(SSL_CTX *ssl_ctx) { + SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION); + SSL_CTX_set_quic_method(ssl_ctx, &quic_method); +} + +int ngtcp2_crypto_openssl_configure_server_context(SSL_CTX *ssl_ctx) { + crypto_openssl_configure_context(ssl_ctx); + + return 0; +} + +int ngtcp2_crypto_openssl_configure_client_context(SSL_CTX *ssl_ctx) { + crypto_openssl_configure_context(ssl_ctx); + + return 0; +} diff --git a/deps/ngtcp2/ngtcp2/crypto/picotls/picotls.c b/deps/ngtcp2/ngtcp2/crypto/picotls/picotls.c new file mode 100644 index 00000000000000..32d17adc6c3a35 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/crypto/picotls/picotls.c @@ -0,0 +1,697 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include +#include + +#include +#include + +#include +#include + +#include "shared.h" + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) { + return ngtcp2_crypto_aead_init(aead, (void *)&ptls_openssl_aes128gcm); +} + +ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) { + md->native_handle = (void *)&ptls_openssl_sha256; + return md; +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) { + ngtcp2_crypto_aead_init(&ctx->aead, (void *)&ptls_openssl_aes128gcm); + ctx->md.native_handle = (void *)&ptls_openssl_sha256; + ctx->hp.native_handle = (void *)&ptls_openssl_aes128ctr; + ctx->max_encryption = 0; + ctx->max_decryption_failure = 0; + return ctx; +} + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead, + void *aead_native_handle) { + ptls_aead_algorithm_t *alg = aead_native_handle; + + aead->native_handle = aead_native_handle; + aead->max_overhead = alg->tag_size; + return aead; +} + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) { + return ngtcp2_crypto_aead_init(aead, (void *)&ptls_openssl_aes128gcm); +} + +static const ptls_aead_algorithm_t *crypto_ptls_get_aead(ptls_t *ptls) { + ptls_cipher_suite_t *cs = ptls_get_cipher(ptls); + + return cs->aead; +} + +static uint64_t crypto_ptls_get_aead_max_encryption(ptls_t *ptls) { + ptls_cipher_suite_t *cs = ptls_get_cipher(ptls); + + if (cs->aead == &ptls_openssl_aes128gcm || + cs->aead == &ptls_openssl_aes256gcm) { + return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM; + } + + if (cs->aead == &ptls_openssl_chacha20poly1305) { + return NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305; + } + + return 0; +} + +static uint64_t crypto_ptls_get_aead_max_decryption_failure(ptls_t *ptls) { + ptls_cipher_suite_t *cs = ptls_get_cipher(ptls); + + if (cs->aead == &ptls_openssl_aes128gcm || + cs->aead == &ptls_openssl_aes256gcm) { + return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM; + } + + if (cs->aead == &ptls_openssl_chacha20poly1305) { + return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305; + } + + return 0; +} + +static const ptls_cipher_algorithm_t *crypto_ptls_get_hp(ptls_t *ptls) { + ptls_cipher_suite_t *cs = ptls_get_cipher(ptls); + + if (cs->aead == &ptls_openssl_aes128gcm) { + return &ptls_openssl_aes128ctr; + } + + if (cs->aead == &ptls_openssl_aes256gcm) { + return &ptls_openssl_aes256ctr; + } + + if (cs->aead == &ptls_openssl_chacha20poly1305) { + return &ptls_openssl_chacha20; + } + + return NULL; +} + +static const ptls_hash_algorithm_t *crypto_ptls_get_md(ptls_t *ptls) { + ptls_cipher_suite_t *cs = ptls_get_cipher(ptls); + + return cs->hash; +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx, + void *tls_native_handle) { + ngtcp2_crypto_picotls_ctx *cptls = tls_native_handle; + ngtcp2_crypto_aead_init(&ctx->aead, + (void *)crypto_ptls_get_aead(cptls->ptls)); + ctx->md.native_handle = (void *)crypto_ptls_get_md(cptls->ptls); + ctx->hp.native_handle = (void *)crypto_ptls_get_hp(cptls->ptls); + ctx->max_encryption = crypto_ptls_get_aead_max_encryption(cptls->ptls); + ctx->max_decryption_failure = + crypto_ptls_get_aead_max_decryption_failure(cptls->ptls); + return ctx; +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx, + void *tls_native_handle) { + return ngtcp2_crypto_ctx_tls(ctx, tls_native_handle); +} + +static size_t crypto_md_hashlen(const ptls_hash_algorithm_t *md) { + return md->digest_size; +} + +size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md) { + return crypto_md_hashlen(md->native_handle); +} + +static size_t crypto_aead_keylen(const ptls_aead_algorithm_t *aead) { + return aead->key_size; +} + +size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead) { + return crypto_aead_keylen(aead->native_handle); +} + +static size_t crypto_aead_noncelen(const ptls_aead_algorithm_t *aead) { + return aead->iv_size; +} + +size_t ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead) { + return crypto_aead_noncelen(aead->native_handle); +} + +int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, + const ngtcp2_crypto_aead *aead, + const uint8_t *key, size_t noncelen) { + const ptls_aead_algorithm_t *cipher = aead->native_handle; + size_t keylen = crypto_aead_keylen(cipher); + ptls_aead_context_t *actx; + static const uint8_t iv[PTLS_MAX_IV_SIZE] = {0}; + + (void)noncelen; + (void)keylen; + + actx = ptls_aead_new_direct(cipher, /* is_enc = */ 1, key, iv); + if (actx == NULL) { + return -1; + } + + aead_ctx->native_handle = actx; + + return 0; +} + +int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, + const ngtcp2_crypto_aead *aead, + const uint8_t *key, size_t noncelen) { + const ptls_aead_algorithm_t *cipher = aead->native_handle; + size_t keylen = crypto_aead_keylen(cipher); + ptls_aead_context_t *actx; + const uint8_t iv[PTLS_MAX_IV_SIZE] = {0}; + + (void)noncelen; + (void)keylen; + + actx = ptls_aead_new_direct(cipher, /* is_enc = */ 0, key, iv); + if (actx == NULL) { + return -1; + } + + aead_ctx->native_handle = actx; + + return 0; +} + +void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) { + if (aead_ctx->native_handle) { + ptls_aead_free(aead_ctx->native_handle); + } +} + +int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx, + const ngtcp2_crypto_cipher *cipher, + const uint8_t *key) { + ptls_cipher_context_t *actx; + + actx = ptls_cipher_new(cipher->native_handle, /* is_enc = */ 1, key); + if (actx == NULL) { + return -1; + } + + cipher_ctx->native_handle = actx; + + return 0; +} + +void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) { + if (cipher_ctx->native_handle) { + ptls_cipher_free(cipher_ctx->native_handle); + } +} + +int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md, + const uint8_t *secret, size_t secretlen, + const uint8_t *salt, size_t saltlen) { + ptls_iovec_t saltv, ikm; + + saltv = ptls_iovec_init(salt, saltlen); + ikm = ptls_iovec_init(secret, secretlen); + + if (ptls_hkdf_extract(md->native_handle, dest, saltv, ikm) != 0) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, const uint8_t *secret, + size_t secretlen, const uint8_t *info, + size_t infolen) { + ptls_iovec_t prk, infov; + + prk = ptls_iovec_init(secret, secretlen); + infov = ptls_iovec_init(info, infolen); + + if (ptls_hkdf_expand(md->native_handle, dest, destlen, prk, infov) != 0) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, const uint8_t *secret, + size_t secretlen, const uint8_t *salt, size_t saltlen, + const uint8_t *info, size_t infolen) { + ptls_iovec_t saltv, ikm, prk, infov; + uint8_t prkbuf[PTLS_MAX_DIGEST_SIZE]; + ptls_hash_algorithm_t *algo = md->native_handle; + + saltv = ptls_iovec_init(salt, saltlen); + ikm = ptls_iovec_init(secret, secretlen); + + if (ptls_hkdf_extract(algo, prkbuf, saltv, ikm) != 0) { + return -1; + } + + prk = ptls_iovec_init(prkbuf, algo->digest_size); + infov = ptls_iovec_init(info, infolen); + + if (ptls_hkdf_expand(algo, dest, destlen, prk, infov) != 0) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *aad, size_t aadlen) { + ptls_aead_context_t *actx = aead_ctx->native_handle; + + (void)aead; + + ptls_aead_xor_iv(actx, nonce, noncelen); + + ptls_aead_encrypt(actx, dest, plaintext, plaintextlen, 0, aad, aadlen); + + /* zero-out static iv once again */ + ptls_aead_xor_iv(actx, nonce, noncelen); + + return 0; +} + +int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *aad, size_t aadlen) { + ptls_aead_context_t *actx = aead_ctx->native_handle; + + (void)aead; + + ptls_aead_xor_iv(actx, nonce, noncelen); + + if (ptls_aead_decrypt(actx, dest, ciphertext, ciphertextlen, 0, aad, + aadlen) == SIZE_MAX) { + return -1; + } + + /* zero-out static iv once again */ + ptls_aead_xor_iv(actx, nonce, noncelen); + + return 0; +} + +int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp, + const ngtcp2_crypto_cipher_ctx *hp_ctx, + const uint8_t *sample) { + ptls_cipher_context_t *actx = hp_ctx->native_handle; + static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00"; + + (void)hp; + + ptls_cipher_init(actx, sample); + ptls_cipher_encrypt(actx, dest, PLAINTEXT, sizeof(PLAINTEXT) - 1); + + return 0; +} + +int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + const uint8_t *data, size_t datalen) { + ngtcp2_crypto_picotls_ctx *cptls = ngtcp2_conn_get_tls_native_handle(conn); + ptls_buffer_t sendbuf; + size_t epoch_offsets[5] = {0}; + size_t epoch = ngtcp2_crypto_picotls_from_ngtcp2_crypto_level(crypto_level); + size_t epoch_datalen; + size_t i; + int rv; + + ptls_buffer_init(&sendbuf, (void *)"", 0); + + assert(epoch == ptls_get_read_epoch(cptls->ptls)); + + rv = ptls_handle_message(cptls->ptls, &sendbuf, epoch_offsets, epoch, data, + datalen, &cptls->handshake_properties); + if (rv != 0 && rv != PTLS_ERROR_IN_PROGRESS) { + if (PTLS_ERROR_GET_CLASS(rv) == PTLS_ERROR_CLASS_SELF_ALERT) { + ngtcp2_conn_set_tls_alert(conn, (uint8_t)PTLS_ERROR_TO_ALERT(rv)); + } + + rv = -1; + goto fin; + } + + if (!ngtcp2_conn_is_server(conn) && + cptls->handshake_properties.client.early_data_acceptance == + PTLS_EARLY_DATA_REJECTED) { + ngtcp2_conn_early_data_rejected(conn); + } + + for (i = 0; i < 4; ++i) { + epoch_datalen = epoch_offsets[i + 1] - epoch_offsets[i]; + if (epoch_datalen == 0) { + continue; + } + + assert(i != 1); + + if (ngtcp2_conn_submit_crypto_data( + conn, ngtcp2_crypto_picotls_from_epoch(i), + sendbuf.base + epoch_offsets[i], epoch_datalen) != 0) { + rv = -1; + goto fin; + } + } + + if (rv == 0) { + ngtcp2_conn_handshake_completed(conn); + } + + rv = 0; + +fin: + ptls_buffer_dispose(&sendbuf); + + return rv; +} + +int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) { + (void)conn; + (void)tls; + + /* The remote transport parameters will be set via picotls + collected_extensions callback */ + + return 0; +} + +int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf, + size_t len) { + (void)tls; + (void)buf; + (void)len; + + /* The local transport parameters will be set in an external + call. */ + + return 0; +} + +ngtcp2_crypto_level ngtcp2_crypto_picotls_from_epoch(size_t epoch) { + switch (epoch) { + case 0: + return NGTCP2_CRYPTO_LEVEL_INITIAL; + case 1: + return NGTCP2_CRYPTO_LEVEL_EARLY; + case 2: + return NGTCP2_CRYPTO_LEVEL_HANDSHAKE; + case 3: + return NGTCP2_CRYPTO_LEVEL_APPLICATION; + default: + assert(0); + abort(); + } +} + +size_t ngtcp2_crypto_picotls_from_ngtcp2_crypto_level( + ngtcp2_crypto_level crypto_level) { + switch (crypto_level) { + case NGTCP2_CRYPTO_LEVEL_INITIAL: + return 0; + case NGTCP2_CRYPTO_LEVEL_EARLY: + return 1; + case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: + return 2; + case NGTCP2_CRYPTO_LEVEL_APPLICATION: + return 3; + default: + assert(0); + abort(); + } +} + +int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data, + void *user_data) { + (void)conn; + (void)user_data; + + ptls_openssl_random_bytes(data, NGTCP2_PATH_CHALLENGE_DATALEN); + + return 0; +} + +int ngtcp2_crypto_random(uint8_t *data, size_t datalen) { + ptls_openssl_random_bytes(data, datalen); + + return 0; +} + +void ngtcp2_crypto_picotls_ctx_init(ngtcp2_crypto_picotls_ctx *cptls) { + cptls->ptls = NULL; + memset(&cptls->handshake_properties, 0, sizeof(cptls->handshake_properties)); +} + +static int set_additional_extensions(ptls_handshake_properties_t *hsprops, + ngtcp2_conn *conn) { + const size_t buflen = 256; + uint8_t *buf; + ngtcp2_ssize nwrite; + ptls_raw_extension_t *exts = hsprops->additional_extensions; + + assert(exts); + + buf = malloc(buflen); + if (buf == NULL) { + return -1; + } + + nwrite = ngtcp2_conn_encode_local_transport_params(conn, buf, buflen); + if (nwrite < 0) { + goto fail; + } + + exts[0].type = NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1; + exts[0].data.base = buf; + exts[0].data.len = (size_t)nwrite; + + return 0; + +fail: + free(buf); + + return -1; +} + +int ngtcp2_crypto_picotls_collect_extension( + ptls_t *ptls, struct st_ptls_handshake_properties_t *properties, + uint16_t type) { + (void)ptls; + (void)properties; + + return type == NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1; +} + +int ngtcp2_crypto_picotls_collected_extensions( + ptls_t *ptls, struct st_ptls_handshake_properties_t *properties, + ptls_raw_extension_t *extensions) { + ngtcp2_crypto_conn_ref *conn_ref; + ngtcp2_conn *conn; + int rv; + + (void)properties; + + for (; extensions->type != UINT16_MAX; ++extensions) { + if (extensions->type != NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1) { + continue; + } + + conn_ref = *ptls_get_data_ptr(ptls); + conn = conn_ref->get_conn(conn_ref); + + rv = ngtcp2_conn_decode_remote_transport_params(conn, extensions->data.base, + extensions->data.len); + if (rv != 0) { + ngtcp2_conn_set_tls_error(conn, rv); + return -1; + } + + return 0; + } + + return 0; +} + +static int update_traffic_key_server_cb(ptls_update_traffic_key_t *self, + ptls_t *ptls, int is_enc, size_t epoch, + const void *secret) { + ngtcp2_crypto_conn_ref *conn_ref = *ptls_get_data_ptr(ptls); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + ngtcp2_crypto_level level = ngtcp2_crypto_picotls_from_epoch(epoch); + ptls_cipher_suite_t *cipher = ptls_get_cipher(ptls); + size_t secretlen = cipher->hash->digest_size; + ngtcp2_crypto_picotls_ctx *cptls; + + (void)self; + + if (is_enc) { + if (ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level, + secret, secretlen) != 0) { + return -1; + } + + if (level == NGTCP2_CRYPTO_LEVEL_HANDSHAKE) { + /* libngtcp2 allows an application to change QUIC transport + * parameters before installing Handshake tx key. We need to + * wait for the key to get the correct local transport + * parameters from ngtcp2_conn. + */ + cptls = ngtcp2_conn_get_tls_native_handle(conn); + + if (set_additional_extensions(&cptls->handshake_properties, conn) != 0) { + return -1; + } + } + + return 0; + } + + if (ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level, + secret, secretlen) != 0) { + return -1; + } + + return 0; +} + +static ptls_update_traffic_key_t update_traffic_key_server = { + update_traffic_key_server_cb, +}; + +static int update_traffic_key_cb(ptls_update_traffic_key_t *self, ptls_t *ptls, + int is_enc, size_t epoch, const void *secret) { + ngtcp2_crypto_conn_ref *conn_ref = *ptls_get_data_ptr(ptls); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + ngtcp2_crypto_level level = ngtcp2_crypto_picotls_from_epoch(epoch); + ptls_cipher_suite_t *cipher = ptls_get_cipher(ptls); + size_t secretlen = cipher->hash->digest_size; + + (void)self; + + if (is_enc) { + if (ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level, + secret, secretlen) != 0) { + return -1; + } + + return 0; + } + + if (ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level, + secret, secretlen) != 0) { + return -1; + } + + return 0; +} + +static ptls_update_traffic_key_t update_traffic_key = {update_traffic_key_cb}; + +int ngtcp2_crypto_picotls_configure_server_context(ptls_context_t *ctx) { + ctx->max_early_data_size = UINT32_MAX; + ctx->omit_end_of_early_data = 1; + ctx->update_traffic_key = &update_traffic_key_server; + + return 0; +} + +int ngtcp2_crypto_picotls_configure_client_context(ptls_context_t *ctx) { + ctx->omit_end_of_early_data = 1; + ctx->update_traffic_key = &update_traffic_key; + + return 0; +} + +int ngtcp2_crypto_picotls_configure_server_session( + ngtcp2_crypto_picotls_ctx *cptls) { + ptls_handshake_properties_t *hsprops = &cptls->handshake_properties; + + hsprops->collect_extension = ngtcp2_crypto_picotls_collect_extension; + hsprops->collected_extensions = ngtcp2_crypto_picotls_collected_extensions; + + return 0; +} + +int ngtcp2_crypto_picotls_configure_client_session( + ngtcp2_crypto_picotls_ctx *cptls, ngtcp2_conn *conn) { + ptls_handshake_properties_t *hsprops = &cptls->handshake_properties; + + hsprops->client.max_early_data_size = calloc(1, sizeof(uint32_t)); + if (hsprops->client.max_early_data_size == NULL) { + return -1; + } + + if (set_additional_extensions(hsprops, conn) != 0) { + free(hsprops->client.max_early_data_size); + hsprops->client.max_early_data_size = NULL; + return -1; + } + + hsprops->collect_extension = ngtcp2_crypto_picotls_collect_extension; + hsprops->collected_extensions = ngtcp2_crypto_picotls_collected_extensions; + + return 0; +} + +void ngtcp2_crypto_picotls_deconfigure_session( + ngtcp2_crypto_picotls_ctx *cptls) { + ptls_handshake_properties_t *hsprops; + ptls_raw_extension_t *exts; + + if (cptls == NULL) { + return; + } + + hsprops = &cptls->handshake_properties; + + free(hsprops->client.max_early_data_size); + + exts = hsprops->additional_extensions; + if (exts) { + free(hsprops->additional_extensions[0].data.base); + } +} diff --git a/deps/ngtcp2/ngtcp2/crypto/shared.c b/deps/ngtcp2/ngtcp2/crypto/shared.c index 5d040f2ce75558..78252b852b4fab 100644 --- a/deps/ngtcp2/ngtcp2/crypto/shared.c +++ b/deps/ngtcp2/ngtcp2/crypto/shared.c @@ -24,10 +24,18 @@ */ #include "shared.h" +#ifdef WIN32 +# include +# include +#else +# include +#endif + #include #include #include "ngtcp2_macro.h" +#include "ngtcp2_net.h" ngtcp2_crypto_md *ngtcp2_crypto_md_init(ngtcp2_crypto_md *md, void *md_native_handle) { @@ -78,10 +86,16 @@ int ngtcp2_crypto_derive_initial_secrets(uint32_t version, uint8_t *rx_secret, ngtcp2_crypto_ctx_initial(&ctx); - if (version == NGTCP2_PROTO_VER_V1) { + switch (version) { + case NGTCP2_PROTO_VER_V1: salt = (const uint8_t *)NGTCP2_INITIAL_SALT_V1; saltlen = sizeof(NGTCP2_INITIAL_SALT_V1) - 1; - } else { + break; + case NGTCP2_PROTO_VER_V2_DRAFT: + salt = (const uint8_t *)NGTCP2_INITIAL_SALT_V2_DRAFT; + saltlen = sizeof(NGTCP2_INITIAL_SALT_V2_DRAFT) - 1; + break; + default: salt = (const uint8_t *)NGTCP2_INITIAL_SALT_DRAFT; saltlen = sizeof(NGTCP2_INITIAL_SALT_DRAFT) - 1; } @@ -119,27 +133,55 @@ size_t ngtcp2_crypto_packet_protection_ivlen(const ngtcp2_crypto_aead *aead) { } int ngtcp2_crypto_derive_packet_protection_key( - uint8_t *key, uint8_t *iv, uint8_t *hp_key, const ngtcp2_crypto_aead *aead, - const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen) { - static const uint8_t KEY_LABEL[] = "quic key"; - static const uint8_t IV_LABEL[] = "quic iv"; - static const uint8_t HP_KEY_LABEL[] = "quic hp"; + uint8_t *key, uint8_t *iv, uint8_t *hp_key, uint32_t version, + const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_md *md, + const uint8_t *secret, size_t secretlen) { + static const uint8_t KEY_LABEL_V1[] = "quic key"; + static const uint8_t IV_LABEL_V1[] = "quic iv"; + static const uint8_t HP_KEY_LABEL_V1[] = "quic hp"; + static const uint8_t KEY_LABEL_V2_DRAFT[] = "quicv2 key"; + static const uint8_t IV_LABEL_V2_DRAFT[] = "quicv2 iv"; + static const uint8_t HP_KEY_LABEL_V2_DRAFT[] = "quicv2 hp"; size_t keylen = ngtcp2_crypto_aead_keylen(aead); size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); + const uint8_t *key_label; + size_t key_labellen; + const uint8_t *iv_label; + size_t iv_labellen; + const uint8_t *hp_key_label; + size_t hp_key_labellen; + + switch (version) { + case NGTCP2_PROTO_VER_V2_DRAFT: + key_label = KEY_LABEL_V2_DRAFT; + key_labellen = sizeof(KEY_LABEL_V2_DRAFT) - 1; + iv_label = IV_LABEL_V2_DRAFT; + iv_labellen = sizeof(IV_LABEL_V2_DRAFT) - 1; + hp_key_label = HP_KEY_LABEL_V2_DRAFT; + hp_key_labellen = sizeof(HP_KEY_LABEL_V2_DRAFT) - 1; + break; + default: + key_label = KEY_LABEL_V1; + key_labellen = sizeof(KEY_LABEL_V1) - 1; + iv_label = IV_LABEL_V1; + iv_labellen = sizeof(IV_LABEL_V1) - 1; + hp_key_label = HP_KEY_LABEL_V1; + hp_key_labellen = sizeof(HP_KEY_LABEL_V1) - 1; + } if (ngtcp2_crypto_hkdf_expand_label(key, keylen, md, secret, secretlen, - KEY_LABEL, sizeof(KEY_LABEL) - 1) != 0) { + key_label, key_labellen) != 0) { return -1; } if (ngtcp2_crypto_hkdf_expand_label(iv, ivlen, md, secret, secretlen, - IV_LABEL, sizeof(IV_LABEL) - 1) != 0) { + iv_label, iv_labellen) != 0) { return -1; } - if (hp_key != NULL && ngtcp2_crypto_hkdf_expand_label( - hp_key, keylen, md, secret, secretlen, HP_KEY_LABEL, - sizeof(HP_KEY_LABEL) - 1) != 0) { + if (hp_key != NULL && + ngtcp2_crypto_hkdf_expand_label(hp_key, keylen, md, secret, secretlen, + hp_key_label, hp_key_labellen) != 0) { return -1; } @@ -176,6 +218,7 @@ int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key, size_t ivlen; int rv; ngtcp2_crypto_ctx cctx; + uint32_t version; if (level == NGTCP2_CRYPTO_LEVEL_EARLY && !ngtcp2_conn_is_server(conn)) { return 0; @@ -191,12 +234,25 @@ int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key, hp_key = hp_keybuf; } - if (level == NGTCP2_CRYPTO_LEVEL_EARLY) { + switch (level) { + case NGTCP2_CRYPTO_LEVEL_EARLY: ngtcp2_crypto_ctx_tls_early(&cctx, tls); ngtcp2_conn_set_early_crypto_ctx(conn, &cctx); ctx = ngtcp2_conn_get_early_crypto_ctx(conn); - } else { + version = ngtcp2_conn_get_client_chosen_version(conn); + break; + case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: + if (ngtcp2_conn_is_server(conn) && + !ngtcp2_conn_get_negotiated_version(conn)) { + rv = ngtcp2_crypto_set_remote_transport_params(conn, tls); + if (rv != 0) { + return -1; + } + } + /* fall through */ + default: ctx = ngtcp2_conn_get_crypto_ctx(conn); + version = ngtcp2_conn_get_negotiated_version(conn); if (!ctx->aead.native_handle) { ngtcp2_crypto_ctx_tls(&cctx, tls); @@ -210,8 +266,8 @@ int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key, hp = &ctx->hp; ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); - if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, aead, md, - secret, secretlen) != 0) { + if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, version, aead, + md, secret, secretlen) != 0) { return -1; } @@ -272,17 +328,10 @@ int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key, * This function returns 0 if it succeeds, or -1. */ static int crypto_set_local_transport_params(ngtcp2_conn *conn, void *tls) { - ngtcp2_transport_params_type exttype = - ngtcp2_conn_is_server(conn) - ? NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS - : NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO; - ngtcp2_transport_params params; ngtcp2_ssize nwrite; uint8_t buf[256]; - ngtcp2_conn_get_local_transport_params(conn, ¶ms); - - nwrite = ngtcp2_encode_transport_params(buf, sizeof(buf), exttype, ¶ms); + nwrite = ngtcp2_conn_encode_local_transport_params(conn, buf, sizeof(buf)); if (nwrite < 0) { return -1; } @@ -310,6 +359,7 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key, size_t ivlen; int rv; ngtcp2_crypto_ctx cctx; + uint32_t version; if (level == NGTCP2_CRYPTO_LEVEL_EARLY && ngtcp2_conn_is_server(conn)) { return 0; @@ -325,12 +375,25 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key, hp_key = hp_keybuf; } - if (level == NGTCP2_CRYPTO_LEVEL_EARLY) { + switch (level) { + case NGTCP2_CRYPTO_LEVEL_EARLY: ngtcp2_crypto_ctx_tls_early(&cctx, tls); ngtcp2_conn_set_early_crypto_ctx(conn, &cctx); ctx = ngtcp2_conn_get_early_crypto_ctx(conn); - } else { + version = ngtcp2_conn_get_client_chosen_version(conn); + break; + case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: + if (ngtcp2_conn_is_server(conn) && + !ngtcp2_conn_get_negotiated_version(conn)) { + rv = ngtcp2_crypto_set_remote_transport_params(conn, tls); + if (rv != 0) { + return -1; + } + } + /* fall through */ + default: ctx = ngtcp2_conn_get_crypto_ctx(conn); + version = ngtcp2_conn_get_negotiated_version(conn); if (!ctx->aead.native_handle) { ngtcp2_crypto_ctx_tls(&cctx, tls); @@ -344,8 +407,8 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key, hp = &ctx->hp; ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); - if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, aead, md, - secret, secretlen) != 0) { + if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, version, aead, + md, secret, secretlen) != 0) { return -1; } @@ -371,15 +434,9 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key, goto fail; } - if (ngtcp2_conn_is_server(conn)) { - rv = ngtcp2_crypto_set_remote_transport_params(conn, tls); - if (rv != 0) { - return rv; - } - - if (crypto_set_local_transport_params(conn, tls) != 0) { - return rv; - } + if (ngtcp2_conn_is_server(conn) && + crypto_set_local_transport_params(conn, tls) != 0) { + goto fail; } break; @@ -408,7 +465,7 @@ int ngtcp2_crypto_derive_and_install_initial_key( ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp_key, uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp_key, - const ngtcp2_cid *client_dcid) { + uint32_t version, const ngtcp2_cid *client_dcid) { uint8_t rx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; uint8_t tx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; uint8_t initial_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; @@ -427,7 +484,6 @@ int ngtcp2_crypto_derive_and_install_initial_key( ngtcp2_crypto_aead_ctx retry_aead_ctx = {0}; int rv; int server = ngtcp2_conn_is_server(conn); - uint32_t version = ngtcp2_conn_get_negotiated_version(conn); const uint8_t *retry_key; size_t retry_noncelen; @@ -472,13 +528,13 @@ int ngtcp2_crypto_derive_and_install_initial_key( } if (ngtcp2_crypto_derive_packet_protection_key( - rx_key, rx_iv, rx_hp_key, &ctx.aead, &ctx.md, rx_secret, + rx_key, rx_iv, rx_hp_key, version, &ctx.aead, &ctx.md, rx_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { return -1; } if (ngtcp2_crypto_derive_packet_protection_key( - tx_key, tx_iv, tx_hp_key, &ctx.aead, &ctx.md, tx_secret, + tx_key, tx_iv, tx_hp_key, version, &ctx.aead, &ctx.md, tx_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { return -1; } @@ -506,10 +562,16 @@ int ngtcp2_crypto_derive_and_install_initial_key( if (!server && !ngtcp2_conn_after_retry(conn)) { ngtcp2_crypto_aead_retry(&retry_aead); - if (ngtcp2_conn_get_negotiated_version(conn) == NGTCP2_PROTO_VER_V1) { + switch (version) { + case NGTCP2_PROTO_VER_V1: retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_V1; retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1; - } else { + break; + case NGTCP2_PROTO_VER_V2_DRAFT: + retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_V2_DRAFT; + retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1; + break; + default: retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_DRAFT; retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1; } @@ -543,6 +605,114 @@ int ngtcp2_crypto_derive_and_install_initial_key( return -1; } +int ngtcp2_crypto_derive_and_install_vneg_initial_key( + ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, + uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, + uint8_t *rx_hp_key, uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp_key, + uint32_t version, const ngtcp2_cid *client_dcid) { + uint8_t rx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t tx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t initial_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t rx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t rx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN]; + uint8_t rx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t tx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t tx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN]; + uint8_t tx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + const ngtcp2_crypto_ctx *ctx = ngtcp2_conn_get_initial_crypto_ctx(conn); + ngtcp2_crypto_aead_ctx rx_aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx rx_hp_ctx = {0}; + ngtcp2_crypto_aead_ctx tx_aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx tx_hp_ctx = {0}; + int rv; + int server = ngtcp2_conn_is_server(conn); + + if (!rx_secret) { + rx_secret = rx_secretbuf; + } + if (!tx_secret) { + tx_secret = tx_secretbuf; + } + if (!initial_secret) { + initial_secret = initial_secretbuf; + } + + if (!rx_key) { + rx_key = rx_keybuf; + } + if (!rx_iv) { + rx_iv = rx_ivbuf; + } + if (!rx_hp_key) { + rx_hp_key = rx_hp_keybuf; + } + if (!tx_key) { + tx_key = tx_keybuf; + } + if (!tx_iv) { + tx_iv = tx_ivbuf; + } + if (!tx_hp_key) { + tx_hp_key = tx_hp_keybuf; + } + + if (ngtcp2_crypto_derive_initial_secrets( + version, rx_secret, tx_secret, initial_secret, client_dcid, + server ? NGTCP2_CRYPTO_SIDE_SERVER : NGTCP2_CRYPTO_SIDE_CLIENT) != + 0) { + return -1; + } + + if (ngtcp2_crypto_derive_packet_protection_key( + rx_key, rx_iv, rx_hp_key, version, &ctx->aead, &ctx->md, rx_secret, + NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { + return -1; + } + + if (ngtcp2_crypto_derive_packet_protection_key( + tx_key, tx_iv, tx_hp_key, version, &ctx->aead, &ctx->md, tx_secret, + NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { + return -1; + } + + if (ngtcp2_crypto_aead_ctx_decrypt_init(&rx_aead_ctx, &ctx->aead, rx_key, + NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) { + goto fail; + } + + if (ngtcp2_crypto_cipher_ctx_encrypt_init(&rx_hp_ctx, &ctx->hp, rx_hp_key) != + 0) { + goto fail; + } + + if (ngtcp2_crypto_aead_ctx_encrypt_init(&tx_aead_ctx, &ctx->aead, tx_key, + NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) { + goto fail; + } + + if (ngtcp2_crypto_cipher_ctx_encrypt_init(&tx_hp_ctx, &ctx->hp, tx_hp_key) != + 0) { + goto fail; + } + + rv = ngtcp2_conn_install_vneg_initial_key( + conn, version, &rx_aead_ctx, rx_iv, &rx_hp_ctx, &tx_aead_ctx, tx_iv, + &tx_hp_ctx, NGTCP2_CRYPTO_INITIAL_IVLEN); + if (rv != 0) { + goto fail; + } + + return 0; + +fail: + ngtcp2_crypto_cipher_ctx_free(&tx_hp_ctx); + ngtcp2_crypto_aead_ctx_free(&tx_aead_ctx); + ngtcp2_crypto_cipher_ctx_free(&rx_hp_ctx); + ngtcp2_crypto_aead_ctx_free(&rx_aead_ctx); + + return -1; +} + int ngtcp2_crypto_update_key( ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_key, uint8_t *rx_iv, @@ -553,14 +723,15 @@ int ngtcp2_crypto_update_key( const ngtcp2_crypto_aead *aead = &ctx->aead; const ngtcp2_crypto_md *md = &ctx->md; size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); + uint32_t version = ngtcp2_conn_get_negotiated_version(conn); if (ngtcp2_crypto_update_traffic_secret(rx_secret, md, current_rx_secret, secretlen) != 0) { return -1; } - if (ngtcp2_crypto_derive_packet_protection_key(rx_key, rx_iv, NULL, aead, md, - rx_secret, secretlen) != 0) { + if (ngtcp2_crypto_derive_packet_protection_key( + rx_key, rx_iv, NULL, version, aead, md, rx_secret, secretlen) != 0) { return -1; } @@ -569,8 +740,8 @@ int ngtcp2_crypto_update_key( return -1; } - if (ngtcp2_crypto_derive_packet_protection_key(tx_key, tx_iv, NULL, aead, md, - tx_secret, secretlen) != 0) { + if (ngtcp2_crypto_derive_packet_protection_key( + tx_key, tx_iv, NULL, version, aead, md, tx_secret, secretlen) != 0) { return -1; } @@ -593,9 +764,9 @@ int ngtcp2_crypto_encrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *plaintext, size_t plaintextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen) { + const uint8_t *aad, size_t aadlen) { if (ngtcp2_crypto_encrypt(dest, aead, aead_ctx, plaintext, plaintextlen, - nonce, noncelen, ad, adlen) != 0) { + nonce, noncelen, aad, aadlen) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; @@ -605,10 +776,10 @@ int ngtcp2_crypto_decrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *ciphertext, size_t ciphertextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen) { + const uint8_t *aad, size_t aadlen) { if (ngtcp2_crypto_decrypt(dest, aead, aead_ctx, ciphertext, ciphertextlen, - nonce, noncelen, ad, adlen) != 0) { - return NGTCP2_ERR_TLS_DECRYPT; + nonce, noncelen, aad, aadlen) != 0) { + return NGTCP2_ERR_DECRYPT; } return 0; } @@ -643,32 +814,413 @@ int ngtcp2_crypto_update_key_cb( } int ngtcp2_crypto_generate_stateless_reset_token(uint8_t *token, - const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen, const ngtcp2_cid *cid) { - uint8_t buf[64]; + static const uint8_t info[] = "stateless_reset"; + ngtcp2_crypto_md md; + + if (ngtcp2_crypto_hkdf(token, NGTCP2_STATELESS_RESET_TOKENLEN, + ngtcp2_crypto_md_sha256(&md), secret, secretlen, + cid->data, cid->datalen, info, + sizeof(info) - 1) != 0) { + return -1; + } + + return 0; +} + +static int crypto_derive_token_key(uint8_t *key, size_t keylen, uint8_t *iv, + size_t ivlen, const ngtcp2_crypto_md *md, + const uint8_t *secret, size_t secretlen, + const uint8_t *salt, size_t saltlen, + const uint8_t *info_prefix, + size_t info_prefixlen) { + static const uint8_t key_info_suffix[] = " key"; + static const uint8_t iv_info_suffix[] = " iv"; + uint8_t intsecret[32]; + uint8_t info[32]; + uint8_t *p; + + assert(ngtcp2_crypto_md_hashlen(md) == sizeof(intsecret)); + assert(info_prefixlen + sizeof(key_info_suffix) - 1 <= sizeof(info)); + assert(info_prefixlen + sizeof(iv_info_suffix) - 1 <= sizeof(info)); + + if (ngtcp2_crypto_hkdf_extract(intsecret, md, secret, secretlen, salt, + saltlen) != 0) { + return -1; + } + + memcpy(info, info_prefix, info_prefixlen); + p = info + info_prefixlen; + + memcpy(p, key_info_suffix, sizeof(key_info_suffix) - 1); + p += sizeof(key_info_suffix) - 1; + + if (ngtcp2_crypto_hkdf_expand(key, keylen, md, intsecret, sizeof(intsecret), + info, (size_t)(p - info)) != 0) { + return -1; + } + + p = info + info_prefixlen; + + memcpy(p, iv_info_suffix, sizeof(iv_info_suffix) - 1); + p += sizeof(iv_info_suffix) - 1; + + if (ngtcp2_crypto_hkdf_expand(iv, ivlen, md, intsecret, sizeof(intsecret), + info, (size_t)(p - info)) != 0) { + return -1; + } + + return 0; +} + +static size_t crypto_generate_retry_token_aad(uint8_t *dest, uint32_t version, + const ngtcp2_sockaddr *sa, + ngtcp2_socklen salen, + const ngtcp2_cid *retry_scid) { + uint8_t *p = dest; + + version = ngtcp2_htonl(version); + memcpy(p, &version, sizeof(version)); + memcpy(p, sa, (size_t)salen); + p += salen; + memcpy(p, retry_scid->data, retry_scid->datalen); + p += retry_scid->datalen; + + return (size_t)(p - dest); +} + +static const uint8_t retry_token_info_prefix[] = "retry_token"; + +ngtcp2_ssize ngtcp2_crypto_generate_retry_token( + uint8_t *token, const uint8_t *secret, size_t secretlen, uint32_t version, + const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen, + const ngtcp2_cid *retry_scid, const ngtcp2_cid *odcid, ngtcp2_tstamp ts) { + uint8_t plaintext[NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN]; + uint8_t rand_data[NGTCP2_CRYPTO_TOKEN_RAND_DATALEN]; + uint8_t key[32]; + uint8_t iv[32]; + size_t keylen; + size_t ivlen; + ngtcp2_crypto_aead aead; + ngtcp2_crypto_md md; + ngtcp2_crypto_aead_ctx aead_ctx; + size_t plaintextlen; + uint8_t aad[sizeof(version) + sizeof(ngtcp2_sockaddr_storage) + + NGTCP2_MAX_CIDLEN]; + size_t aadlen; + uint8_t *p = plaintext; + ngtcp2_tstamp ts_be = ngtcp2_htonl64(ts); int rv; - assert(ngtcp2_crypto_md_hashlen(md) <= sizeof(buf)); - assert(NGTCP2_STATELESS_RESET_TOKENLEN <= sizeof(buf)); + memset(plaintext, 0, sizeof(plaintext)); + + *p++ = (uint8_t)odcid->datalen; + memcpy(p, odcid->data, odcid->datalen); + p += NGTCP2_MAX_CIDLEN; + memcpy(p, &ts_be, sizeof(ts_be)); + p += sizeof(ts_be); + + plaintextlen = (size_t)(p - plaintext); + + if (ngtcp2_crypto_random(rand_data, sizeof(rand_data)) != 0) { + return -1; + } + + ngtcp2_crypto_aead_aes_128_gcm(&aead); + ngtcp2_crypto_md_sha256(&md); + + keylen = ngtcp2_crypto_aead_keylen(&aead); + ivlen = ngtcp2_crypto_aead_noncelen(&aead); + + assert(sizeof(key) >= keylen); + assert(sizeof(iv) >= ivlen); + + if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen, + rand_data, sizeof(rand_data), + retry_token_info_prefix, + sizeof(retry_token_info_prefix) - 1) != 0) { + return -1; + } + + aadlen = crypto_generate_retry_token_aad(aad, version, remote_addr, + remote_addrlen, retry_scid); + + p = token; + *p++ = NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY; + + if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &aead, key, ivlen) != 0) { + return -1; + } + + rv = ngtcp2_crypto_encrypt(p, &aead, &aead_ctx, plaintext, plaintextlen, iv, + ivlen, aad, aadlen); + + ngtcp2_crypto_aead_ctx_free(&aead_ctx); - rv = ngtcp2_crypto_hkdf_extract(buf, md, secret, secretlen, cid->data, - cid->datalen); if (rv != 0) { return -1; } - memcpy(token, buf, NGTCP2_STATELESS_RESET_TOKENLEN); + p += plaintextlen + aead.max_overhead; + memcpy(p, rand_data, sizeof(rand_data)); + p += sizeof(rand_data); + + return p - token; +} + +int ngtcp2_crypto_verify_retry_token( + ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen, + const uint8_t *secret, size_t secretlen, uint32_t version, + const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen, + const ngtcp2_cid *dcid, ngtcp2_duration timeout, ngtcp2_tstamp ts) { + uint8_t + plaintext[/* cid len = */ 1 + NGTCP2_MAX_CIDLEN + sizeof(ngtcp2_tstamp)]; + uint8_t key[32]; + uint8_t iv[32]; + size_t keylen; + size_t ivlen; + ngtcp2_crypto_aead_ctx aead_ctx; + ngtcp2_crypto_aead aead; + ngtcp2_crypto_md md; + uint8_t aad[sizeof(version) + sizeof(ngtcp2_sockaddr_storage) + + NGTCP2_MAX_CIDLEN]; + size_t aadlen; + const uint8_t *rand_data; + const uint8_t *ciphertext; + size_t ciphertextlen; + size_t cil; + int rv; + ngtcp2_tstamp gen_ts; + + if (tokenlen != NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN || + token[0] != NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY) { + return -1; + } + + rand_data = token + tokenlen - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN; + ciphertext = token + 1; + ciphertextlen = tokenlen - 1 - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN; + + ngtcp2_crypto_aead_aes_128_gcm(&aead); + ngtcp2_crypto_md_sha256(&md); + + keylen = ngtcp2_crypto_aead_keylen(&aead); + ivlen = ngtcp2_crypto_aead_noncelen(&aead); + + if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen, + rand_data, NGTCP2_CRYPTO_TOKEN_RAND_DATALEN, + retry_token_info_prefix, + sizeof(retry_token_info_prefix) - 1) != 0) { + return -1; + } + + aadlen = crypto_generate_retry_token_aad(aad, version, remote_addr, + remote_addrlen, dcid); + + if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, &aead, key, ivlen) != 0) { + return -1; + } + + rv = ngtcp2_crypto_decrypt(plaintext, &aead, &aead_ctx, ciphertext, + ciphertextlen, iv, ivlen, aad, aadlen); + + ngtcp2_crypto_aead_ctx_free(&aead_ctx); + + if (rv != 0) { + return -1; + } + + cil = plaintext[0]; + + assert(cil == 0 || (cil >= NGTCP2_MIN_CIDLEN && cil <= NGTCP2_MAX_CIDLEN)); + + memcpy(&gen_ts, plaintext + /* cid len = */ 1 + NGTCP2_MAX_CIDLEN, + sizeof(gen_ts)); + + gen_ts = ngtcp2_ntohl64(gen_ts); + if (gen_ts + timeout <= ts) { + return -1; + } + + ngtcp2_cid_init(odcid, plaintext + /* cid len = */ 1, cil); return 0; } -ngtcp2_ssize ngtcp2_crypto_write_connection_close(uint8_t *dest, size_t destlen, - uint32_t version, - const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, - uint64_t error_code) { +static size_t crypto_generate_regular_token_aad(uint8_t *dest, + const ngtcp2_sockaddr *sa) { + const uint8_t *addr; + size_t addrlen; + + switch (sa->sa_family) { + case AF_INET: + addr = (const uint8_t *)&((const ngtcp2_sockaddr_in *)(void *)sa)->sin_addr; + addrlen = sizeof(((const ngtcp2_sockaddr_in *)(void *)sa)->sin_addr); + break; + case AF_INET6: + addr = + (const uint8_t *)&((const ngtcp2_sockaddr_in6 *)(void *)sa)->sin6_addr; + addrlen = sizeof(((const ngtcp2_sockaddr_in6 *)(void *)sa)->sin6_addr); + break; + default: + assert(0); + abort(); + } + + memcpy(dest, addr, addrlen); + + return addrlen; +} + +static const uint8_t regular_token_info_prefix[] = "regular_token"; + +ngtcp2_ssize ngtcp2_crypto_generate_regular_token( + uint8_t *token, const uint8_t *secret, size_t secretlen, + const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen, + ngtcp2_tstamp ts) { + uint8_t plaintext[sizeof(ngtcp2_tstamp)]; + uint8_t rand_data[NGTCP2_CRYPTO_TOKEN_RAND_DATALEN]; + uint8_t key[32]; + uint8_t iv[32]; + size_t keylen; + size_t ivlen; + ngtcp2_crypto_aead aead; + ngtcp2_crypto_md md; + ngtcp2_crypto_aead_ctx aead_ctx; + size_t plaintextlen; + uint8_t aad[sizeof(ngtcp2_sockaddr_in6)]; + size_t aadlen; + uint8_t *p = plaintext; + ngtcp2_tstamp ts_be = ngtcp2_htonl64(ts); + int rv; + (void)remote_addrlen; + + memcpy(p, &ts_be, sizeof(ts_be)); + p += sizeof(ts_be); + + plaintextlen = (size_t)(p - plaintext); + + if (ngtcp2_crypto_random(rand_data, sizeof(rand_data)) != 0) { + return -1; + } + + ngtcp2_crypto_aead_aes_128_gcm(&aead); + ngtcp2_crypto_md_sha256(&md); + + keylen = ngtcp2_crypto_aead_keylen(&aead); + ivlen = ngtcp2_crypto_aead_noncelen(&aead); + + assert(sizeof(key) >= keylen); + assert(sizeof(iv) >= ivlen); + + if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen, + rand_data, sizeof(rand_data), + regular_token_info_prefix, + sizeof(regular_token_info_prefix) - 1) != 0) { + return -1; + } + + aadlen = crypto_generate_regular_token_aad(aad, remote_addr); + + p = token; + *p++ = NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR; + + if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &aead, key, ivlen) != 0) { + return -1; + } + + rv = ngtcp2_crypto_encrypt(p, &aead, &aead_ctx, plaintext, plaintextlen, iv, + ivlen, aad, aadlen); + + ngtcp2_crypto_aead_ctx_free(&aead_ctx); + + if (rv != 0) { + return -1; + } + + p += plaintextlen + aead.max_overhead; + memcpy(p, rand_data, sizeof(rand_data)); + p += sizeof(rand_data); + + return p - token; +} + +int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen, + const uint8_t *secret, size_t secretlen, + const ngtcp2_sockaddr *remote_addr, + ngtcp2_socklen remote_addrlen, + ngtcp2_duration timeout, + ngtcp2_tstamp ts) { + uint8_t plaintext[sizeof(ngtcp2_tstamp)]; + uint8_t key[32]; + uint8_t iv[32]; + size_t keylen; + size_t ivlen; + ngtcp2_crypto_aead_ctx aead_ctx; + ngtcp2_crypto_aead aead; + ngtcp2_crypto_md md; + uint8_t aad[sizeof(ngtcp2_sockaddr_in6)]; + size_t aadlen; + const uint8_t *rand_data; + const uint8_t *ciphertext; + size_t ciphertextlen; + int rv; + ngtcp2_tstamp gen_ts; + (void)remote_addrlen; + + if (tokenlen != NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN || + token[0] != NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR) { + return -1; + } + + rand_data = token + tokenlen - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN; + ciphertext = token + 1; + ciphertextlen = tokenlen - 1 - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN; + + ngtcp2_crypto_aead_aes_128_gcm(&aead); + ngtcp2_crypto_md_sha256(&md); + + keylen = ngtcp2_crypto_aead_keylen(&aead); + ivlen = ngtcp2_crypto_aead_noncelen(&aead); + + if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen, + rand_data, NGTCP2_CRYPTO_TOKEN_RAND_DATALEN, + regular_token_info_prefix, + sizeof(regular_token_info_prefix) - 1) != 0) { + return -1; + } + + aadlen = crypto_generate_regular_token_aad(aad, remote_addr); + + if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, &aead, key, ivlen) != 0) { + return -1; + } + + rv = ngtcp2_crypto_decrypt(plaintext, &aead, &aead_ctx, ciphertext, + ciphertextlen, iv, ivlen, aad, aadlen); + + ngtcp2_crypto_aead_ctx_free(&aead_ctx); + + if (rv != 0) { + return -1; + } + + memcpy(&gen_ts, plaintext, sizeof(gen_ts)); + + gen_ts = ngtcp2_ntohl64(gen_ts); + if (gen_ts + timeout <= ts) { + return -1; + } + + return 0; +} + +ngtcp2_ssize ngtcp2_crypto_write_connection_close( + uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason, + size_t reasonlen) { uint8_t rx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; uint8_t tx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; uint8_t initial_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; @@ -689,7 +1241,7 @@ ngtcp2_ssize ngtcp2_crypto_write_connection_close(uint8_t *dest, size_t destlen, } if (ngtcp2_crypto_derive_packet_protection_key( - tx_key, tx_iv, tx_hp_key, &ctx.aead, &ctx.md, tx_secret, + tx_key, tx_iv, tx_hp_key, version, &ctx.aead, &ctx.md, tx_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { return -1; } @@ -706,8 +1258,9 @@ ngtcp2_ssize ngtcp2_crypto_write_connection_close(uint8_t *dest, size_t destlen, } spktlen = ngtcp2_pkt_write_connection_close( - dest, destlen, version, dcid, scid, error_code, ngtcp2_crypto_encrypt_cb, - &ctx.aead, &aead_ctx, tx_iv, ngtcp2_crypto_hp_mask_cb, &ctx.hp, &hp_ctx); + dest, destlen, version, dcid, scid, error_code, reason, reasonlen, + ngtcp2_crypto_encrypt_cb, &ctx.aead, &aead_ctx, tx_iv, + ngtcp2_crypto_hp_mask_cb, &ctx.hp, &hp_ctx); if (spktlen < 0) { spktlen = -1; } @@ -732,10 +1285,16 @@ ngtcp2_ssize ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen, ngtcp2_crypto_aead_retry(&aead); - if (version == NGTCP2_PROTO_VER_V1) { + switch (version) { + case NGTCP2_PROTO_VER_V1: key = (const uint8_t *)NGTCP2_RETRY_KEY_V1; noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1; - } else { + break; + case NGTCP2_PROTO_VER_V2_DRAFT: + key = (const uint8_t *)NGTCP2_RETRY_KEY_V2_DRAFT; + noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1; + break; + default: key = (const uint8_t *)NGTCP2_RETRY_KEY_DRAFT; noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1; } @@ -757,22 +1316,14 @@ ngtcp2_ssize ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen, return spktlen; } -/* - * crypto_setup_initial_crypto establishes the initial secrets and - * encryption keys, and prepares local QUIC transport parameters. - */ -static int crypto_setup_initial_crypto(ngtcp2_conn *conn, - const ngtcp2_cid *dcid) { - return ngtcp2_crypto_derive_and_install_initial_key( - conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, dcid); -} - int ngtcp2_crypto_client_initial_cb(ngtcp2_conn *conn, void *user_data) { const ngtcp2_cid *dcid = ngtcp2_conn_get_dcid(conn); void *tls = ngtcp2_conn_get_tls_native_handle(conn); (void)user_data; - if (crypto_setup_initial_crypto(conn, dcid) != 0) { + if (ngtcp2_crypto_derive_and_install_initial_key( + conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + ngtcp2_conn_get_client_chosen_version(conn), dcid) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -792,9 +1343,9 @@ int ngtcp2_crypto_recv_retry_cb(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, void *user_data) { (void)user_data; - if (ngtcp2_crypto_derive_and_install_initial_key(conn, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, - &hd->scid) != 0) { + if (ngtcp2_crypto_derive_and_install_initial_key( + conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + ngtcp2_conn_get_client_chosen_version(conn), &hd->scid) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -806,7 +1357,23 @@ int ngtcp2_crypto_recv_client_initial_cb(ngtcp2_conn *conn, void *user_data) { (void)user_data; - if (crypto_setup_initial_crypto(conn, dcid) != 0) { + if (ngtcp2_crypto_derive_and_install_initial_key( + conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + ngtcp2_conn_get_client_chosen_version(conn), dcid) != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +int ngtcp2_crypto_version_negotiation_cb(ngtcp2_conn *conn, uint32_t version, + const ngtcp2_cid *client_dcid, + void *user_data) { + (void)user_data; + + if (ngtcp2_crypto_derive_and_install_vneg_initial_key( + conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, version, + client_dcid) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -829,3 +1396,23 @@ void ngtcp2_crypto_delete_crypto_cipher_ctx_cb( ngtcp2_crypto_cipher_ctx_free(cipher_ctx); } + +int ngtcp2_crypto_recv_crypto_data_cb(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + uint64_t offset, const uint8_t *data, + size_t datalen, void *user_data) { + int rv; + (void)offset; + (void)user_data; + + if (ngtcp2_crypto_read_write_crypto_data(conn, crypto_level, data, datalen) != + 0) { + rv = ngtcp2_conn_get_tls_error(conn); + if (rv) { + return rv; + } + return NGTCP2_ERR_CRYPTO; + } + + return 0; +} diff --git a/deps/ngtcp2/ngtcp2/crypto/shared.h b/deps/ngtcp2/ngtcp2/crypto/shared.h index b7fe2f15da93a7..02b948901ae40e 100644 --- a/deps/ngtcp2/ngtcp2/crypto/shared.h +++ b/deps/ngtcp2/ngtcp2/crypto/shared.h @@ -51,6 +51,16 @@ "\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb" \ "\x7f\x0a" +/** + * @macro + * + * :macro:`NGTCP2_INITIAL_SALT_V2_DRAFT` is a salt value which is used to + * derive initial secret. It is used for QUIC v2 draft. + */ +#define NGTCP2_INITIAL_SALT_V2_DRAFT \ + "\xa7\x07\xc2\x03\xa5\x9b\x47\x18\x4a\x1d\x62\xca\x57\x04\x06\xea\x7a\xe3" \ + "\xe5\xd3" + /* Maximum key usage (encryption) limits */ #define NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM (1ULL << 23) #define NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305 (1ULL << 62) @@ -62,6 +72,40 @@ #define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305 (1ULL << 36) #define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM (2965820ULL) +/** + * @function + * + * `ngtcp2_crypto_ctx_initial` initializes |ctx| for Initial packet + * encryption and decryption. + */ +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx); + +/** + * @function + * + * `ngtcp2_crypto_aead_init` initializes |aead| with the provided + * |aead_native_handle| which is an underlying AEAD object. + * + * If libngtcp2_crypto_openssl is linked, |aead_native_handle| must be + * a pointer to EVP_CIPHER. + * + * If libngtcp2_crypto_gnutls is linked, |aead_native_handle| must be + * gnutls_cipher_algorithm_t casted to ``void *``. + * + * If libngtcp2_crypto_boringssl is linked, |aead_native_handle| must + * be a pointer to EVP_AEAD. + */ +ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead, + void *aead_native_handle); + +/** + * @function + * + * `ngtcp2_crypto_aead_retry` initializes |aead| with the AEAD cipher + * AEAD_AES_128_GCM for Retry packet integrity protection. + */ +ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead); + /** * @function * @@ -84,6 +128,34 @@ int ngtcp2_crypto_derive_initial_secrets(uint32_t version, uint8_t *rx_secret, const ngtcp2_cid *client_dcid, ngtcp2_crypto_side side); +/** + * @function + * + * `ngtcp2_crypto_derive_packet_protection_key` derives packet + * protection key. This function writes packet protection key into + * the buffer pointed by |key|. The length of derived key is + * `ngtcp2_crypto_aead_keylen(aead) ` + * bytes. |key| must have enough capacity to store the key. This + * function writes packet protection IV into |iv|. The length of + * derived IV is `ngtcp2_crypto_packet_protection_ivlen(aead) + * ` bytes. |iv| must have + * enough capacity to store the IV. + * + * If |hp| is not NULL, this function also derives packet header + * protection key and writes the key into the buffer pointed by |hp|. + * The length of derived key is `ngtcp2_crypto_aead_keylen(aead) + * ` bytes. |hp|, if not NULL, must have + * enough capacity to store the key. + * + * This function returns 0 if it succeeds, or -1. + */ +int ngtcp2_crypto_derive_packet_protection_key(uint8_t *key, uint8_t *iv, + uint8_t *hp, uint32_t version, + const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_md *md, + const uint8_t *secret, + size_t secretlen); + /** * @function * @@ -183,7 +255,58 @@ int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls); int ngtcp2_crypto_derive_and_install_initial_key( ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp, - uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, + uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, uint32_t version, + const ngtcp2_cid *client_dcid); + +/** + * @function + * + * `ngtcp2_crypto_derive_and_install_vneg_initial_key` derives initial + * keying materials and installs keys to |conn|. This function is + * dedicated to install keys for |version| which is negotiated, or + * being negotiated. + * + * If |rx_secret| is not NULL, the secret for decryption is written to + * the buffer pointed by |rx_secret|. The length of secret is 32 + * bytes, and |rx_secret| must point to the buffer which has enough + * capacity. + * + * If |tx_secret| is not NULL, the secret for encryption is written to + * the buffer pointed by |tx_secret|. The length of secret is 32 + * bytes, and |tx_secret| must point to the buffer which has enough + * capacity. + * + * If |initial_secret| is not NULL, the initial secret is written to + * the buffer pointed by |initial_secret|. The length of secret is 32 + * bytes, and |initial_secret| must point to the buffer which has + * enough capacity. + * + * |client_dcid| is the destination connection ID in first Initial + * packet of client. + * + * If |rx_key| is not NULL, the derived packet protection key for + * decryption is written to the buffer pointed by |rx_key|. If + * |rx_iv| is not NULL, the derived packet protection IV for + * decryption is written to the buffer pointed by |rx_iv|. If |rx_hp| + * is not NULL, the derived header protection key for decryption is + * written to the buffer pointed by |rx_hp|. + * + * If |tx_key| is not NULL, the derived packet protection key for + * encryption is written to the buffer pointed by |tx_key|. If + * |tx_iv| is not NULL, the derived packet protection IV for + * encryption is written to the buffer pointed by |tx_iv|. If |tx_hp| + * is not NULL, the derived header protection key for encryption is + * written to the buffer pointed by |tx_hp|. + * + * The length of packet protection key and header protection key is 16 + * bytes long. The length of packet protection IV is 12 bytes long. + * + * This function returns 0 if it succeeds, or -1. + */ +int ngtcp2_crypto_derive_and_install_vneg_initial_key( + ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, + uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp, + uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, uint32_t version, const ngtcp2_cid *client_dcid); /** @@ -208,4 +331,20 @@ int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx, */ void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx); +/* + * `ngtcp2_crypto_md_sha256` initializes |md| with SHA256 message + * digest algorithm and returns |md|. + */ +ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md); + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead); + +/* + * `ngtcp2_crypto_random` writes cryptographically-secure random + * |datalen| bytes into the buffer pointed by |data|. + * + * This function returns 0 if it succeeds, or -1. + */ +int ngtcp2_crypto_random(uint8_t *data, size_t datalen); + #endif /* NGTCP2_SHARED_H */ diff --git a/deps/ngtcp2/ngtcp2/crypto/wolfssl/wolfssl.c b/deps/ngtcp2/ngtcp2/crypto/wolfssl/wolfssl.c new file mode 100644 index 00000000000000..9a58b9be2b76e9 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/crypto/wolfssl/wolfssl.c @@ -0,0 +1,524 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include +#include + +#include +#include + +#include "shared.h" + +#define PRINTF_DEBUG 0 +#if PRINTF_DEBUG +# define DEBUG_MSG(...) fprintf(stderr, __VA_ARGS__) +#else +# define DEBUG_MSG(...) (void)0 +#endif + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) { + return ngtcp2_crypto_aead_init(aead, (void *)wolfSSL_EVP_aes_128_gcm()); +} + +ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) { + md->native_handle = (void *)wolfSSL_EVP_sha256(); + return md; +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) { + ngtcp2_crypto_aead_init(&ctx->aead, (void *)wolfSSL_EVP_aes_128_gcm()); + ctx->md.native_handle = (void *)wolfSSL_EVP_sha256(); + ctx->hp.native_handle = (void *)wolfSSL_EVP_aes_128_ctr(); + ctx->max_encryption = 0; + ctx->max_decryption_failure = 0; + return ctx; +} + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead, + void *aead_native_handle) { + aead->native_handle = aead_native_handle; + aead->max_overhead = wolfSSL_quic_get_aead_tag_len( + (const WOLFSSL_EVP_CIPHER *)(aead_native_handle)); + return aead; +} + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) { + return ngtcp2_crypto_aead_init(aead, (void *)wolfSSL_EVP_aes_128_gcm()); +} + +static uint64_t crypto_wolfssl_get_aead_max_encryption(WOLFSSL *ssl) { + const WOLFSSL_EVP_CIPHER *aead = wolfSSL_quic_get_aead(ssl); + + if (wolfSSL_quic_aead_is_gcm(aead)) { + return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM; + } + if (wolfSSL_quic_aead_is_chacha20(aead)) { + return NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305; + } + if (wolfSSL_quic_aead_is_ccm(aead)) { + return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_CCM; + } + return 0; +} + +static uint64_t crypto_wolfssl_get_aead_max_decryption_failure(WOLFSSL *ssl) { + const WOLFSSL_EVP_CIPHER *aead = wolfSSL_quic_get_aead(ssl); + + if (wolfSSL_quic_aead_is_gcm(aead)) { + return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM; + } + if (wolfSSL_quic_aead_is_chacha20(aead)) { + return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305; + } + if (wolfSSL_quic_aead_is_ccm(aead)) { + return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM; + } + return 0; +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx, + void *tls_native_handle) { + WOLFSSL *ssl = tls_native_handle; + + ngtcp2_crypto_aead_init(&ctx->aead, (void *)wolfSSL_quic_get_aead(ssl)); + ctx->md.native_handle = (void *)wolfSSL_quic_get_md(ssl); + ctx->hp.native_handle = (void *)wolfSSL_quic_get_hp(ssl); + ctx->max_encryption = crypto_wolfssl_get_aead_max_encryption(ssl); + ctx->max_decryption_failure = + crypto_wolfssl_get_aead_max_decryption_failure(ssl); + return ctx; +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx, + void *tls_native_handle) { + return ngtcp2_crypto_ctx_tls(ctx, tls_native_handle); +} + +static size_t crypto_md_hashlen(const WOLFSSL_EVP_MD *md) { + return (size_t)wolfSSL_EVP_MD_size(md); +} + +size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md) { + return crypto_md_hashlen(md->native_handle); +} + +static size_t crypto_aead_keylen(const WOLFSSL_EVP_CIPHER *aead) { + return (size_t)wolfSSL_EVP_Cipher_key_length(aead); +} + +size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead) { + return crypto_aead_keylen(aead->native_handle); +} + +static size_t crypto_aead_noncelen(const WOLFSSL_EVP_CIPHER *aead) { + return (size_t)wolfSSL_EVP_CIPHER_iv_length(aead); +} + +size_t ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead) { + return crypto_aead_noncelen(aead->native_handle); +} + +int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, + const ngtcp2_crypto_aead *aead, + const uint8_t *key, size_t noncelen) { + const WOLFSSL_EVP_CIPHER *cipher = aead->native_handle; + WOLFSSL_EVP_CIPHER_CTX *actx; + static const uint8_t iv[AES_BLOCK_SIZE] = {0}; + + (void)noncelen; + actx = wolfSSL_quic_crypt_new(cipher, key, iv, /* encrypt */ 1); + if (actx == NULL) { + return -1; + } + + aead_ctx->native_handle = actx; + return 0; +} + +int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, + const ngtcp2_crypto_aead *aead, + const uint8_t *key, size_t noncelen) { + const WOLFSSL_EVP_CIPHER *cipher = aead->native_handle; + WOLFSSL_EVP_CIPHER_CTX *actx; + static const uint8_t iv[AES_BLOCK_SIZE] = {0}; + + (void)noncelen; + actx = wolfSSL_quic_crypt_new(cipher, key, iv, /* encrypt */ 0); + if (actx == NULL) { + return -1; + } + + aead_ctx->native_handle = actx; + return 0; +} + +void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) { + if (aead_ctx->native_handle) { + wolfSSL_EVP_CIPHER_CTX_free(aead_ctx->native_handle); + } +} + +int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx, + const ngtcp2_crypto_cipher *cipher, + const uint8_t *key) { + WOLFSSL_EVP_CIPHER_CTX *actx; + + actx = + wolfSSL_quic_crypt_new(cipher->native_handle, key, NULL, /* encrypt */ 1); + if (actx == NULL) { + return -1; + } + + cipher_ctx->native_handle = actx; + return 0; +} + +void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) { + if (cipher_ctx->native_handle) { + wolfSSL_EVP_CIPHER_CTX_free(cipher_ctx->native_handle); + } +} + +int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md, + const uint8_t *secret, size_t secretlen, + const uint8_t *salt, size_t saltlen) { + if (wolfSSL_quic_hkdf_extract(dest, md->native_handle, secret, secretlen, + salt, saltlen) != WOLFSSL_SUCCESS) { + return -1; + } + return 0; +} + +int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, const uint8_t *secret, + size_t secretlen, const uint8_t *info, + size_t infolen) { + if (wolfSSL_quic_hkdf_expand(dest, destlen, md->native_handle, secret, + secretlen, info, infolen) != WOLFSSL_SUCCESS) { + return -1; + } + return 0; +} + +int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, const uint8_t *secret, + size_t secretlen, const uint8_t *salt, size_t saltlen, + const uint8_t *info, size_t infolen) { + if (wolfSSL_quic_hkdf(dest, destlen, md->native_handle, secret, secretlen, + salt, saltlen, info, infolen) != WOLFSSL_SUCCESS) { + return -1; + } + return 0; +} + +int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *aad, size_t aadlen) { + (void)aead; + (void)noncelen; + if (wolfSSL_quic_aead_encrypt(dest, aead_ctx->native_handle, plaintext, + plaintextlen, nonce, aad, + aadlen) != WOLFSSL_SUCCESS) { + DEBUG_MSG("WOLFSSL: encrypt FAILED\n"); + return -1; + } + return 0; +} + +int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *aad, size_t aadlen) { + (void)aead; + (void)noncelen; + if (wolfSSL_quic_aead_decrypt(dest, aead_ctx->native_handle, ciphertext, + ciphertextlen, nonce, aad, + aadlen) != WOLFSSL_SUCCESS) { + + DEBUG_MSG("WOLFSSL: decrypt FAILED\n"); + return -1; + } + return 0; +} + +int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp, + const ngtcp2_crypto_cipher_ctx *hp_ctx, + const uint8_t *sample) { + static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00"; + WOLFSSL_EVP_CIPHER_CTX *actx = hp_ctx->native_handle; + int len; + + (void)hp; + + if (wolfSSL_EVP_EncryptInit_ex(actx, NULL, NULL, NULL, sample) != + WOLFSSL_SUCCESS || + wolfSSL_EVP_CipherUpdate(actx, dest, &len, PLAINTEXT, + sizeof(PLAINTEXT) - 1) != WOLFSSL_SUCCESS || + wolfSSL_EVP_EncryptFinal_ex(actx, dest + sizeof(PLAINTEXT) - 1, &len) != + WOLFSSL_SUCCESS) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + const uint8_t *data, size_t datalen) { + WOLFSSL *ssl = ngtcp2_conn_get_tls_native_handle(conn); + WOLFSSL_ENCRYPTION_LEVEL level = + ngtcp2_crypto_wolfssl_from_ngtcp2_crypto_level(crypto_level); + int rv; + int err; + + DEBUG_MSG("WOLFSSL: read/write crypto data, level=%d len=%lu\n", level, + datalen); + if (datalen > 0) { + rv = wolfSSL_provide_quic_data(ssl, level, data, datalen); + if (rv != WOLFSSL_SUCCESS) { + DEBUG_MSG("WOLFSSL: read/write crypto data FAILED, rv=%d\n", rv); + return -1; + } + } + + if (!ngtcp2_conn_get_handshake_completed(conn)) { + rv = wolfSSL_quic_do_handshake(ssl); + DEBUG_MSG("WOLFSSL: do_handshake, rv=%d\n", rv); + if (rv <= 0) { + err = wolfSSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + return 0; + case SSL_ERROR_SSL: + return -1; + default: + return -1; + } + } + + DEBUG_MSG("WOLFSSL: handshake done\n"); + ngtcp2_conn_handshake_completed(conn); + } + + rv = wolfSSL_process_quic_post_handshake(ssl); + DEBUG_MSG("WOLFSSL: process post handshake, rv=%d\n", rv); + if (rv != 1) { + err = wolfSSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + return 0; + case SSL_ERROR_SSL: + case SSL_ERROR_ZERO_RETURN: + return -1; + default: + return -1; + } + } + + return 0; +} + +int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) { + WOLFSSL *ssl = tls; + const uint8_t *tp; + size_t tplen; + int rv; + + wolfSSL_get_peer_quic_transport_params(ssl, &tp, &tplen); + DEBUG_MSG("WOLFSSL: get peer transport params, len=%lu\n", tplen); + + rv = ngtcp2_conn_decode_remote_transport_params(conn, tp, tplen); + if (rv != 0) { + DEBUG_MSG("WOLFSSL: decode peer transport params failed, rv=%d\n", rv); + ngtcp2_conn_set_tls_error(conn, rv); + return -1; + } + + return 0; +} + +int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf, + size_t len) { + WOLFSSL *ssl = tls; + DEBUG_MSG("WOLFSSL: set local peer transport params, len=%lu\n", len); + if (wolfSSL_set_quic_transport_params(ssl, buf, len) != WOLFSSL_SUCCESS) { + return -1; + } + + return 0; +} + +ngtcp2_crypto_level ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level( + WOLFSSL_ENCRYPTION_LEVEL wolfssl_level) { + switch (wolfssl_level) { + case wolfssl_encryption_initial: + return NGTCP2_CRYPTO_LEVEL_INITIAL; + case wolfssl_encryption_early_data: + return NGTCP2_CRYPTO_LEVEL_EARLY; + case wolfssl_encryption_handshake: + return NGTCP2_CRYPTO_LEVEL_HANDSHAKE; + case wolfssl_encryption_application: + return NGTCP2_CRYPTO_LEVEL_APPLICATION; + default: + assert(0); + abort(); /* if NDEBUG is set */ + } +} + +WOLFSSL_ENCRYPTION_LEVEL +ngtcp2_crypto_wolfssl_from_ngtcp2_crypto_level( + ngtcp2_crypto_level crypto_level) { + switch (crypto_level) { + case NGTCP2_CRYPTO_LEVEL_INITIAL: + return wolfssl_encryption_initial; + case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: + return wolfssl_encryption_handshake; + case NGTCP2_CRYPTO_LEVEL_APPLICATION: + return wolfssl_encryption_application; + case NGTCP2_CRYPTO_LEVEL_EARLY: + return wolfssl_encryption_early_data; + default: + assert(0); + abort(); /* if NDEBUG is set */ + } +} + +int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data, + void *user_data) { + (void)conn; + (void)user_data; + + DEBUG_MSG("WOLFSSL: get path challenge data\n"); + if (wolfSSL_RAND_bytes(data, NGTCP2_PATH_CHALLENGE_DATALEN) != 1) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + return 0; +} + +int ngtcp2_crypto_random(uint8_t *data, size_t datalen) { + DEBUG_MSG("WOLFSSL: get random\n"); + if (wolfSSL_RAND_bytes(data, (int)datalen) != 1) { + return -1; + } + return 0; +} + +static int set_encryption_secrets(WOLFSSL *ssl, + WOLFSSL_ENCRYPTION_LEVEL wolfssl_level, + const uint8_t *rx_secret, + const uint8_t *tx_secret, size_t secretlen) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + ngtcp2_crypto_level level = + ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level(wolfssl_level); + + DEBUG_MSG("WOLFSSL: set encryption secrets, level=%d, rxlen=%lu, txlen=%lu\n", + wolfssl_level, rx_secret ? secretlen : 0, + tx_secret ? secretlen : 0); + if (rx_secret && + ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level, + rx_secret, secretlen) != 0) { + return 0; + } + + if (tx_secret && + ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level, + tx_secret, secretlen) != 0) { + return 0; + } + + return 1; +} + +static int add_handshake_data(WOLFSSL *ssl, + WOLFSSL_ENCRYPTION_LEVEL wolfssl_level, + const uint8_t *data, size_t datalen) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + ngtcp2_crypto_level level = + ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level(wolfssl_level); + int rv; + + DEBUG_MSG("WOLFSSL: add handshake data, level=%d len=%lu\n", wolfssl_level, + datalen); + rv = ngtcp2_conn_submit_crypto_data(conn, level, data, datalen); + if (rv != 0) { + ngtcp2_conn_set_tls_error(conn, rv); + return 0; + } + + return 1; +} + +static int flush_flight(WOLFSSL *ssl) { + (void)ssl; + return 1; +} + +static int send_alert(WOLFSSL *ssl, enum wolfssl_encryption_level_t level, + uint8_t alert) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + (void)level; + + DEBUG_MSG("WOLFSSL: send alert, level=%d alert=%d\n", level, alert); + ngtcp2_conn_set_tls_alert(conn, alert); + + return 1; +} + +static WOLFSSL_QUIC_METHOD quic_method = { + set_encryption_secrets, + add_handshake_data, + flush_flight, + send_alert, +}; + +static void crypto_wolfssl_configure_context(WOLFSSL_CTX *ssl_ctx) { + wolfSSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION); + wolfSSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION); + wolfSSL_CTX_set_quic_method(ssl_ctx, &quic_method); +} + +int ngtcp2_crypto_wolfssl_configure_server_context(WOLFSSL_CTX *ssl_ctx) { + crypto_wolfssl_configure_context(ssl_ctx); + return 0; +} + +int ngtcp2_crypto_wolfssl_configure_client_context(WOLFSSL_CTX *ssl_ctx) { + crypto_wolfssl_configure_context(ssl_ctx); + wolfSSL_CTX_UseSessionTicket(ssl_ctx); + return 0; +} diff --git a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h index 8a37bebda6b095..ed71cb3ea0cb37 100644 --- a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h +++ b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h @@ -32,8 +32,9 @@ # define WIN32 #endif -#ifdef __cplusplus -extern "C" { +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4324) #endif #include @@ -49,10 +50,29 @@ extern "C" { #include #include -#ifdef WIN32 -# include +#ifndef NGTCP2_USE_GENERIC_SOCKADDR +# ifdef WIN32 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +# else +# include +# include +# endif +#endif + +#ifdef AF_INET +# define NGTCP2_AF_INET AF_INET #else -# include +# define NGTCP2_AF_INET 2 +#endif + +#ifdef AF_INET6 +# define NGTCP2_AF_INET6 AF_INET6 +#else +# define NGTCP2_AF_INET6 23 +# define NGTCP2_USE_GENERIC_IPV6_SOCKADDR #endif #include @@ -73,6 +93,16 @@ extern "C" { # endif /* !BUILDING_NGTCP2 */ #endif /* !defined(WIN32) */ +#ifdef _MSC_VER +# define NGTCP2_ALIGN(N) __declspec(align(N)) +#else /* !_MSC_VER */ +# define NGTCP2_ALIGN(N) __attribute__((aligned(N))) +#endif /* !_MSC_VER */ + +#ifdef __cplusplus +extern "C" { +#endif + /** * @typedef * @@ -83,58 +113,69 @@ typedef ptrdiff_t ngtcp2_ssize; /** * @functypedef * - * Custom memory allocator to replace malloc(). The |mem_user_data| - * is the mem_user_data member of :type:`ngtcp2_mem` structure. + * :type:`ngtcp2_malloc` is a custom memory allocator to replace + * :manpage:`malloc(3)`. The |user_data| is + * :member:`ngtcp2_mem.user_data`. */ -typedef void *(*ngtcp2_malloc)(size_t size, void *mem_user_data); +typedef void *(*ngtcp2_malloc)(size_t size, void *user_data); /** * @functypedef * - * Custom memory allocator to replace free(). The |mem_user_data| is - * the mem_user_data member of :type:`ngtcp2_mem` structure. + * :type:`ngtcp2_free` is a custom memory allocator to replace + * :manpage:`free(3)`. The |user_data| is + * :member:`ngtcp2_mem.user_data`. */ -typedef void (*ngtcp2_free)(void *ptr, void *mem_user_data); +typedef void (*ngtcp2_free)(void *ptr, void *user_data); /** * @functypedef * - * Custom memory allocator to replace calloc(). The |mem_user_data| - * is the mem_user_data member of :type:`ngtcp2_mem` structure. + * :type:`ngtcp2_calloc` is a custom memory allocator to replace + * :manpage:`calloc(3)`. The |user_data| is the + * :member:`ngtcp2_mem.user_data`. */ -typedef void *(*ngtcp2_calloc)(size_t nmemb, size_t size, void *mem_user_data); +typedef void *(*ngtcp2_calloc)(size_t nmemb, size_t size, void *user_data); /** * @functypedef * - * Custom memory allocator to replace realloc(). The |mem_user_data| - * is the mem_user_data member of :type:`ngtcp2_mem` structure. + * :type:`ngtcp2_realloc` is a custom memory allocator to replace + * :manpage:`realloc(3)`. The |user_data| is the + * :member:`ngtcp2_mem.user_data`. */ -typedef void *(*ngtcp2_realloc)(void *ptr, size_t size, void *mem_user_data); +typedef void *(*ngtcp2_realloc)(void *ptr, size_t size, void *user_data); /** * @struct * - * Custom memory allocator functions and user defined pointer. The - * |mem_user_data| member is passed to each allocator function. This - * can be used, for example, to achieve per-session memory pool. + * :type:`ngtcp2_mem` is a custom memory allocator. The + * :member:`user_data` field is passed to each allocator function. + * This can be used, for example, to achieve per-connection memory + * pool. * * In the following example code, ``my_malloc``, ``my_free``, * ``my_calloc`` and ``my_realloc`` are the replacement of the - * standard allocators ``malloc``, ``free``, ``calloc`` and - * ``realloc`` respectively:: + * standard allocators :manpage:`malloc(3)`, :manpage:`free(3)`, + * :manpage:`calloc(3)` and :manpage:`realloc(3)` respectively:: * - * void *my_malloc_cb(size_t size, void *mem_user_data) { + * void *my_malloc_cb(size_t size, void *user_data) { + * (void)user_data; * return my_malloc(size); * } * - * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } + * void my_free_cb(void *ptr, void *user_data) { + * (void)user_data; + * my_free(ptr); + * } * - * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { + * void *my_calloc_cb(size_t nmemb, size_t size, void *user_data) { + * (void)user_data; * return my_calloc(nmemb, size); * } * - * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { + * void *my_realloc_cb(void *ptr, size_t size, void *user_data) { + * (void)user_data; * return my_realloc(ptr, size); * } * @@ -147,24 +188,28 @@ typedef void *(*ngtcp2_realloc)(void *ptr, size_t size, void *mem_user_data); */ typedef struct ngtcp2_mem { /** - * An arbitrary user supplied data. This is passed to each - * allocator function. + * :member:`user_data` is an arbitrary user supplied data. This + * is passed to each allocator function. */ - void *mem_user_data; + void *user_data; /** - * Custom allocator function to replace malloc(). + * :member:`malloc` is a custom allocator function to replace + * :manpage:`malloc(3)`. */ ngtcp2_malloc malloc; /** - * Custom allocator function to replace free(). + * :member:`free` is a custom allocator function to replace + * :manpage:`free(3)`. */ ngtcp2_free free; /** - * Custom allocator function to replace calloc(). + * :member:`calloc` is a custom allocator function to replace + * :manpage:`calloc(3)`. */ ngtcp2_calloc calloc; /** - * Custom allocator function to replace realloc(). + * :member:`realloc` is a custom allocator function to replace + * :manpage:`realloc(3)`. */ ngtcp2_realloc realloc; } ngtcp2_mem; @@ -180,7 +225,7 @@ typedef struct ngtcp2_mem { * * :macro:`NGTCP2_SECONDS` is a count of tick which corresponds to 1 second. */ -#define NGTCP2_SECONDS ((uint64_t)1000000000ULL) +#define NGTCP2_SECONDS ((ngtcp2_duration)1000000000ULL) /** * @macro @@ -188,7 +233,7 @@ typedef struct ngtcp2_mem { * :macro:`NGTCP2_MILLISECONDS` is a count of tick which corresponds * to 1 millisecond. */ -#define NGTCP2_MILLISECONDS ((uint64_t)1000000ULL) +#define NGTCP2_MILLISECONDS ((ngtcp2_duration)1000000ULL) /** * @macro @@ -196,7 +241,7 @@ typedef struct ngtcp2_mem { * :macro:`NGTCP2_MICROSECONDS` is a count of tick which corresponds * to 1 microsecond. */ -#define NGTCP2_MICROSECONDS ((uint64_t)1000ULL) +#define NGTCP2_MICROSECONDS ((ngtcp2_duration)1000ULL) /** * @macro @@ -204,7 +249,7 @@ typedef struct ngtcp2_mem { * :macro:`NGTCP2_NANOSECONDS` is a count of tick which corresponds to * 1 nanosecond. */ -#define NGTCP2_NANOSECONDS ((uint64_t)1ULL) +#define NGTCP2_NANOSECONDS ((ngtcp2_duration)1ULL) /** * @macrosection @@ -217,7 +262,17 @@ typedef struct ngtcp2_mem { * * :macro:`NGTCP2_PROTO_VER_V1` is the QUIC version 1. */ -#define NGTCP2_PROTO_VER_V1 0x00000001u +#define NGTCP2_PROTO_VER_V1 ((uint32_t)0x00000001u) + +/** + * @macro + * + * :macro:`NGTCP2_PROTO_VER_V2_DRAFT` is the provisional version + * number for QUIC version 2 draft. + * + * https://quicwg.org/quic-v2/draft-ietf-quic-v2.html + */ +#define NGTCP2_PROTO_VER_V2_DRAFT ((uint32_t)0x709a50c4u) /** * @macro @@ -251,43 +306,35 @@ typedef struct ngtcp2_mem { */ #define NGTCP2_PROTO_VER_MIN NGTCP2_PROTO_VER_DRAFT_MIN -/** - * @macrosection - * - * IP packet related macros - */ - /** * @macro * - * :macro:`NGTCP2_MAX_PKTLEN_IPV4` is the maximum datagram size of - * IPv4 packet without PMTUD. + * :macro:`NGTCP2_RESERVED_VERSION_MASK` is the bit mask of reserved + * version. */ -#define NGTCP2_MAX_PKTLEN_IPV4 1252 +#define NGTCP2_RESERVED_VERSION_MASK 0x0a0a0a0au + /** - * @macro + * @macrosection * - * :macro:`NGTCP2_MAX_PKTLEN_IPV6` is the maximum datagram size of - * IPv6 packet without PMTUD. + * UDP datagram related macros */ -#define NGTCP2_MAX_PKTLEN_IPV6 1232 /** * @macro * - * :macro:`NGTCP2_MIN_INITIAL_PKTLEN` is the minimum datagram size for - * a packet sent by client which contains its first Initial packet. + * :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE` is the default maximum UDP + * datagram payload size that this endpoint transmits. */ -#define NGTCP2_MIN_INITIAL_PKTLEN 1200 +#define NGTCP2_MAX_UDP_PAYLOAD_SIZE 1200 /** * @macro * - * :macro:`NGTCP2_DEFAULT_MAX_PKTLEN` is the default maximum datagram - * size that this endpoint transmits. It is used by congestion - * controller to compute congestion window. + * :macro:`NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE` is the maximum UDP + * datagram payload size that Path MTU Discovery can discover. */ -#define NGTCP2_DEFAULT_MAX_PKTLEN 1200 +#define NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE 1452 /** * @macrosection @@ -315,10 +362,18 @@ typedef struct ngtcp2_mem { * @macro * * :macro:`NGTCP2_MIN_STATELESS_RESET_RANDLEN` is the minimum length - * of random bytes (Unpredictable Bits) in Stateless Retry packet + * of random bytes (Unpredictable Bits) in Stateless Reset packet */ #define NGTCP2_MIN_STATELESS_RESET_RANDLEN 5 +/** + * @macro + * + * :macro:`NGTCP2_PATH_CHALLENGE_DATALEN` is the length of + * PATH_CHALLENGE data. + */ +#define NGTCP2_PATH_CHALLENGE_DATALEN 8 + /** * @macro * @@ -354,6 +409,28 @@ typedef struct ngtcp2_mem { */ #define NGTCP2_RETRY_NONCE_V1 "\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb" +/** + * @macro + * + * :macro:`NGTCP2_RETRY_KEY_V2_DRAFT` is an encryption key to create + * integrity tag of Retry packet. It is used for QUIC v2 draft. + * + * https://quicwg.org/quic-v2/draft-ietf-quic-v2.html + */ +#define NGTCP2_RETRY_KEY_V2_DRAFT \ + "\xba\x85\x8d\xc7\xb4\x3d\xe5\xdb\xf8\x76\x17\xff\x4a\xb2\x53\xdb" + +/** + * @macro + * + * :macro:`NGTCP2_RETRY_NONCE_V2_DRAFT` is nonce used when generating + * integrity tag of Retry packet. It is used for QUIC v2 draft. + * + * https://quicwg.org/quic-v2/draft-ietf-quic-v2.html + */ +#define NGTCP2_RETRY_NONCE_V2_DRAFT \ + "\x14\x1b\x99\xc2\x39\xb0\x3e\x78\x5d\x6a\x2e\x9f" + /** * @macro * @@ -399,6 +476,14 @@ typedef struct ngtcp2_mem { */ #define NGTCP2_MIN_INITIAL_DCIDLEN 8 +/** + * @macro + * + * :macro:`NGTCP2_DEFAULT_HANDSHAKE_TIMEOUT` is the default handshake + * timeout. + */ +#define NGTCP2_DEFAULT_HANDSHAKE_TIMEOUT (10 * NGTCP2_SECONDS) + /** * @macrosection * @@ -440,15 +525,18 @@ typedef struct ngtcp2_mem { */ #define NGTCP2_ECN_MASK 0x3 +#define NGTCP2_PKT_INFO_VERSION_V1 1 +#define NGTCP2_PKT_INFO_VERSION NGTCP2_PKT_INFO_VERSION_V1 + /** * @struct * * :type:`ngtcp2_pkt_info` is a packet metadata. */ -typedef struct ngtcp2_pkt_info { +typedef struct NGTCP2_ALIGN(8) ngtcp2_pkt_info { /** - * :member:`ecn ` is ECN marking and when - * passing `ngtcp2_conn_read_pkt()`, and it should be either + * :member:`ecn` is ECN marking and when passing + * `ngtcp2_conn_read_pkt()`, and it should be either * :macro:`NGTCP2_ECN_NOT_ECT`, :macro:`NGTCP2_ECN_ECT_1`, * :macro:`NGTCP2_ECN_ECT_0`, or :macro:`NGTCP2_ECN_CE`. */ @@ -580,9 +668,9 @@ typedef struct ngtcp2_pkt_info { /** * @macro * - * :macro:`NGTCP2_ERR_TLS_DECRYPT` indicates TLS decryption failure. + * :macro:`NGTCP2_ERR_DECRYPT` indicates a decryption failure. */ -#define NGTCP2_ERR_TLS_DECRYPT -220 +#define NGTCP2_ERR_DECRYPT -220 /** * @macro * @@ -638,13 +726,6 @@ typedef struct ngtcp2_pkt_info { * :macro:`NGTCP2_ERR_DISCARD_PKT` indicates a packet was discarded. */ #define NGTCP2_ERR_DISCARD_PKT -235 -/** - * @macro - * - * :macro:`NGTCP2_ERR_PATH_VALIDATION_FAILED` indicates that a path - * validation failed. - */ -#define NGTCP2_ERR_PATH_VALIDATION_FAILED -236 /** * @macro * @@ -699,9 +780,38 @@ typedef struct ngtcp2_pkt_info { * @macro * * :macro:`NGTCP2_ERR_NO_VIABLE_PATH` indicates that path validation - * could not probe that a path is not capable of at least 1200 MTU. + * could not probe that a path is capable of sending UDP datagram + * payload of size at least 1200 bytes. */ #define NGTCP2_ERR_NO_VIABLE_PATH -244 +/** + * @macro + * + * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION` indicates that server + * should send Version Negotiation packet. + */ +#define NGTCP2_ERR_VERSION_NEGOTIATION -245 +/** + * @macro + * + * :macro:`NGTCP2_ERR_HANDSHAKE_TIMEOUT` indicates that QUIC + * connection is not established before the specified deadline. + */ +#define NGTCP2_ERR_HANDSHAKE_TIMEOUT -246 +/** + * @macro + * + * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE` indicates the + * version negotiation failed. + */ +#define NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE -247 +/** + * @macro + * + * :macro:`NGTCP2_ERR_IDLE_CLOSE` indicates the connection should be + * closed silently because of idle timeout. + */ +#define NGTCP2_ERR_IDLE_CLOSE -248 /** * @macro * @@ -735,54 +845,68 @@ typedef struct ngtcp2_pkt_info { * * :macro:`NGTCP2_PKT_FLAG_NONE` indicates no flag set. */ -#define NGTCP2_PKT_FLAG_NONE 0 +#define NGTCP2_PKT_FLAG_NONE 0x00u /** * @macro * - * :macro:`NGTCP2_PKT_FLAG_LONG_FORM` indicates the Long packet + * :macro:`NGTCP2_PKT_FLAG_LONG_FORM` indicates the Long header packet * header. */ -#define NGTCP2_PKT_FLAG_LONG_FORM 0x01 +#define NGTCP2_PKT_FLAG_LONG_FORM 0x01u + +/** + * @macro + * + * :macro:`NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR` indicates that Fixed Bit + * (aka QUIC bit) is not set. + */ +#define NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR 0x02u /** * @macro * * :macro:`NGTCP2_PKT_FLAG_KEY_PHASE` indicates Key Phase bit set. */ -#define NGTCP2_PKT_FLAG_KEY_PHASE 0x04 +#define NGTCP2_PKT_FLAG_KEY_PHASE 0x04u /** * @enum * - * :type:`ngtcp2_pkt_type` defines QUIC packet types. + * :type:`ngtcp2_pkt_type` defines QUIC version-independent QUIC + * packet types. */ typedef enum ngtcp2_pkt_type { /** * :enum:`NGTCP2_PKT_VERSION_NEGOTIATION` is defined by libngtcp2 * for convenience. */ - NGTCP2_PKT_VERSION_NEGOTIATION = 0xf0, + NGTCP2_PKT_VERSION_NEGOTIATION = 0x80, + /** + * :enum:`NGTCP2_PKT_STATELESS_RESET` is defined by libngtcp2 for + * convenience. + */ + NGTCP2_PKT_STATELESS_RESET = 0x81, /** * :enum:`NGTCP2_PKT_INITIAL` indicates Initial packet. */ - NGTCP2_PKT_INITIAL = 0x0, + NGTCP2_PKT_INITIAL = 0x10, /** * :enum:`NGTCP2_PKT_0RTT` indicates 0RTT packet. */ - NGTCP2_PKT_0RTT = 0x1, + NGTCP2_PKT_0RTT = 0x11, /** * :enum:`NGTCP2_PKT_HANDSHAKE` indicates Handshake packet. */ - NGTCP2_PKT_HANDSHAKE = 0x2, + NGTCP2_PKT_HANDSHAKE = 0x12, /** * :enum:`NGTCP2_PKT_RETRY` indicates Retry packet. */ - NGTCP2_PKT_RETRY = 0x3, + NGTCP2_PKT_RETRY = 0x13, /** - * :enum:`NGTCP2_PKT_SHORT` is defined by libngtcp2 for convenience. + * :enum:`NGTCP2_PKT_1RTT` is defined by libngtcp2 for convenience. */ - NGTCP2_PKT_SHORT = 0x70 + NGTCP2_PKT_1RTT = 0x40 } ngtcp2_pkt_type; /** @@ -934,6 +1058,16 @@ typedef enum ngtcp2_pkt_type { */ #define NGTCP2_CRYPTO_ERROR 0x100u +/** + * @macro + * + * :macro:`NGTCP2_VERSION_NEGOTIATION_ERROR_DRAFT` is QUIC transport + * error code ``VERSION_NEGOTIATION_ERROR``. + * + * https://quicwg.org/quic-v2/draft-ietf-quic-v2.html + */ +#define NGTCP2_VERSION_NEGOTIATION_ERROR_DRAFT 0x53f8u + /** * @enum * @@ -950,7 +1084,12 @@ typedef enum ngtcp2_path_validation_result { * :enum:`NGTCP2_PATH_VALIDATION_RESULT_FAILURE` indicates * validation failure. */ - NGTCP2_PATH_VALIDATION_RESULT_FAILURE + NGTCP2_PATH_VALIDATION_RESULT_FAILURE, + /** + * :enum:`NGTCP2_PATH_VALIDATION_RESULT_ABORTED` indicates that path + * validation was aborted. + */ + NGTCP2_PATH_VALIDATION_RESULT_ABORTED } ngtcp2_path_validation_result; /** @@ -975,13 +1114,11 @@ typedef uint64_t ngtcp2_duration; */ typedef struct ngtcp2_cid { /** - * :member:`datalen ` is the length of - * Connection ID. + * :member:`datalen` is the length of Connection ID. */ size_t datalen; /** - * :member:`data ` is the buffer to store - * Connection ID. + * :member:`data` is the buffer to store Connection ID. */ uint8_t data[NGTCP2_MAX_CIDLEN]; } ngtcp2_cid; @@ -994,12 +1131,12 @@ typedef struct ngtcp2_cid { */ typedef struct ngtcp2_vec { /** - * :member:`base ` points to the data. + * :member:`base` points to the data. */ uint8_t *base; /** - * :member:`len ` is the number of bytes which the - * buffer pointed by base contains. + * :member:`len` is the number of bytes which the buffer pointed by + * base contains. */ size_t len; } ngtcp2_vec; @@ -1009,12 +1146,19 @@ typedef struct ngtcp2_vec { * * `ngtcp2_cid_init` initializes Connection ID |cid| with the byte * string pointed by |data| and its length is |datalen|. |datalen| - * must be at least :macro:`NGTCP2_MIN_CIDLEN`, and at most - * :macro:`NGTCP2_MAX_CIDLEN`. + * must be at most :macro:`NGTCP2_MAX_CIDLEN`. */ NGTCP2_EXTERN void ngtcp2_cid_init(ngtcp2_cid *cid, const uint8_t *data, size_t datalen); +/** + * @function + * + * `ngtcp2_cid_eq` returns nonzero if |a| and |b| share the same + * Connection ID. + */ +NGTCP2_EXTERN int ngtcp2_cid_eq(const ngtcp2_cid *a, const ngtcp2_cid *b); + /** * @struct * @@ -1058,8 +1202,8 @@ typedef struct ngtcp2_pkt_hd { */ uint8_t type; /** - * :member:`flags` is zero or more of NGTCP2_PKT_FLAG_*. See - * :macro:`NGTCP2_PKT_FLAG_NONE`. + * :member:`flags` is zero or more of :macro:`NGTCP2_PKT_FLAG_* + * `. */ uint8_t flags; } ngtcp2_pkt_hd; @@ -1085,27 +1229,6 @@ typedef struct ngtcp2_pkt_stateless_reset { size_t randlen; } ngtcp2_pkt_stateless_reset; -typedef enum ngtcp2_transport_param_id { - NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID = 0x0000, - NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT = 0x0001, - NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN = 0x0002, - NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE = 0x0003, - NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA = 0x0004, - NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL = 0x0005, - NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE = 0x0006, - NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI = 0x0007, - NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI = 0x0008, - NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI = 0x0009, - NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT = 0x000a, - NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY = 0x000b, - NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION = 0x000c, - NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS = 0x000d, - NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT = 0x000e, - NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID = 0x000f, - NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID = 0x0010, - NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE = 0x0020 -} ngtcp2_transport_param_id; - /** * @enum * @@ -1125,20 +1248,6 @@ typedef enum ngtcp2_transport_params_type { NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS } ngtcp2_transport_params_type; -/** - * @enum - * - * ngtcp2_rand_usage describes the usage of the generated random data. - */ -typedef enum ngtcp2_rand_usage { - NGTCP2_RAND_USAGE_NONE, - /** - * :enum:`NGTCP2_RAND_USAGE_PATH_CHALLENGE` indicates that random - * value is used for PATH_CHALLENGE. - */ - NGTCP2_RAND_USAGE_PATH_CHALLENGE -} ngtcp2_rand_usage; - /** * @macrosection * @@ -1148,10 +1257,10 @@ typedef enum ngtcp2_rand_usage { /** * @macro * - * :macro:`NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE` is the default value - * of max_udp_payload_size transport parameter. + * :macro:`NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE` is the default + * value of max_udp_payload_size transport parameter. */ -#define NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE 65527 +#define NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE 65527 /** * @macro @@ -1182,10 +1291,19 @@ typedef enum ngtcp2_rand_usage { /** * @macro * - * :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS` is TLS extension - * type of quic_transport_parameters. + * :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1` is TLS + * extension type of quic_transport_parameters. + */ +#define NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1 0x39u + +/** + * @macro + * + * :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_DRAFT` is TLS + * extension type of quic_transport_parameters used during draft + * development. */ -#define NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS 0xffa5u +#define NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_DRAFT 0xffa5u /** * @struct @@ -1214,12 +1332,48 @@ typedef struct ngtcp2_preferred_addr { * :member:`ipv6_addr` contains IPv6 address in network byte order. */ uint8_t ipv6_addr[16]; + /** + * :member:`ipv4_present` indicates that :member:`ipv4_addr` and + * :member:`ipv4_port` contain IPv4 address and port respectively. + */ + uint8_t ipv4_present; + /** + * :member:`ipv6_present` indicates that :member:`ipv6_addr` and + * :member:`ipv6_port` contain IPv6 address and port respectively. + */ + uint8_t ipv6_present; /** * :member:`stateless_reset_token` contains stateless reset token. */ uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN]; } ngtcp2_preferred_addr; +/** + * @struct + * + * :type:`ngtcp2_version_info` represents version_information + * structure. + */ +typedef struct ngtcp2_version_info { + /** + * :member:`chosen_version` is the version chosen by the sender. + */ + uint32_t chosen_version; + /** + * :member:`other_versions` points the wire image of other_versions + * field. The each version is therefore in network byte order. + */ + uint8_t *other_versions; + /** + * :member:`other_versionslen` is the number of bytes pointed by + * :member:`other_versions`, not the number of versions included. + */ + size_t other_versionslen; +} ngtcp2_version_info; + +#define NGTCP2_TRANSPORT_PARAMS_VERSION_V1 1 +#define NGTCP2_TRANSPORT_PARAMS_VERSION NGTCP2_TRANSPORT_PARAMS_VERSION_V1 + /** * @struct * @@ -1324,7 +1478,7 @@ typedef struct ngtcp2_transport_params { * :member:`max_datagram_frame_size` is the maximum size of DATAGRAM * frame that this endpoint willingly receives. Specifying 0 * disables DATAGRAM support. See - * https://tools.ietf.org/html/draft-ietf-quic-datagram-01 + * https://datatracker.ietf.org/doc/html/rfc9221 */ uint64_t max_datagram_frame_size; /** @@ -1351,15 +1505,28 @@ typedef struct ngtcp2_transport_params { * :member:`stateless_reset_token` contains stateless reset token. */ uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN]; + /** + * :member:`grease_quic_bit` is nonzero if sender supports "Greasing + * the QUIC Bit" extension. See + * https://datatracker.ietf.org/doc/html/draft-ietf-quic-bit-grease. + * Note that the local endpoint always enables greasing QUIC bit + * regardless of this field value. + */ + uint8_t grease_quic_bit; + /** + * :member:`version_info` contains version_information field if + * :member:`version_info_present` is nonzero. Application should + * not specify this field. + */ + ngtcp2_version_info version_info; + /** + * :member:`version_info_present` is nonzero if + * :member:`version_info` is set. Application should not specify + * this field. + */ + uint8_t version_info_present; } ngtcp2_transport_params; -/** - * @struct - * - * :type:`ngtcp2_log` is ngtcp2 library internal logger. - */ -typedef struct ngtcp2_log ngtcp2_log; - /** * @enum * @@ -1388,6 +1555,9 @@ typedef enum ngtcp2_pktns_id { NGTCP2_PKTNS_ID_MAX } ngtcp2_pktns_id; +#define NGTCP2_CONN_STAT_VERSION_V1 1 +#define NGTCP2_CONN_STAT_VERSION NGTCP2_CONN_STAT_VERSION_V1 + /** * @struct * @@ -1435,13 +1605,11 @@ typedef struct ngtcp2_conn_stat { ngtcp2_tstamp loss_detection_timer; /** * :member:`last_tx_pkt_ts` corresponds to - * time_of_last_sent_ack_eliciting_packet in - * draft-ietf-quic-recovery-32. + * time_of_last_ack_eliciting_packet in :rfc:`9002`. */ ngtcp2_tstamp last_tx_pkt_ts[NGTCP2_PKTNS_ID_MAX]; /** - * :member:`loss_time` corresponds to loss_time in - * draft-ietf-quic-recovery-32. + * :member:`loss_time` corresponds to loss_time in :rfc:`9002`. */ ngtcp2_tstamp loss_time[NGTCP2_PKTNS_ID_MAX]; /** @@ -1473,6 +1641,16 @@ typedef struct ngtcp2_conn_stat { * in byte per second. */ uint64_t delivery_rate_sec; + /** + * :member:`pacing_rate` is the current packet sending rate. If + * pacing is disabled, 0 is set. + */ + double pacing_rate; + /** + * :member:`send_quantum` is the maximum size of a data aggregate + * scheduled and transmitted together. + */ + size_t send_quantum; } ngtcp2_conn_stat; /** @@ -1490,195 +1668,16 @@ typedef enum ngtcp2_cc_algo { */ NGTCP2_CC_ALGO_CUBIC = 0x01, /** - * :enum:`NGTCP2_CC_ALGO_CUSTOM` represents custom congestion - * control algorithm. - */ - NGTCP2_CC_ALGO_CUSTOM = 0xff -} ngtcp2_cc_algo; - -/** - * @struct - * - * :type:`ngtcp2_cc_base` is the base structure of custom congestion - * control algorithm. It must be the first field of custom congestion - * controller. - */ -typedef struct ngtcp2_cc_base { - /** - * :member:`log` is ngtcp2 library internal logger. - */ - ngtcp2_log *log; -} ngtcp2_cc_base; - -/** - * @struct - * - * :type:`ngtcp2_cc_pkt` is a convenient structure to include - * acked/lost/sent packet. - */ -typedef struct ngtcp2_cc_pkt { - /** - * :member:`pkt_num` is the packet number - */ - int64_t pkt_num; - /** - * :member:`pktlen` is the length of packet. - */ - size_t pktlen; - /** - * :member:`pktns_id` is the ID of packet number space which this - * packet belongs to. - */ - ngtcp2_pktns_id pktns_id; - /** - * :member:`ts_sent` is the timestamp when packet is sent. - */ - ngtcp2_tstamp ts_sent; -} ngtcp2_cc_pkt; - -typedef struct ngtcp2_cc ngtcp2_cc; - -/** - * @functypedef - * - * :type:`ngtcp2_cc_on_pkt_acked` is a callback function which is - * called with an acknowledged packet. - */ -typedef void (*ngtcp2_cc_on_pkt_acked)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - const ngtcp2_cc_pkt *pkt, - ngtcp2_tstamp ts); - -/** - * @functypedef - * - * :type:`ngtcp2_cc_congestion_event` is a callback function which is - * called when congestion event happens (e.g., when packet is lost). - */ -typedef void (*ngtcp2_cc_congestion_event)(ngtcp2_cc *cc, - ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts_sent, - ngtcp2_tstamp ts); - -/** - * @functypedef - * - * :type:`ngtcp2_cc_on_persistent_congestion` is a callback function - * which is called when persistent congestion is established. - */ -typedef void (*ngtcp2_cc_on_persistent_congestion)(ngtcp2_cc *cc, - ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts); - -/** - * @functypedef - * - * :type:`ngtcp2_cc_on_ack_recv` is a callback function which is - * called when an acknowledgement is received. - */ -typedef void (*ngtcp2_cc_on_ack_recv)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts); - -/** - * @functypedef - * - * :type:`ngtcp2_cc_on_pkt_sent` is a callback function which is - * called when an ack-eliciting packet is sent. - */ -typedef void (*ngtcp2_cc_on_pkt_sent)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - const ngtcp2_cc_pkt *pkt); - -/** - * @functypedef - * - * :type:`ngtcp2_cc_new_rtt_sample` is a callback function which is - * called when new RTT sample is obtained. - */ -typedef void (*ngtcp2_cc_new_rtt_sample)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts); - -/** - * @functypedef - * - * :type:`ngtcp2_cc_reset` is a callback function which is called when - * congestion state must be reset. - */ -typedef void (*ngtcp2_cc_reset)(ngtcp2_cc *cc); - -/** - * @enum - * - * :type:`ngtcp2_cc_event_type` defines congestion control events. - */ -typedef enum ngtcp2_cc_event_type { - /** - * :enum:`NGTCP2_CC_EVENT_TX_START` occurs when ack-eliciting packet - * is sent and no other ack-eliciting packet is present. - */ - NGTCP2_CC_EVENT_TYPE_TX_START -} ngtcp2_cc_event_type; - -/** - * @functypedef - * - * :type:`ngtcp2_cc_event` is a callback function which is called when - * a specific event happens. - */ -typedef void (*ngtcp2_cc_event)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - ngtcp2_cc_event_type event, ngtcp2_tstamp ts); - -/** - * @struct - * - * :type:`ngtcp2_cc` is congestion control algorithm interface to - * allow custom implementation. - */ -typedef struct ngtcp2_cc { - /** - * :member:`ccb` is a pointer to :type:`ngtcp2_cc_base` which - * usually contains a state. - */ - ngtcp2_cc_base *ccb; - /** - * :member:`on_pkt_acked` is a callback function which is called - * when a packet is acknowledged. - */ - ngtcp2_cc_on_pkt_acked on_pkt_acked; - /** - * :member:`congestion_event` is a callback function which is called - * when congestion event happens (.e.g, packet is lost). - */ - ngtcp2_cc_congestion_event congestion_event; - /** - * :member:`on_persistent_congestion` is a callback function which - * is called when persistent congestion is established. - */ - ngtcp2_cc_on_persistent_congestion on_persistent_congestion; - /** - * :member:`on_ack_recv` is a callback function which is called when - * an acknowledgement is received. - */ - ngtcp2_cc_on_ack_recv on_ack_recv; - /** - * :member:`on_pkt_sent` is a callback function which is called when - * ack-eliciting packet is sent. + * :enum:`NGTCP2_CC_ALGO_BBR` represents BBR. If BBR is chosen, + * packet pacing is enabled. */ - ngtcp2_cc_on_pkt_sent on_pkt_sent; + NGTCP2_CC_ALGO_BBR = 0x02, /** - * :member:`new_rtt_sample` is a callback function which is called - * when new RTT sample is obtained. + * :enum:`NGTCP2_CC_ALGO_BBR2` represents BBR v2. If BBR v2 is + * chosen, packet pacing is enabled. */ - ngtcp2_cc_new_rtt_sample new_rtt_sample; - /** - * :member:`reset` is a callback function which is called when - * congestion control state must be reset. - */ - ngtcp2_cc_reset reset; - /** - * :member:`event` is a callback function which is called when a - * specific event happens. - */ - ngtcp2_cc_event event; -} ngtcp2_cc; + NGTCP2_CC_ALGO_BBR2 = 0x03 +} ngtcp2_cc_algo; /** * @functypedef @@ -1700,14 +1699,14 @@ typedef void (*ngtcp2_printf)(void *user_data, const char *format, ...); * * :macro:`NGTCP2_QLOG_WRITE_FLAG_NONE` indicates no flag set. */ -#define NGTCP2_QLOG_WRITE_FLAG_NONE 0 +#define NGTCP2_QLOG_WRITE_FLAG_NONE 0x00u /** * @macro * * :macro:`NGTCP2_QLOG_WRITE_FLAG_FIN` indicates that this is the * final call to :type:`ngtcp2_qlog_write` in the current connection. */ -#define NGTCP2_QLOG_WRITE_FLAG_FIN 0x01 +#define NGTCP2_QLOG_WRITE_FLAG_FIN 0x01u /** * @struct @@ -1730,8 +1729,8 @@ typedef struct ngtcp2_rand_ctx { * * :type:`ngtcp2_qlog_write` is a callback function which is called to * write qlog |data| of length |datalen| bytes. |flags| is bitwise OR - * of zero or more of NGTCP2_QLOG_WRITE_FLAG_*. See - * :macro:`NGTCP2_QLOG_WRITE_FLAG_NONE`. If + * of zero or more of :macro:`NGTCP2_QLOG_WRITE_FLAG_* + * `. If * :macro:`NGTCP2_QLOG_WRITE_FLAG_FIN` is set, |datalen| may be 0. */ typedef void (*ngtcp2_qlog_write)(void *user_data, uint32_t flags, @@ -1757,6 +1756,9 @@ typedef struct ngtcp2_qlog_settings { ngtcp2_qlog_write write; } ngtcp2_qlog_settings; +#define NGTCP2_SETTINGS_VERSION_V1 1 +#define NGTCP2_SETTINGS_VERSION NGTCP2_SETTINGS_VERSION_V1 + /** * @struct * @@ -1768,18 +1770,9 @@ typedef struct ngtcp2_settings { */ ngtcp2_qlog_settings qlog; /** - * :member:`cc_algo` specifies congestion control algorithm. If - * :enum:`ngtcp2_cc_algo.NGTCP2_CC_ALGO_CUSTOM` is set, :member:`cc` - * must be set to a pointer to custom congestion control algorithm. + * :member:`cc_algo` specifies congestion control algorithm. */ ngtcp2_cc_algo cc_algo; - /** - * :member:`cc` is a pointer to custom congestion control algorithm. - * :member:`cc_algo` must be set to - * :enum:`ngtcp2_cc_algo.NGTCP2_CC_ALGO_CUSTOM` in order to enable - * custom congestion control algorithm. - */ - ngtcp2_cc *cc; /** * :member:`initial_ts` is an initial timestamp given to the * library. @@ -1798,8 +1791,7 @@ typedef struct ngtcp2_settings { /** * :member:`max_udp_payload_size` is the maximum size of UDP * datagram payload that this endpoint transmits. It is used by - * congestion controller to compute congestion window. If it is set - * to 0, it defaults to :macro:`NGTCP2_DEFAULT_MAX_PKTLEN`. + * congestion controller to compute congestion window. */ size_t max_udp_payload_size; /** @@ -1847,33 +1839,193 @@ typedef struct ngtcp2_settings { * immediate acknowledgement. */ size_t ack_thresh; -} ngtcp2_settings; - -/** - * @struct - * - * :type:`ngtcp2_addr` is the endpoint address. - */ -typedef struct ngtcp2_addr { /** - * :member:`addrlen` is the length of addr. + * :member:`no_udp_payload_size_shaping`, if set to nonzero, + * instructs the library not to limit the UDP payload size to + * :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE` (which can be extended by + * Path MTU Discovery) and instead use the mininum size among the + * given buffer size, :member:`max_udp_payload_size`, and the + * received max_udp_payload QUIC transport parameter. + */ + int no_udp_payload_size_shaping; + /** + * :member:`handshake_timeout` is the period of time before giving + * up QUIC connection establishment. If QUIC handshake is not + * complete within this period, `ngtcp2_conn_handle_expiry` returns + * :macro:`NGTCP2_ERR_HANDSHAKE_TIMEOUT` error. The deadline is + * :member:`initial_ts` + :member:`handshake_timeout`. If this + * field is set to ``UINT64_MAX``, no handshake timeout is set. + */ + ngtcp2_duration handshake_timeout; + /** + * :member:`preferred_versions` is the array of versions that are + * preferred by the local endpoint. All versions set in this array + * must be supported by the library, and compatible to QUIC v1. The + * reserved versions are not allowed. They are sorted in the order + * of preference. + * + * On compatible version negotiation, server will negotiate one of + * those versions contained in this array if a client initially + * chooses a less preferred version. This version set corresponds + * to Offered Versions in QUIC Version Negotiation draft, and it should + * be sent in Version Negotiation packet. + * + * Client uses this field and :member:`original_version` to prevent + * version downgrade attack if it reacted upon Version Negotiation + * packet. If this field is specified, client must include + * |client_chosen_version| passed to `ngtcp2_conn_client_new` unless + * |client_chosen_version| is a reserved version. */ - size_t addrlen; + uint32_t *preferred_versions; /** - * :member:`addr` points to the buffer which contains endpoint - * address. It must not be ``NULL``. + * :member:`preferred_versionslen` is the number of versions that + * are contained in the array pointed by + * :member:`preferred_versions`. */ - struct sockaddr *addr; + size_t preferred_versionslen; /** - * :member:`user_data` is an arbitrary data and opaque to the - * library. + * :member:`other_versions` is the array of versions that are set in + * :member:`other_versions ` + * field of outgoing version_information QUIC transport parameter. + * + * For server, this corresponds to Fully-Deployed Versions in QUIC + * Version Negotiation draft. If this field is set not, it is set + * to :member:`preferred_versions` internally if + * :member:`preferred_versionslen` is not zero. If this field is + * not set, and :member:`preferred_versionslen` is zero, this field + * is set to :macro:`NGTCP2_PROTO_VER_V1` internally. + * + * Client must include |client_chosen_version| passed to + * `ngtcp2_conn_client_new` in this array if this field is set and + * |client_chosen_version| is not a reserved version. If this field + * is not set, |client_chosen_version| passed to + * `ngtcp2_conn_client_new` will be set in this field internally + * unless |client_chosen_version| is a reserved version. */ - void *user_data; -} ngtcp2_addr; - -/** - * @struct - * + uint32_t *other_versions; + /** + * :member:`other_versionslen` is the number of versions that are + * contained in the array pointed by :member:`other_versions`. + */ + size_t other_versionslen; + /** + * :member:`original_version` is the original version that client + * initially used to make a connection attempt. If it is set, and + * it differs from |client_chosen_version| passed to + * `ngtcp2_conn_client_new`, the library assumes that client reacted + * upon Version Negotiation packet. Server does not use this field. + */ + uint32_t original_version; + /** + * :member:`no_pmtud`, if set to nonzero, disables Path MTU + * Discovery. + */ + int no_pmtud; +} ngtcp2_settings; + +#ifdef NGTCP2_USE_GENERIC_SOCKADDR +typedef struct ngtcp2_sockaddr { + uint16_t sa_family; + uint8_t sa_data[14]; +} ngtcp2_sockaddr; + +typedef struct ngtcp2_in_addr { + uint32_t s_addr; +} ngtcp2_in_addr; + +typedef struct ngtcp2_sockaddr_in { + uint16_t sin_family; + uint16_t sin_port; + ngtcp2_in_addr sin_addr; + uint8_t sin_zero[8]; +} ngtcp2_sockaddr_in; + +# define NGTCP2_SS_MAXSIZE 128 +# define NGTCP2_SS_ALIGNSIZE (sizeof(uint64_t)) +# define NGTCP2_SS_PAD1SIZE (NGTCP2_SS_ALIGNSIZE - sizeof(uint16_t)) +# define NGTCP2_SS_PAD2SIZE \ + (NGTCP2_SS_MAXSIZE - \ + (sizeof(uint16_t) + NGTCP2_SS_PAD1SIZE + NGTCP2_SS_ALIGNSIZE)) + +typedef struct ngtcp2_sockaddr_storage { + uint16_t ss_family; + uint8_t _ss_pad1[NGTCP2_SS_PAD1SIZE]; + uint64_t _ss_align; + uint8_t _ss_pad2[NGTCP2_SS_PAD2SIZE]; +} ngtcp2_sockaddr_storage; + +# undef NGTCP2_SS_PAD2SIZE +# undef NGTCP2_SS_PAD1SIZE +# undef NGTCP2_SS_ALIGNSIZE +# undef NGTCP2_SS_MAXSIZE + +typedef uint32_t ngtcp2_socklen; +#else +/** + * @typedef + * + * :type:`ngtcp2_sockaddr` is typedefed to struct sockaddr. If + * :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is defined, it is typedefed to + * the generic struct sockaddr defined in ngtcp2.h. + */ +typedef struct sockaddr ngtcp2_sockaddr; +/** + * @typedef + * + * :type:`ngtcp2_sockaddr_storage` is typedefed to struct + * sockaddr_storage. If :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is + * defined, it is typedefed to the generic struct sockaddr_storage + * defined in ngtcp2.h. + */ +typedef struct sockaddr_storage ngtcp2_sockaddr_storage; +typedef struct sockaddr_in ngtcp2_sockaddr_in; +/** + * @typedef + * + * :type:`ngtcp2_socklen` is typedefed to socklen_t. If + * :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is defined, it is typedefed to + * uint32_t. + */ +typedef socklen_t ngtcp2_socklen; +#endif + +#if defined(NGTCP2_USE_GENERIC_SOCKADDR) || \ + defined(NGTCP2_USE_GENERIC_IPV6_SOCKADDR) +typedef struct ngtcp2_in6_addr { + uint8_t in6_addr[16]; +} ngtcp2_in6_addr; + +typedef struct ngtcp2_sockaddr_in6 { + uint16_t sin6_family; + uint16_t sin6_port; + uint32_t sin6_flowinfo; + ngtcp2_in6_addr sin6_addr; + uint32_t sin6_scope_id; +} ngtcp2_sockaddr_in6; +#else +typedef struct sockaddr_in6 ngtcp2_sockaddr_in6; +#endif + +/** + * @struct + * + * :type:`ngtcp2_addr` is the endpoint address. + */ +typedef struct ngtcp2_addr { + /** + * :member:`addr` points to the buffer which contains endpoint + * address. It must not be ``NULL``. + */ + ngtcp2_sockaddr *addr; + /** + * :member:`addrlen` is the length of addr. + */ + ngtcp2_socklen addrlen; +} ngtcp2_addr; + +/** + * @struct + * * :type:`ngtcp2_path` is the network endpoints where a packet is sent * and received. */ @@ -1886,6 +2038,21 @@ typedef struct ngtcp2_path { * :member:`remote` is the address of remote endpoint. */ ngtcp2_addr remote; + /** + * :member:`user_data` is an arbitrary data and opaque to the + * library. + * + * Note that :type:`ngtcp2_path` is generally passed to + * :type:`ngtcp2_conn` by an application, and :type:`ngtcp2_conn` + * stores their copies. Unfortunately, there is no way for the + * application to know when :type:`ngtcp2_conn` finishes using a + * specific :type:`ngtcp2_path` object in mid connection, which + * means that the application cannot free the data pointed by this + * field. Therefore, it is advised to use this field only when the + * data pointed by this field persists in an entire lifetime of the + * connection. + */ + void *user_data; } ngtcp2_path; /** @@ -1896,17 +2063,17 @@ typedef struct ngtcp2_path { */ typedef struct ngtcp2_path_storage { /** - * :member:`local_addrbuf` is a buffer to store local address. + * :member:`path` stores network path. */ - struct sockaddr_storage local_addrbuf; + ngtcp2_path path; /** - * :member:`remote_addrbuf` is a buffer to store remote address. + * :member:`local_addrbuf` is a buffer to store local address. */ - struct sockaddr_storage remote_addrbuf; + ngtcp2_sockaddr_storage local_addrbuf; /** - * :member:`path` stores network path. + * :member:`remote_addrbuf` is a buffer to store remote address. */ - ngtcp2_path path; + ngtcp2_sockaddr_storage remote_addrbuf; } ngtcp2_path_storage; /** @@ -1993,7 +2160,7 @@ typedef struct ngtcp2_crypto_cipher_ctx { * :type:`ngtcp2_crypto_ctx` is a convenient structure to bind all * crypto related objects in one place. Use * `ngtcp2_crypto_ctx_initial` to initialize this struct for Initial - * packet encryption. For Handshake and Short packets, use + * packet encryption. For Handshake and 1RTT packets, use * `ngtcp2_crypto_ctx_tls`. */ typedef struct ngtcp2_crypto_ctx { @@ -2027,17 +2194,21 @@ typedef struct ngtcp2_crypto_ctx { * `ngtcp2_encode_transport_params` encodes |params| in |dest| of * length |destlen|. * + * If |dest| is NULL, and |destlen| is zero, this function just + * returns the number of bytes required to store the encoded transport + * parameters. + * * This function returns the number of written, or one of the * following negative error codes: * * :macro:`NGTCP2_ERR_NOBUF` * Buffer is too small. - * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`: + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` * |exttype| is invalid. */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_encode_transport_params( +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_encode_transport_params_versioned( uint8_t *dest, size_t destlen, ngtcp2_transport_params_type exttype, - const ngtcp2_transport_params *params); + int transport_params_version, const ngtcp2_transport_params *params); /** * @function @@ -2049,6 +2220,44 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_encode_transport_params( * If the optional parameters are missing, the default value is * assigned. * + * The following fields may point to somewhere inside the buffer + * pointed by |data| of length |datalen|: + * + * - :member:`ngtcp2_transport_params.version_info.other_versions + * ` + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM` + * The required parameter is missing. + * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM` + * The input is malformed. + */ +NGTCP2_EXTERN int ngtcp2_decode_transport_params_versioned( + int transport_params_version, ngtcp2_transport_params *params, + ngtcp2_transport_params_type exttype, const uint8_t *data, size_t datalen); + +/** + * @function + * + * `ngtcp2_decode_transport_params_new` decodes transport parameters + * in |data| of length |datalen|, and stores the result in the object + * allocated dynamically. The pointer to the allocated object is + * assigned to |*pparams|. Unlike `ngtcp2_decode_transport_params`, + * all direct and indirect fields are also allocated dynamically if + * needed. + * + * |mem| is a memory allocator to allocate memory. If |mem| is + * ``NULL``, the memory allocator returned by `ngtcp2_mem_default()` + * is used. + * + * If the optional parameters are missing, the default value is + * assigned. + * + * `ngtcp2_transport_params_del` frees the memory allocated by this + * function. + * * This function returns 0 if it succeeds, or one of the following * negative error codes: * @@ -2056,13 +2265,58 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_encode_transport_params( * The required parameter is missing. * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM` * The input is malformed. - * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`: - * |exttype| is invalid. + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory. */ -NGTCP2_EXTERN int -ngtcp2_decode_transport_params(ngtcp2_transport_params *params, - ngtcp2_transport_params_type exttype, - const uint8_t *data, size_t datalen); +NGTCP2_EXTERN int ngtcp2_decode_transport_params_new( + ngtcp2_transport_params **pparams, ngtcp2_transport_params_type exttype, + const uint8_t *data, size_t datalen, const ngtcp2_mem *mem); + +/** + * @function + * + * `ngtcp2_transport_params_del` frees the |params| which must be + * dynamically allocated by `ngtcp2_decode_transport_params_new`. + * + * |mem| is a memory allocator that allocated |params|. If |mem| is + * ``NULL``, the memory allocator returned by `ngtcp2_mem_default()` + * is used. + * + * If |params| is ``NULL``, this function does nothing. + */ +NGTCP2_EXTERN void ngtcp2_transport_params_del(ngtcp2_transport_params *params, + const ngtcp2_mem *mem); + +/** + * @struct + * + * :type:`ngtcp2_version_cid` is a convenient struct to store the + * result of `ngtcp2_pkt_decode_version_cid`. + */ +typedef struct ngtcp2_version_cid { + /** + * :member:`version` stores QUIC version. + */ + uint32_t version; + /** + * :member:`dcid` points to the Destination Connection ID. + */ + const uint8_t *dcid; + /** + * :member:`dcidlen` is the length of the Destination Connection ID + * pointed by :member:`dcid`. + */ + size_t dcidlen; + /** + * :member:`scid` points to the Source Connection ID. + */ + const uint8_t *scid; + /** + * :member:`scidlen` is the length of the Source Connection ID + * pointed by :member:`scid`. + */ + size_t scidlen; +} ngtcp2_version_cid; /** * @function @@ -2076,34 +2330,44 @@ ngtcp2_decode_transport_params(ngtcp2_transport_params *params, * Longer Connection ID is only valid if the version is unsupported * QUIC version. * - * If the given packet is Long packet, this function extracts the - * version from the packet and assigns it to |*pversion|. It also + * If the given packet is Long header packet, this function extracts + * the version from the packet and assigns it to + * :member:`dest->version `. It also * extracts the pointer to the Destination Connection ID and its - * length and assigns them to |*pdcid| and |*pdcidlen| respectively. - * Similarly, it extracts the pointer to the Source Connection ID and - * its length and assigns them to |*pscid| and |*pscidlen| - * respectively. - * - * If the given packet is Short packet, |*pversion| will be 0, - * |*pscid| will be ``NULL``, and |*pscidlen| will be 0. Because the - * Short packet does not have the length of Destination Connection ID, - * the caller has to pass the length in |short_dcidlen|. This - * function extracts the pointer to the Destination Connection ID and - * assigns it to |*pdcid|. |short_dcidlen| is assigned to - * |*pdcidlen|. - * - * This function returns 0 or 1 if it succeeds. It returns 1 if - * Version Negotiation packet should be sent. Otherwise, one of the + * length and assigns them to :member:`dest->dcid + * ` and :member:`dest->dcidlen + * ` respectively. Similarly, it extracts + * the pointer to the Source Connection ID and its length and assigns + * them to :member:`dest->scid ` and + * :member:`dest->scidlen ` respectively. + * + * If the given packet is Short header packet, :member:`dest->version + * ` will be 0, :member:`dest->scid + * ` will be ``NULL``, and + * :member:`dest->scidlen ` will be 0. + * Because the Short header packet does not have the length of + * Destination Connection ID, the caller has to pass the length in + * |short_dcidlen|. This function extracts the pointer to the + * Destination Connection ID and assigns it to :member:`dest->dcid + * `. |short_dcidlen| is assigned to + * :member:`dest->dcidlen `. + * + * If Version Negotiation is required, this function returns + * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION`. Unlike the other error + * cases, all fields of |dest| are assigned as described above. + * + * This function returns 0 if it succeeds. Otherwise, one of the * following negative error code: * * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` * The function could not decode the packet header. + * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION` + * Version Negotiation packet should be sent. */ -NGTCP2_EXTERN int -ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid, - size_t *pdcidlen, const uint8_t **pscid, - size_t *pscidlen, const uint8_t *data, - size_t datalen, size_t short_dcidlen); +NGTCP2_EXTERN int ngtcp2_pkt_decode_version_cid(ngtcp2_version_cid *dest, + const uint8_t *data, + size_t datalen, + size_t short_dcidlen); /** * @function @@ -2124,8 +2388,11 @@ ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid, * Negotiation packet has random type in wire format. For * convenience, this function sets * :enum:`ngtcp2_pkt_type.NGTCP2_PKT_VERSION_NEGOTIATION` to - * dest->type, and set dest->payloadlen and dest->pkt_num to 0. - * Version Negotiation packet occupies a single packet. + * :member:`dest->type `, clears + * :macro:`NGTCP2_PKT_FLAG_LONG_FORM` flag from :member:`dest->flags + * `, and sets 0 to :member:`dest->len + * `. Version Negotiation packet occupies a single + * packet. * * It stores the result in the object pointed by |dest|, and returns * the number of bytes decoded to read the packet header if it @@ -2141,12 +2408,12 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, /** * @function * - * `ngtcp2_pkt_decode_hd_short` decodes QUIC short packet header in - * |pkt| of length |pktlen|. |dcidlen| is the length of DCID in - * packet header. Short packet does not encode the length of - * connection ID, thus we need the input from the outside. This - * function only parses the input just before packet number field. - * This function can handle Connection ID up to + * `ngtcp2_pkt_decode_hd_short` decodes QUIC short header packet + * header in |pkt| of length |pktlen|. |dcidlen| is the length of + * DCID in packet header. Short header packet does not encode the + * length of connection ID, thus we need the input from the outside. + * This function only parses the input just before packet number + * field. This function can handle Connection ID up to * :macro:`NGTCP2_MAX_CIDLEN`. Consider to use * `ngtcp2_pkt_decode_version_cid` to get longer Connection ID. It * stores the result in the object pointed by |dest|, and returns the @@ -2170,7 +2437,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, * and its length must be :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` * bytes long. |rand| specifies the random octets preceding Stateless * Reset Token. The length of |rand| is specified by |randlen| which - * must be at least :macro:`NGTCP2_MIN_STATELESS_RETRY_RANDLEN` bytes + * must be at least :macro:`NGTCP2_MIN_STATELESS_RESET_RANDLEN` bytes * long. * * If |randlen| is too long to write them all in the buffer, |rand| is @@ -2183,7 +2450,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, * Buffer is too small. * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` * |randlen| is strictly less than - * :macro:`NGTCP2_MIN_STATELESS_RETRY_RANDLEN`. + * :macro:`NGTCP2_MIN_STATELESS_RESET_RANDLEN`. */ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_stateless_reset( uint8_t *dest, size_t destlen, const uint8_t *stateless_reset_token, @@ -2231,15 +2498,12 @@ typedef struct ngtcp2_conn ngtcp2_conn; * `ngtcp2_conn_submit_crypto_data` function. Make sure that before * calling `ngtcp2_conn_submit_crypto_data` function, client * application must create initial packet protection keys and IVs, and - * provide them to ngtcp2 library using `ngtcp2_conn_set_initial_key` - * and + * provide them to ngtcp2 library using + * `ngtcp2_conn_install_initial_key`. * * This callback function must return 0 if it succeeds, or * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call * return immediately. - * - * TODO: Define error code for TLS stack failure. Suggestion: - * NGTCP2_ERR_CRYPTO. */ typedef int (*ngtcp2_client_initial)(ngtcp2_conn *conn, void *user_data); @@ -2250,16 +2514,13 @@ typedef int (*ngtcp2_client_initial)(ngtcp2_conn *conn, void *user_data); * Initial packet from client. An server application must implement * this callback, and generate initial keys and IVs for both * transmission and reception. Install them using - * `ngtcp2_conn_set_initial_key`. |dcid| is the destination + * `ngtcp2_conn_install_initial_key`. |dcid| is the destination * connection ID which client generated randomly. It is used to * derive initial packet protection keys. * * The callback function must return 0 if it succeeds. If an error * occurs, return :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the * library call return immediately. - * - * TODO: Define error code for TLS stack failure. Suggestion: - * NGTCP2_ERR_CRYPTO. */ typedef int (*ngtcp2_recv_client_initial)(ngtcp2_conn *conn, const ngtcp2_cid *dcid, @@ -2310,12 +2571,24 @@ typedef enum ngtcp2_crypto_level { * * The application should provide the given data to TLS stack. * - * The callback function must return 0 if it succeeds. If TLS stack - * reported error, return :macro:`NGTCP2_ERR_CRYPTO`. If application - * encounters fatal error, return :macro:`NGTCP2_ERR_CALLBACK_FAILURE` - * which makes the library call return immediately. If the other - * value is returned, it is treated as + * The callback function must return 0 if it succeeds, or one of the + * following negative error codes: + * + * - :macro:`NGTCP2_ERR_CRYPTO` + * - :macro:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM` + * - :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM` + * - :macro:`NGTCP2_ERR_TRANSPORT_PARAM` + * - :macro:`NGTCP2_ERR_PROTO` + * - :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE` + * - :macro:`NGTCP2_ERR_NOMEM` + * - :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * + * If the other value is returned, it is treated as * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. + * + * If application encounters fatal error, return + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call + * return immediately. */ typedef int (*ngtcp2_recv_crypto_data)(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, @@ -2369,15 +2642,15 @@ typedef int (*ngtcp2_recv_version_negotiation)(ngtcp2_conn *conn, * @functypedef * * :type:`ngtcp2_recv_retry` is invoked when Retry packet is received. - * This callback is client only. + * This callback is client use only. * * Application must regenerate packet protection key, IV, and header * protection key for Initial packets using the destination connection - * ID obtained by `ngtcp2_conn_get_dcid()` and install them by calling - * `ngtcp2_conn_install_initial_key()`. + * ID obtained by :member:`hd->scid ` and install + * them by calling `ngtcp2_conn_install_initial_key()`. * - * 0-RTT data accepted by the ngtcp2 library will be retransmitted by - * the library automatically. + * 0-RTT data accepted by the ngtcp2 library will be automatically + * retransmitted as 0-RTT data by the library. * * The callback function must return 0 if it succeeds. Returning * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return @@ -2394,13 +2667,13 @@ typedef int (*ngtcp2_recv_retry)(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, * encrypt is passed as |plaintext| of length |plaintextlen|. The * AEAD cipher is |aead|. |aead_ctx| is the AEAD cipher context * object which is initialized with encryption key. The nonce is - * passed as |nonce| of length |noncelen|. The ad, Additional Data to - * AEAD, is passed as |ad| of length |adlen|. + * passed as |nonce| of length |noncelen|. The Additional + * Authenticated Data is passed as |aad| of length |aadlen|. * * The implementation of this callback must encrypt |plaintext| using * the negotiated cipher suite and write the ciphertext into the * buffer pointed by |dest|. |dest| has enough capacity to store the - * ciphertext. + * ciphertext and any additional AEAD tag data. * * |dest| and |plaintext| may point to the same buffer. * @@ -2412,7 +2685,7 @@ typedef int (*ngtcp2_encrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *plaintext, size_t plaintextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen); + const uint8_t *aad, size_t aadlen); /** * @functypedef @@ -2422,8 +2695,8 @@ typedef int (*ngtcp2_encrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead, * decrypt is passed as |ciphertext| of length |ciphertextlen|. The * AEAD cipher is |aead|. |aead_ctx| is the AEAD cipher context * object which is initialized with decryption key. The nonce is - * passed as |nonce| of length |noncelen|. The ad, Additional Data to - * AEAD, is passed as |ad| of length |adlen|. + * passed as |nonce| of length |noncelen|. The Additional + * Authenticated Data is passed as |aad| of length |aadlen|. * * The implementation of this callback must decrypt |ciphertext| using * the negotiated cipher suite and write the ciphertext into the @@ -2433,34 +2706,38 @@ typedef int (*ngtcp2_encrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead, * |dest| and |ciphertext| may point to the same buffer. * * The callback function must return 0 if it succeeds. If TLS stack - * fails to decrypt data, return :macro:`NGTCP2_ERR_TLS_DECRYPT`. For - * any other errors, return :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which + * fails to decrypt data, return :macro:`NGTCP2_ERR_DECRYPT`. For any + * other errors, return :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which * makes the library call return immediately. */ typedef int (*ngtcp2_decrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *ciphertext, size_t ciphertextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen); + const uint8_t *aad, size_t aadlen); /** * @functypedef * * :type:`ngtcp2_hp_mask` is invoked when the ngtcp2 library asks the - * application to produce mask to encrypt or decrypt packet header. + * application to produce a mask to encrypt or decrypt packet header. * The encryption cipher is |hp|. |hp_ctx| is the cipher context * object which is initialized with header protection key. The sample - * is passed as |sample|. + * is passed as |sample| which is :macro:`NGTCP2_HP_SAMPLELEN` bytes + * long. * * The implementation of this callback must produce a mask using the * header protection cipher suite specified by QUIC specification and * write the result into the buffer pointed by |dest|. The length of - * mask must be :macro:`NGTCP2_HP_MASKLEN`. The library ensures that - * |dest| has enough capacity. + * the mask must be at least :macro:`NGTCP2_HP_MASKLEN`. The library + * only uses the first :macro:`NGTCP2_HP_MASKLEN` bytes of the + * produced mask. The buffer pointed by |dest| is guaranteed to have + * at least :macro:`NGTCP2_HP_SAMPLELEN` bytes available for + * convenience. * * The callback function must return 0 if it succeeds, or - * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call - * return immediately. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call + * return immediately. */ typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp, const ngtcp2_crypto_cipher_ctx *hp_ctx, @@ -2477,7 +2754,7 @@ typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp, * * :macro:`NGTCP2_STREAM_DATA_FLAG_NONE` indicates no flag set. */ -#define NGTCP2_STREAM_DATA_FLAG_NONE 0x00 +#define NGTCP2_STREAM_DATA_FLAG_NONE 0x00u /** * @macro @@ -2485,7 +2762,7 @@ typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp, * :macro:`NGTCP2_STREAM_DATA_FLAG_FIN` indicates that this chunk of * data is final piece of an incoming stream. */ -#define NGTCP2_STREAM_DATA_FLAG_FIN 0x01 +#define NGTCP2_STREAM_DATA_FLAG_FIN 0x01u /** * @macro @@ -2494,21 +2771,21 @@ typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp, * data contains data received in 0RTT packet and the handshake has * not completed yet, which means that the data might be replayed. */ -#define NGTCP2_STREAM_DATA_FLAG_EARLY 0x02 +#define NGTCP2_STREAM_DATA_FLAG_EARLY 0x02u /** * @functypedef * * :type:`ngtcp2_recv_stream_data` is invoked when stream data is * received. The stream is specified by |stream_id|. |flags| is the - * bitwise-OR of zero or more of NGTCP2_STREAM_DATA_FLAG_*. See - * :macro:`NGTCP2_STREAM_DATA_FLAG_NONE`. If |flags| & + * bitwise-OR of zero or more of :macro:`NGTCP2_STREAM_DATA_FLAG_* + * `. If |flags| & * :macro:`NGTCP2_STREAM_DATA_FLAG_FIN` is nonzero, this portion of * the data is the last data in this stream. |offset| is the offset * where this data begins. The library ensures that data is passed to - * the application in the non-decreasing order of |offset|. The data - * is passed as |data| of length |datalen|. |datalen| may be 0 if and - * only if |fin| is nonzero. + * the application in the non-decreasing order of |offset| without any + * overlap. The data is passed as |data| of length |datalen|. + * |datalen| may be 0 if and only if |fin| is nonzero. * * If :macro:`NGTCP2_STREAM_DATA_FLAG_EARLY` is set in |flags|, it * indicates that a part of or whole data was received in 0RTT packet @@ -2538,21 +2815,55 @@ typedef int (*ngtcp2_recv_stream_data)(ngtcp2_conn *conn, uint32_t flags, typedef int (*ngtcp2_stream_open)(ngtcp2_conn *conn, int64_t stream_id, void *user_data); +/** + * @macrosection + * + * Stream close flags + */ + +/** + * @macro + * + * :macro:`NGTCP2_STREAM_CLOSE_FLAG_NONE` indicates no flag set. + */ +#define NGTCP2_STREAM_CLOSE_FLAG_NONE 0x00u + +/** + * @macro + * + * :macro:`NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET` indicates that + * app_error_code parameter is set. + */ +#define NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET 0x01u + /** * @functypedef * * :type:`ngtcp2_stream_close` is invoked when a stream is closed. * This callback is not called when QUIC connection is closed before - * existing streams are closed. |app_error_code| indicates the error - * code of this closure. + * existing streams are closed. |flags| is the bitwise-OR of zero or + * more of :macro:`NGTCP2_STREAM_CLOSE_FLAG_* + * `. |app_error_code| indicates the + * error code of this closure if + * :macro:`NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET` is set in + * |flags|. If it is not set, the stream was closed without any error + * code, which generally means success. + * + * |app_error_code| is the first application error code sent by a + * local endpoint, or received from a remote endpoint. If a stream is + * closed cleanly, no application error code is exchanged. Since QUIC + * stack does not know the application error code which indicates "no + * errors", |app_error_code| is set to 0 and + * :macro:`NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET` is not set in + * |flags| in this case. * * The implementation of this callback should return 0 if it succeeds. * Returning :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library * call return immediately. */ -typedef int (*ngtcp2_stream_close)(ngtcp2_conn *conn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data); +typedef int (*ngtcp2_stream_close)(ngtcp2_conn *conn, uint32_t flags, + int64_t stream_id, uint64_t app_error_code, + void *user_data, void *stream_user_data); /** * @functypedef @@ -2575,10 +2886,10 @@ typedef int (*ngtcp2_stream_reset)(ngtcp2_conn *conn, int64_t stream_id, * which is called when stream data is acked, and application can free * the data. The acked range of data is [offset, offset + datalen). * For a given stream_id, this callback is called sequentially in - * increasing order of |offset|. |datalen| is normally strictly - * greater than 0. One exception is that when a packet which includes - * STREAM frame which has fin flag set, and 0 length data, this - * callback is invoked with 0 passed as |datalen|. + * increasing order of |offset| without any overlap. |datalen| is + * normally strictly greater than 0. One exception is that when a + * packet which includes STREAM frame which has fin flag set, and 0 + * length data, this callback is invoked with 0 passed as |datalen|. * * If a stream is closed prematurely and stream data is still * in-flight, this callback function is not called for those data. @@ -2591,26 +2902,6 @@ typedef int (*ngtcp2_acked_stream_data_offset)( ngtcp2_conn *conn, int64_t stream_id, uint64_t offset, uint64_t datalen, void *user_data, void *stream_user_data); -/** - * @functypedef - * - * :type:`ngtcp2_acked_crypto_offset` is a callback function which is - * called when crypto stream data is acknowledged, and application can - * free the data. |crypto_level| indicates the encryption level where - * this data was sent. Crypto data never be sent in - * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`. This works - * like :type:`ngtcp2_acked_stream_data_offset` but crypto stream has - * no stream_id and stream_user_data, and |datalen| never become 0. - * - * The implementation of this callback should return 0 if it succeeds. - * Returning :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library - * call return immediately. - */ -typedef int (*ngtcp2_acked_crypto_offset)(ngtcp2_conn *conn, - ngtcp2_crypto_level crypto_level, - uint64_t offset, uint64_t datalen, - void *user_data); - /** * @functypedef * @@ -2663,16 +2954,11 @@ typedef int (*ngtcp2_extend_max_stream_data)(ngtcp2_conn *conn, * * :type:`ngtcp2_rand` is a callback function to get randomized byte * string from application. Application must fill random |destlen| - * bytes to the buffer pointed by |dest|. |usage| provides the usage - * of the generated random data. - * - * The callback function must return 0 if it succeeds. Returning - * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return - * immediately. + * bytes to the buffer pointed by |dest|. The generated bytes are + * used only in non-cryptographic context. */ -typedef int (*ngtcp2_rand)(uint8_t *dest, size_t destlen, - const ngtcp2_rand_ctx *rand_ctx, - ngtcp2_rand_usage usage); +typedef void (*ngtcp2_rand)(uint8_t *dest, size_t destlen, + const ngtcp2_rand_ctx *rand_ctx); /** * @functypedef @@ -2740,12 +3026,36 @@ typedef int (*ngtcp2_update_key)( const uint8_t *current_rx_secret, const uint8_t *current_tx_secret, size_t secretlen, void *user_data); +/** + * @macrosection + * + * Path validation related macros + */ + +/** + * @macro + * + * :macro:`NGTCP2_PATH_VALIDATION_FLAG_NONE` indicates no flag set. + */ +#define NGTCP2_PATH_VALIDATION_FLAG_NONE 0x00u + +/** + * @macro + * + * :macro:`NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR` indicates the + * validation involving server preferred address. This flag is only + * set for client. + */ +#define NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR 0x01u + /** * @functypedef * * :type:`ngtcp2_path_validation` is a callback function which tells - * the application the outcome of path validation. |path| is the path - * that was validated. If |res| is + * the application the outcome of path validation. |flags| is zero or + * more of :macro:`NGTCP2_PATH_VALIDATION_FLAG_* + * `. |path| is the path that was + * validated. If |res| is * :enum:`ngtcp2_path_validation_result.NGTCP2_PATH_VALIDATION_RESULT_SUCCESS`, * the path validation succeeded. If |res| is * :enum:`ngtcp2_path_validation_result.NGTCP2_PATH_VALIDATION_RESULT_FAILURE`, @@ -2755,7 +3065,7 @@ typedef int (*ngtcp2_update_key)( * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return * immediately. */ -typedef int (*ngtcp2_path_validation)(ngtcp2_conn *conn, +typedef int (*ngtcp2_path_validation)(ngtcp2_conn *conn, uint32_t flags, const ngtcp2_path *path, ngtcp2_path_validation_result res, void *user_data); @@ -2766,16 +3076,28 @@ typedef int (*ngtcp2_path_validation)(ngtcp2_conn *conn, * :type:`ngtcp2_select_preferred_addr` is a callback function which * asks a client application to choose server address from preferred * addresses |paddr| received from server. An application should - * write preferred address in |dest|. If an application denies the - * preferred addresses, just leave |dest| unmodified (or set dest->len - * to 0) and return 0. + * write a network path for a selected preferred address in |dest|. + * More specifically, the selected preferred address must be set to + * :member:`dest->remote `, a client source + * address must be set to :member:`dest->local `. + * If a client source address does not change for the new server + * address, leave :member:`dest->local ` + * unmodified, or copy the value of :member:`local + * ` field of the current network path obtained + * from `ngtcp2_conn_get_path()`. Both :member:`dest->local.addr + * ` and :member:`dest->remote.addr + * ` point to buffers which are at least + * ``sizeof(struct sockaddr_storage)`` bytes long, respectively. If + * an application denies the preferred addresses, just leave |dest| + * unmodified (or set :member:`dest->remote.addrlen + * ` to 0) and return 0. * * The callback function must return 0 if it succeeds. Returning * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return * immediately. */ typedef int (*ngtcp2_select_preferred_addr)(ngtcp2_conn *conn, - ngtcp2_addr *dest, + ngtcp2_path *dest, const ngtcp2_preferred_addr *paddr, void *user_data); @@ -2840,7 +3162,9 @@ typedef int (*ngtcp2_recv_new_token)(ngtcp2_conn *conn, const ngtcp2_vec *token, * @functypedef * * :type:`ngtcp2_delete_crypto_aead_ctx` is a callback function which - * must delete the native object pointed by |aead_ctx|->native_handle. + * must delete the native object pointed by + * :member:`aead_ctx->native_handle + * `. */ typedef void (*ngtcp2_delete_crypto_aead_ctx)(ngtcp2_conn *conn, ngtcp2_crypto_aead_ctx *aead_ctx, @@ -2851,7 +3175,8 @@ typedef void (*ngtcp2_delete_crypto_aead_ctx)(ngtcp2_conn *conn, * * :type:`ngtcp2_delete_crypto_cipher_ctx` is a callback function * which must delete the native object pointed by - * |cipher_ctx|->native_handle. + * :member:`cipher_ctx->native_handle + * `. */ typedef void (*ngtcp2_delete_crypto_cipher_ctx)( ngtcp2_conn *conn, ngtcp2_crypto_cipher_ctx *cipher_ctx, void *user_data); @@ -2867,7 +3192,7 @@ typedef void (*ngtcp2_delete_crypto_cipher_ctx)( * * :macro:`NGTCP2_DATAGRAM_FLAG_NONE` indicates no flag set. */ -#define NGTCP2_DATAGRAM_FLAG_NONE 0x00 +#define NGTCP2_DATAGRAM_FLAG_NONE 0x00u /** * @macro @@ -2876,14 +3201,14 @@ typedef void (*ngtcp2_delete_crypto_cipher_ctx)( * is received in 0RTT packet and the handshake has not completed yet, * which means that the data might be replayed. */ -#define NGTCP2_DATAGRAM_FLAG_EARLY 0x01 +#define NGTCP2_DATAGRAM_FLAG_EARLY 0x01u /** * @functypedef * * :type:`ngtcp2_recv_datagram` is invoked when DATAGRAM frame is * received. |flags| is bitwise-OR of zero or more of - * NGTCP2_DATAGRAM_FLAG_*. See :macro:`NGTCP2_DATAGRAM_FLAG_NONE`. + * :macro:`NGTCP2_DATAGRAM_FLAG_* `. * * If :macro:`NGTCP2_DATAGRAM_FLAG_EARLY` is set in |flags|, it * indicates that DATAGRAM frame was received in 0RTT packet and a @@ -2897,6 +3222,107 @@ typedef int (*ngtcp2_recv_datagram)(ngtcp2_conn *conn, uint32_t flags, const uint8_t *data, size_t datalen, void *user_data); +/** + * @functypedef + * + * :type:`ngtcp2_ack_datagram` is invoked when a packet which contains + * DATAGRAM frame which is identified by |dgram_id| is acknowledged. + * |dgram_id| is the valued passed to `ngtcp2_conn_writev_datagram`. + * + * The callback function must return 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library return + * immediately. + */ +typedef int (*ngtcp2_ack_datagram)(ngtcp2_conn *conn, uint64_t dgram_id, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_lost_datagram` is invoked when a packet which + * contains DATAGRAM frame which is identified by |dgram_id| is + * declared lost. |dgram_id| is the valued passed to + * `ngtcp2_conn_writev_datagram`. Note that the loss might be + * spurious, and DATAGRAM frame might be acknowledged later. + * + * The callback function must return 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library return + * immediately. + */ +typedef int (*ngtcp2_lost_datagram)(ngtcp2_conn *conn, uint64_t dgram_id, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_get_path_challenge_data` is a callback function to + * ask an application for new data that is sent in PATH_CHALLENGE + * frame. Application must generate new unpredictable exactly + * :macro:`NGTCP2_PATH_CHALLENGE_DATALEN` bytes of random data and + * store them into the buffer pointed by |data|. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_get_path_challenge_data)(ngtcp2_conn *conn, uint8_t *data, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_stream_stop_sending` is invoked when a stream is no + * longer read by a local endpoint before it receives all stream data. + * This function is called at most once per stream. |app_error_code| + * is the error code passed to `ngtcp2_conn_shutdown_stream_read` or + * `ngtcp2_conn_shutdown_stream`. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_stream_stop_sending)(ngtcp2_conn *conn, int64_t stream_id, + uint64_t app_error_code, + void *user_data, + void *stream_user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_version_negotiation` is invoked when the compatible + * version negotiation takes place. For client, it is called when it + * sees a change in version field of a long header packet. This + * callback function might be called multiple times for client. For + * server, it is called once when the version is negotiated. + * + * The implementation of this callback must install new Initial keys + * for |version|. Use `ngtcp2_conn_install_vneg_initial_key` to + * install keys. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_version_negotiation)(ngtcp2_conn *conn, uint32_t version, + const ngtcp2_cid *client_dcid, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_recv_key` is invoked when new key is installed to + * |conn| during QUIC cryptographic handshake. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_recv_key)(ngtcp2_conn *conn, ngtcp2_crypto_level level, + void *user_data); + +#define NGTCP2_CALLBACKS_VERSION_V1 1 +#define NGTCP2_CALLBACKS_VERSION NGTCP2_CALLBACKS_VERSION_V1 + /** * @struct * @@ -2906,13 +3332,14 @@ typedef struct ngtcp2_callbacks { /** * :member:`client_initial` is a callback function which is invoked * when client asks TLS stack to produce first TLS cryptographic - * handshake message. This callback function must be specified. + * handshake message. This callback function must be specified for + * a client application. */ ngtcp2_client_initial client_initial; /** * :member:`recv_client_initial` is a callback function which is * invoked when a server receives the first packet from client. - * This callback function must be specified. + * This callback function must be specified for a server application. */ ngtcp2_recv_client_initial recv_client_initial; /** @@ -2956,14 +3383,6 @@ typedef struct ngtcp2_callbacks { * received. This callback function is optional. */ ngtcp2_recv_stream_data recv_stream_data; - /** - * :member:`acked_crypto_offset` is a callback function which is - * invoked when CRYPTO data is acknowledged by a remote endpoint. - * It tells an application the largest offset of acknowledged CRYPTO - * data without a gap so that the application can free memory for - * the data. This callback function is optional. - */ - ngtcp2_acked_crypto_offset acked_crypto_offset; /** * :member:`acked_stream_data_offset` is a callback function which * is invoked when STREAM data, which includes application data, is @@ -3012,8 +3431,8 @@ typedef struct ngtcp2_callbacks { ngtcp2_extend_max_streams extend_max_local_streams_uni; /** * :member:`rand` is a callback function which is invoked when the - * library needs unpredictable sequence of random data. This - * callback function must be specified. + * library needs sequence of random data. This callback function + * must be specified. */ ngtcp2_rand rand; /** @@ -3031,18 +3450,21 @@ typedef struct ngtcp2_callbacks { /** * :member:`update_key` is a callback function which is invoked when * the library tells an application that it must update keying - * materials and install new keys. This function must be specified. + * materials and install new keys. This callback function must be + * specified. */ ngtcp2_update_key update_key; /** * :member:`path_validation` is a callback function which is invoked - * when path validation completed. This function is optional. + * when path validation completed. This callback function is + * optional. */ ngtcp2_path_validation path_validation; /** * :member:`select_preferred_addr` is a callback function which is * invoked when the library asks a client to select preferred - * address presented by a server. This function is optional. + * address presented by a server. This callback function is + * optional. */ ngtcp2_select_preferred_addr select_preferred_addr; /** @@ -3075,47 +3497,97 @@ typedef struct ngtcp2_callbacks { /** * :member:`dcid_status` is a callback function which is invoked * when the new destination Connection ID is activated or the - * activated destination Connection ID is now deactivated. + * activated destination Connection ID is now deactivated. This + * callback function is optional. */ ngtcp2_connection_id_status dcid_status; /** * :member:`handshake_confirmed` is a callback function which is * invoked when both endpoints agree that handshake has finished. * This field is ignored by server because handshake_completed - * indicates the handshake confirmation for server. + * indicates the handshake confirmation for server. This callback + * function is optional. */ ngtcp2_handshake_confirmed handshake_confirmed; /** * :member:`recv_new_token` is a callback function which is invoked * when new token is received from server. This field is ignored by - * server. + * server. This callback function is optional. */ ngtcp2_recv_new_token recv_new_token; /** * :member:`delete_crypto_aead_ctx` is a callback function which - * deletes a given AEAD cipher context object. + * deletes a given AEAD cipher context object. This callback + * function must be specified. */ ngtcp2_delete_crypto_aead_ctx delete_crypto_aead_ctx; /** * :member:`delete_crypto_cipher_ctx` is a callback function which - * deletes a given cipher context object. + * deletes a given cipher context object. This callback function + * must be specified. */ ngtcp2_delete_crypto_cipher_ctx delete_crypto_cipher_ctx; /** * :member:`recv_datagram` is a callback function which is invoked - * when DATAGRAM frame is received. + * when DATAGRAM frame is received. This callback function is + * optional. */ ngtcp2_recv_datagram recv_datagram; + /** + * :member:`ack_datagram` is a callback function which is invoked + * when a packet containing DATAGRAM frame is acknowledged. This + * callback function is optional. + */ + ngtcp2_ack_datagram ack_datagram; + /** + * :member:`lost_datagram` is a callback function which is invoked + * when a packet containing DATAGRAM frame is declared lost. This + * callback function is optional. + */ + ngtcp2_lost_datagram lost_datagram; + /** + * :member:`get_path_challenge_data` is a callback function which is + * invoked when the library needs new PATH_CHALLENGE data. This + * callback must be specified. + */ + ngtcp2_get_path_challenge_data get_path_challenge_data; + /** + * :member:`stream_stop_sending` is a callback function which is + * invoked when a local endpoint no longer reads from a stream + * before it receives all stream data. This callback function is + * optional. + */ + ngtcp2_stream_stop_sending stream_stop_sending; + /** + * :member:`version_negotiation` is a callback function which is + * invoked when the compatible version negotiation takes place. + * This callback function must be specified. + */ + ngtcp2_version_negotiation version_negotiation; + /** + * :member:`recv_rx_key` is a callback function which is invoked + * when a new key for decrypting packets is installed during QUIC + * cryptographic handshake. It is not called for + * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_INITIAL`. + */ + ngtcp2_recv_key recv_rx_key; + /** + * :member:`recv_tx_key` is a callback function which is invoked + * when a new key for encrypting packets is installed during QUIC + * cryptographic handshake. It is not called for + * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_INITIAL`. + */ + ngtcp2_recv_key recv_tx_key; } ngtcp2_callbacks; /** * @function * * `ngtcp2_pkt_write_connection_close` writes Initial packet - * containing CONNECTION_CLOSE frame with the given |error_code| to - * the buffer pointed by |dest| of length |destlen|. All encryption - * parameters are for Initial packet encryption. The packet number is - * always 0. + * containing CONNECTION_CLOSE frame with the given |error_code| and + * the optional |reason| of length |reasonlen| to the buffer pointed + * by |dest| of length |destlen|. All encryption parameters are for + * Initial packet encryption. The packet number is always 0. * * The primary use case of this function is for server to send * CONNECTION_CLOSE frame in Initial packet to close connection @@ -3131,20 +3603,24 @@ typedef struct ngtcp2_callbacks { */ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_connection_close( uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, uint64_t error_code, ngtcp2_encrypt encrypt, - const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, - const uint8_t *iv, ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp, + const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason, + size_t reasonlen, ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, + ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp, const ngtcp2_crypto_cipher_ctx *hp_ctx); /** * @function * * `ngtcp2_pkt_write_retry` writes Retry packet in the buffer pointed - * by |dest| whose length is |destlen|. |odcid| specifies Original - * Destination Connection ID. |token| specifies Retry Token, and - * |tokenlen| specifies its length. |aead| must be AEAD_AES_128_GCM. - * |aead_ctx| must be initialized with :macro:`NGTCP2_RETRY_KEY` as an - * encryption key. + * by |dest| whose length is |destlen|. |dcid| is the destination + * connection ID which appeared in a packet as a source connection ID + * sent by client. |scid| is a server chosen source connection ID. + * |odcid| specifies Original Destination Connection ID which appeared + * in a packet as a destination connection ID sent by client. |token| + * specifies Retry Token, and |tokenlen| specifies its length. |aead| + * must be AEAD_AES_128_GCM. |aead_ctx| must be initialized with + * :macro:`NGTCP2_RETRY_KEY` as an encryption key. * * This function returns the number of bytes written to the buffer, or * one of the following negative error codes: @@ -3153,6 +3629,9 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_connection_close( * Buffer is too small. * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` * Callback function failed. + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + * :member:`odcid->datalen ` is less than + * :macro:`NGTCP2_MIN_INITIAL_DCIDLEN`. */ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_retry( uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid, @@ -3164,15 +3643,21 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_retry( * @function * * `ngtcp2_accept` is used by server implementation, and decides - * whether packet |pkt| of length |pktlen| is acceptable for initial - * packet from client. + * whether packet |pkt| of length |pktlen| from client is acceptable + * for the very initial packet to a connection. + * + * If |dest| is not ``NULL`` and the function returns 0, or + * :macro:`NGTCP2_ERR_RETRY`, the decoded packet header is stored to + * the object pointed by |dest|. * - * If it is acceptable, it returns 0. If it is not acceptable, and - * Version Negotiation packet is required to send, it returns 1. - * Otherwise, it returns -1. + * This function returns 0 if it succeeds, or one of the following + * negative error codes: * - * If |dest| is not ``NULL``, and the return value is 0 or 1, the - * decoded packet header is stored to the object pointed by |dest|. + * :macro:`NGTCP2_ERR_RETRY` + * Retry packet should be sent. + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + * The packet is not acceptable for the very first packet to a new + * connection; or the function failed to parse the packet header. */ NGTCP2_EXTERN int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt, size_t pktlen); @@ -3182,15 +3667,16 @@ NGTCP2_EXTERN int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt, * * `ngtcp2_conn_client_new` creates new :type:`ngtcp2_conn`, and * initializes it as client. |dcid| is randomized destination - * connection ID. |scid| is source connection ID. |version| is a - * QUIC version to use. |path| is the network path where this QUIC - * connection is being established and must not be ``NULL``. - * |callbacks|, |settings|, and |params| must not be ``NULL``, and the - * function make a copy of each of them. |params| is local QUIC - * transport parameters and sent to a remote endpoint during - * handshake. |user_data| is the arbitrary pointer which is passed to - * the user-defined callback functions. If |mem| is ``NULL``, the - * memory allocator returned by `ngtcp2_mem_default()` is used. + * connection ID. |scid| is source connection ID. + * |client_chosen_version| is a QUIC version that a client chooses. + * |path| is the network path where this QUIC connection is being + * established and must not be ``NULL``. |callbacks|, |settings|, and + * |params| must not be ``NULL``, and the function make a copy of each + * of them. |params| is local QUIC transport parameters and sent to a + * remote endpoint during handshake. |user_data| is the arbitrary + * pointer which is passed to the user-defined callback functions. If + * |mem| is ``NULL``, the memory allocator returned by + * `ngtcp2_mem_default()` is used. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -3198,13 +3684,13 @@ NGTCP2_EXTERN int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt, * :macro:`NGTCP2_ERR_NOMEM` * Out of memory. */ -NGTCP2_EXTERN int -ngtcp2_conn_client_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, const ngtcp2_path *path, - uint32_t version, const ngtcp2_callbacks *callbacks, - const ngtcp2_settings *settings, - const ngtcp2_transport_params *params, - const ngtcp2_mem *mem, void *user_data); +NGTCP2_EXTERN int ngtcp2_conn_client_new_versioned( + ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, + const ngtcp2_path *path, uint32_t client_chosen_version, + int callbacks_version, const ngtcp2_callbacks *callbacks, + int settings_version, const ngtcp2_settings *settings, + int transport_params_version, const ngtcp2_transport_params *params, + const ngtcp2_mem *mem, void *user_data); /** * @function @@ -3212,14 +3698,14 @@ ngtcp2_conn_client_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, * `ngtcp2_conn_server_new` creates new :type:`ngtcp2_conn`, and * initializes it as server. |dcid| is a destination connection ID. * |scid| is a source connection ID. |path| is the network path where - * this QUIC connection is being established and must not be ``NULL`. - * |version| is a QUIC version to use. |callbacks|, |settings|, and - * |params| must not be ``NULL``, and the function make a copy of each - * of them. |params| is local QUIC transport parameters and sent to a - * remote endpoint during handshake. |user_data| is the arbitrary - * pointer which is passed to the user-defined callback functions. If - * |mem| is ``NULL``, the memory allocator returned by - * `ngtcp2_mem_default()` is used. + * this QUIC connection is being established and must not be ``NULL``. + * |client_chosen_version| is a QUIC version that a client chooses. + * |callbacks|, |settings|, and |params| must not be ``NULL``, and the + * function make a copy of each of them. |params| is local QUIC + * transport parameters and sent to a remote endpoint during + * handshake. |user_data| is the arbitrary pointer which is passed to + * the user-defined callback functions. If |mem| is ``NULL``, the + * memory allocator returned by `ngtcp2_mem_default()` is used. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -3227,13 +3713,13 @@ ngtcp2_conn_client_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, * :macro:`NGTCP2_ERR_NOMEM` * Out of memory. */ -NGTCP2_EXTERN int -ngtcp2_conn_server_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, const ngtcp2_path *path, - uint32_t version, const ngtcp2_callbacks *callbacks, - const ngtcp2_settings *settings, - const ngtcp2_transport_params *params, - const ngtcp2_mem *mem, void *user_data); +NGTCP2_EXTERN int ngtcp2_conn_server_new_versioned( + ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, + const ngtcp2_path *path, uint32_t client_chosen_version, + int callbacks_version, const ngtcp2_callbacks *callbacks, + int settings_version, const ngtcp2_settings *settings, + int transport_params_version, const ngtcp2_transport_params *params, + const ngtcp2_mem *mem, void *user_data); /** * @function @@ -3249,56 +3735,59 @@ NGTCP2_EXTERN void ngtcp2_conn_del(ngtcp2_conn *conn); * `ngtcp2_conn_read_pkt` decrypts QUIC packet given in |pkt| of * length |pktlen| and processes it. |path| is the network path the * packet is delivered and must not be ``NULL``. |pi| is packet - * metadata and must not be ``NULL``. This function performs QUIC - * handshake as well. + * metadata and may be ``NULL``. This function performs QUIC handshake + * as well. * * This function must not be called from inside the callback * functions. * * This function returns 0 if it succeeds, or negative error codes. - * In general, if the error code which satisfies - * `ngtcp2_err_is_fatal(err) ` != 0 is returned, - * the application should just close the connection by calling - * `ngtcp2_conn_write_connection_close` or just delete the QUIC - * connection using `ngtcp2_conn_del`. It is undefined to call the - * other library functions. If :macro:`NGTCP2_ERR_RETRY` is returned, - * application must be a server and it must perform address validation - * by sending Retry packet and close the connection. If + * If :macro:`NGTCP2_ERR_RETRY` is returned, application must be a + * server and it must perform address validation by sending Retry + * packet and discard the connection state. If * :macro:`NGTCP2_ERR_DROP_CONN` is returned, server application must * drop the connection silently (without sending any CONNECTION_CLOSE - * frame) and discard connection state. + * frame) and discard connection state. If + * :macro:`NGTCP2_ERR_DRAINING` is returned, a connection has entered + * the draining state, and no further packet transmission is allowed. + * If :macro:`NGTCP2_ERR_CRYPTO` is returned, the error happened in + * TLS stack and `ngtcp2_conn_get_tls_alert` returns TLS alert if set. + * + * If any other negative errors are returned, call + * `ngtcp2_conn_write_connection_close` to get terminal packet, and + * sending it makes QUIC connection enter the closing state. */ -NGTCP2_EXTERN int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, - const ngtcp2_path *path, - const ngtcp2_pkt_info *pi, - const uint8_t *pkt, size_t pktlen, - ngtcp2_tstamp ts); +NGTCP2_EXTERN int +ngtcp2_conn_read_pkt_versioned(ngtcp2_conn *conn, const ngtcp2_path *path, + int pkt_info_version, const ngtcp2_pkt_info *pi, + const uint8_t *pkt, size_t pktlen, + ngtcp2_tstamp ts); /** * @function * * `ngtcp2_conn_write_pkt` is equivalent to calling - * `ngtcp2_conn_writev_stream` without specifying stream data and + * `ngtcp2_conn_writev_stream` with -1 as stream_id, no stream data, and * :macro:`NGTCP2_WRITE_STREAM_FLAG_NONE` as flags. */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_pkt(ngtcp2_conn *conn, - ngtcp2_path *path, - ngtcp2_pkt_info *pi, - uint8_t *dest, size_t destlen, - ngtcp2_tstamp ts); +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_pkt_versioned( + ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_tstamp ts); /** * @function * - * `ngtcp2_conn_handshake_completed` tells |conn| that the QUIC - * handshake has completed. + * `ngtcp2_conn_handshake_completed` tells |conn| that the TLS stack + * declares TLS handshake completion. This does not mean QUIC + * handshake has completed. The library needs extra conditions to be + * met. */ NGTCP2_EXTERN void ngtcp2_conn_handshake_completed(ngtcp2_conn *conn); /** * @function * - * `ngtcp2_conn_get_handshake_completed` returns nonzero if handshake + * `ngtcp2_conn_get_handshake_completed` returns nonzero if QUIC handshake * has completed. */ NGTCP2_EXTERN int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn); @@ -3308,14 +3797,17 @@ NGTCP2_EXTERN int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn); * * `ngtcp2_conn_install_initial_key` installs packet protection keying * materials for Initial packets. |rx_aead_ctx| is AEAD cipher - * context object and must be initialized with decryption key, IV - * |rx_iv| of length |rx_ivlen|, and packet header protection cipher - * context object |rx_hp_ctx| to decrypt incoming Initial packets. + * context object and must be initialized with a decryption key. + * |rx_iv| is IV of length |rx_ivlen| for decryption. |rx_hp_ctx| is + * a packet header protection cipher context object for decryption. * Similarly, |tx_aead_ctx|, |tx_iv| and |tx_hp_ctx| are for * encrypting outgoing packets and are the same length with the * decryption counterpart . If they have already been set, they are * overwritten. * + * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if + * that is larger. + * * If this function succeeds, |conn| takes ownership of |rx_aead_ctx|, * |rx_hp_ctx|, |tx_aead_ctx|, and |tx_hp_ctx|. * :type:`ngtcp2_delete_crypto_aead_ctx` and @@ -3339,15 +3831,53 @@ NGTCP2_EXTERN int ngtcp2_conn_install_initial_key( const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv, const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen); +/** + * @function + * + * `ngtcp2_conn_install_vneg_initial_key` installs packet protection + * keying materials for Initial packets on compatible version + * negotiation for |version|. |rx_aead_ctx| is AEAD cipher context + * object and must be initialized with a decryption key. |rx_iv| is + * IV of length |rx_ivlen| for decryption. |rx_hp_ctx| is a packet + * header protection cipher context object for decryption. Similarly, + * |tx_aead_ctx|, |tx_iv| and |tx_hp_ctx| are for encrypting outgoing + * packets and are the same length with the decryption counterpart . + * If they have already been set, they are overwritten. + * + * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if + * that is larger. + * + * If this function succeeds, |conn| takes ownership of |rx_aead_ctx|, + * |rx_hp_ctx|, |tx_aead_ctx|, and |tx_hp_ctx|. + * :type:`ngtcp2_delete_crypto_aead_ctx` and + * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete + * these objects when they are no longer used. If this function + * fails, the caller is responsible to delete them. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_install_vneg_initial_key( + ngtcp2_conn *conn, uint32_t version, + const ngtcp2_crypto_aead_ctx *rx_aead_ctx, const uint8_t *rx_iv, + const ngtcp2_crypto_cipher_ctx *rx_hp_ctx, + const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv, + const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen); + /** * @function * * `ngtcp2_conn_install_rx_handshake_key` installs packet protection * keying materials for decrypting incoming Handshake packets. * |aead_ctx| is AEAD cipher context object which must be initialized - * with decryption key, IV |iv| of length |ivlen|, and packet header - * protection cipher context object |hp_ctx| to decrypt incoming - * Handshake packets. + * with a decryption key. |iv| is IV of length |ivlen|. |hp_ctx| is + * a packet header protection cipher context object. + * + * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if + * that is larger. * * If this function succeeds, |conn| takes ownership of |aead_ctx|, * and |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and @@ -3371,9 +3901,11 @@ NGTCP2_EXTERN int ngtcp2_conn_install_rx_handshake_key( * `ngtcp2_conn_install_tx_handshake_key` installs packet protection * keying materials for encrypting outgoing Handshake packets. * |aead_ctx| is AEAD cipher context object which must be initialized - * with encryption key, IV |iv| of length |ivlen|, and packet header - * protection cipher context object |hp_ctx| to encrypt outgoing - * Handshake packets. + * with an encryption key. |iv| is IV of length |ivlen|. |hp_ctx| is + * a packet header protection cipher context object. + * + * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if + * that is larger. * * If this function succeeds, |conn| takes ownership of |aead_ctx| and * |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and @@ -3399,6 +3931,9 @@ NGTCP2_EXTERN int ngtcp2_conn_install_tx_handshake_key( * packet header protection cipher context object |hp_ctx| to encrypt * (for client) or decrypt (for server) 0RTT packets. * + * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if + * that is larger. + * * If this function succeeds, |conn| takes ownership of |aead_ctx| and * |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete @@ -3419,12 +3954,15 @@ NGTCP2_EXTERN int ngtcp2_conn_install_early_key( * @function * * `ngtcp2_conn_install_rx_key` installs packet protection keying - * materials for decrypting Short packets. |secret| of length + * materials for decrypting Short header packets. |secret| of length * |secretlen| is the decryption secret which is used to derive keying * materials passed to this function. |aead_ctx| is AEAD cipher - * context object which must be initialized with decryption key, IV - * |iv| of length |ivlen|, and packet header protection cipher context - * object |hp_ctx| to decrypt incoming Short packets. + * context object which must be initialized with a decryption key. + * |iv| is IV of length |ivlen|. |hp_ctx| is a packet header + * protection cipher context object. + * + * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if + * that is larger. * * If this function succeeds, |conn| takes ownership of |aead_ctx| and * |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and @@ -3447,12 +3985,15 @@ NGTCP2_EXTERN int ngtcp2_conn_install_rx_key( * @function * * `ngtcp2_conn_install_tx_key` installs packet protection keying - * materials for encrypting Short packets. |secret| of length + * materials for encrypting Short header packets. |secret| of length * |secretlen| is the encryption secret which is used to derive keying * materials passed to this function. |aead_ctx| is AEAD cipher - * context object which must be initialized with encryption key, IV - * |iv| of length |ivlen|, and packet header protection cipher context - * object |hp_ctx| to encrypt outgoing Short packets. + * context object which must be initialized with an encryption key. + * |iv| is IV of length |ivlen|. |hp_ctx| is a packet header + * protection cipher context object. + * + * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if + * that is larger. * * If this function succeeds, |conn| takes ownership of |aead_ctx| and * |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and @@ -3492,7 +4033,7 @@ NGTCP2_EXTERN int ngtcp2_conn_initiate_key_update(ngtcp2_conn *conn, * `ngtcp2_conn_set_tls_error` sets the TLS related error |liberr| in * |conn|. |liberr| must be one of ngtcp2 library error codes (which * is defined as NGTCP2_ERR_* macro, such as - * :macro:`NGTCP2_ERR_TLS_DECRYPT`). In general, error code should be + * :macro:`NGTCP2_ERR_DECRYPT`). In general, error code should be * propagated via return value, but sometimes ngtcp2 API is called * inside callback function of TLS stack and it does not allow to * return ngtcp2 error code directly. In this case, implementation @@ -3510,6 +4051,34 @@ NGTCP2_EXTERN void ngtcp2_conn_set_tls_error(ngtcp2_conn *conn, int liberr); */ NGTCP2_EXTERN int ngtcp2_conn_get_tls_error(ngtcp2_conn *conn); +/** + * @function + * + * `ngtcp2_conn_set_tls_alert` sets a TLS alert |alert| generated by a + * local endpoint to |conn|. + */ +NGTCP2_EXTERN void ngtcp2_conn_set_tls_alert(ngtcp2_conn *conn, uint8_t alert); + +/** + * @function + * + * `ngtcp2_conn_get_tls_alert` returns the value set by + * `ngtcp2_conn_set_tls_alert`. If no value is set, this function + * returns 0. + */ +NGTCP2_EXTERN uint8_t ngtcp2_conn_get_tls_alert(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_set_keep_alive_timeout` sets keep-alive timeout. If + * nonzero value is given, after a connection is idle at least in a + * given amount of time, a keep-alive packet is sent. If 0 is set, + * keep-alive functionality is disabled and this is the default. + */ +NGTCP2_EXTERN void ngtcp2_conn_set_keep_alive_timeout(ngtcp2_conn *conn, + ngtcp2_duration timeout); + /** * @function * @@ -3529,15 +4098,6 @@ NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn); NGTCP2_EXTERN int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts); -/** - * @function - * - * `ngtcp2_conn_get_idle_expiry` returns the time when a connection - * should be closed if it continues to be idle. If idle timeout is - * disabled, this function returns ``UINT64_MAX``. - */ -NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn); - /** * @function * @@ -3548,51 +4108,71 @@ NGTCP2_EXTERN ngtcp2_duration ngtcp2_conn_get_pto(ngtcp2_conn *conn); /** * @function * - * `ngtcp2_conn_set_remote_transport_params` sets transport parameter - * |params| to |conn|. + * `ngtcp2_conn_decode_remote_transport_params` decodes QUIC transport + * parameters from the buffer pointed by |data| of length |datalen|, + * and sets the result to |conn|. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * :macro:`NGTCP2_ERR_PROTO` - * If |conn| is server, and negotiated_version field is not the - * same as the used version. + * :macro:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM` + * The required parameter is missing. + * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM` + * The input is malformed. + * :macro:`NGTCP2_ERR_TRANSPORT_PARAM` + * Failed to validate the remote QUIC transport parameters. + * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE` + * Version negotiation failure. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * User callback failed */ NGTCP2_EXTERN int -ngtcp2_conn_set_remote_transport_params(ngtcp2_conn *conn, - const ngtcp2_transport_params *params); +ngtcp2_conn_decode_remote_transport_params(ngtcp2_conn *conn, + const uint8_t *data, size_t datalen); /** * @function * - * `ngtcp2_conn_get_remote_transport_params` fills settings values in - * |params|. original_connection_id and - * original_connection_id_present are always zero filled. + * `ngtcp2_conn_get_remote_transport_params` returns a pointer to the + * remote QUIC transport parameters. If no remote transport + * parameters are set, it returns NULL. */ -NGTCP2_EXTERN void -ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn, - ngtcp2_transport_params *params); +NGTCP2_EXTERN const ngtcp2_transport_params * +ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn); /** * @function * * `ngtcp2_conn_set_early_remote_transport_params` sets |params| as - * transport parameter previously received from a server. The - * parameters are used to send 0-RTT data. QUIC requires that client - * application should remember transport parameter as well as session - * ticket. - * - * At least following fields must be set: - * - * * initial_max_stream_id_bidi - * * initial_max_stream_id_uni - * * initial_max_stream_data_bidi_local - * * initial_max_stream_data_bidi_remote - * * initial_max_stream_data_uni - * * initial_max_data - */ -NGTCP2_EXTERN void ngtcp2_conn_set_early_remote_transport_params( - ngtcp2_conn *conn, const ngtcp2_transport_params *params); + * transport parameters previously received from a server. The + * parameters are used to send early data. QUIC requires that client + * application should remember transport parameters along with a + * session ticket. + * + * At least following fields should be set: + * + * - initial_max_stream_id_bidi + * - initial_max_stream_id_uni + * - initial_max_stream_data_bidi_local + * - initial_max_stream_data_bidi_remote + * - initial_max_stream_data_uni + * - initial_max_data + * - active_connection_id_limit + * - max_datagram_frame_size (if DATAGRAM extension was negotiated) + * + * The following fields are ignored: + * + * - ack_delay_exponent + * - max_ack_delay + * - initial_scid + * - original_dcid + * - preferred_address and preferred_address_present + * - retry_scid and retry_scid_present + * - stateless_reset_token and stateless_reset_token_present + */ +NGTCP2_EXTERN void ngtcp2_conn_set_early_remote_transport_params_versioned( + ngtcp2_conn *conn, int transport_params_version, + const ngtcp2_transport_params *params); /** * @function @@ -3611,19 +4191,35 @@ NGTCP2_EXTERN void ngtcp2_conn_set_early_remote_transport_params( * :macro:`NGTCP2_ERR_INVALID_STATE` * `ngtcp2_conn_install_tx_handshake_key` has been called. */ -NGTCP2_EXTERN int -ngtcp2_conn_set_local_transport_params(ngtcp2_conn *conn, - const ngtcp2_transport_params *params); +NGTCP2_EXTERN int ngtcp2_conn_set_local_transport_params_versioned( + ngtcp2_conn *conn, int transport_params_version, + const ngtcp2_transport_params *params); /** * @function * - * `ngtcp2_conn_get_local_transport_params` fills settings values in - * |params|. + * `ngtcp2_conn_get_local_transport_params` returns a pointer to the + * local QUIC transport parameters. */ -NGTCP2_EXTERN void -ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn, - ngtcp2_transport_params *params); +NGTCP2_EXTERN const ngtcp2_transport_params * +ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_encode_local_transport_params` encodes the local QUIC + * transport parameters in |dest| of length |destlen|. This is + * equivalent to calling `ngtcp2_conn_get_local_transport_params` and + * then `ngtcp2_encode_transport_params`. + * + * This function returns the number of written, or one of the + * following negative error codes: + * + * :macro:`NGTCP2_ERR_NOBUF` + * Buffer is too small. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_encode_local_transport_params( + ngtcp2_conn *conn, uint8_t *dest, size_t destlen); /** * @function @@ -3636,7 +4232,7 @@ ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn, * 0RTT packet, application can call this function after calling * `ngtcp2_conn_set_early_remote_transport_params`. For 1RTT packet, * application can call this function after calling - * `ngtcp2_conn_set_remote_transport_params` and + * `ngtcp2_conn_decode_remote_transport_params` and * `ngtcp2_conn_install_tx_key`. If ngtcp2 crypto support library is * used, application can call this function after calling * `ngtcp2_crypto_derive_and_install_tx_key` for 1RTT packet. @@ -3664,7 +4260,7 @@ NGTCP2_EXTERN int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, * 0RTT packet, application can call this function after calling * `ngtcp2_conn_set_early_remote_transport_params`. For 1RTT packet, * application can call this function after calling - * `ngtcp2_conn_set_remote_transport_params` and + * `ngtcp2_conn_decode_remote_transport_params` and * `ngtcp2_conn_install_tx_key`. If ngtcp2 crypto support library is * used, application can call this function after calling * `ngtcp2_crypto_derive_and_install_tx_key` for 1RTT packet. @@ -3698,8 +4294,6 @@ NGTCP2_EXTERN int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, * * :macro:`NGTCP2_ERR_NOMEM` * Out of memory - * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND` - * Stream does not exist */ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream(ngtcp2_conn *conn, int64_t stream_id, @@ -3720,8 +4314,6 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream(ngtcp2_conn *conn, * * :macro:`NGTCP2_ERR_NOMEM` * Out of memory - * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND` - * Stream does not exist */ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_write(ngtcp2_conn *conn, int64_t stream_id, @@ -3741,8 +4333,6 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_write(ngtcp2_conn *conn, * * :macro:`NGTCP2_ERR_NOMEM` * Out of memory - * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND` - * Stream does not exist */ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, int64_t stream_id, @@ -3759,7 +4349,7 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, * * :macro:`NGTCP2_WRITE_STREAM_FLAG_NONE` indicates no flag set. */ -#define NGTCP2_WRITE_STREAM_FLAG_NONE 0x00 +#define NGTCP2_WRITE_STREAM_FLAG_NONE 0x00u /** * @macro @@ -3767,7 +4357,7 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, * :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` indicates that more data may * come and should be coalesced into the same packet if possible. */ -#define NGTCP2_WRITE_STREAM_FLAG_MORE 0x01 +#define NGTCP2_WRITE_STREAM_FLAG_MORE 0x01u /** * @macro @@ -3775,7 +4365,7 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, * :macro:`NGTCP2_WRITE_STREAM_FLAG_FIN` indicates that the passed * data is the final part of a stream. */ -#define NGTCP2_WRITE_STREAM_FLAG_FIN 0x02 +#define NGTCP2_WRITE_STREAM_FLAG_FIN 0x02u /** * @function @@ -3784,10 +4374,11 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, * `ngtcp2_conn_writev_stream`. The only difference is that it * conveniently accepts a single buffer. */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream( - ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, ngtcp2_ssize *pdatalen, uint32_t flags, int64_t stream_id, - const uint8_t *data, size_t datalen, ngtcp2_tstamp ts); +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream_versioned( + ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen, + uint32_t flags, int64_t stream_id, const uint8_t *data, size_t datalen, + ngtcp2_tstamp ts); /** * @function @@ -3797,6 +4388,9 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream( * pointed by |dest| of length |destlen|. This function performs QUIC * handshake as well. * + * |destlen| should be at least + * :member:`ngtcp2_settings.max_udp_payload_size`. + * * Specifying -1 to |stream_id| means no new stream data to send. * * If |path| is not ``NULL``, this function stores the network path @@ -3839,8 +4433,8 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream( * by default, application can only send 1 STREAM frame in one QUIC * packet. In order to include more than 1 STREAM frame in one QUIC * packet, specify :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` in |flags|. - * This is analogous to ``MSG_MORE`` flag in ``send(2)``. If the - * :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` is used, there are 4 + * This is analogous to ``MSG_MORE`` flag in :manpage:`send(2)`. If + * the :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` is used, there are 4 * outcomes: * * - The function returns the written length of packet just like @@ -3865,12 +4459,12 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream( * * When application sees :macro:`NGTCP2_ERR_WRITE_MORE`, it must not * call other ngtcp2 API functions (application can still call - * `ngtcp2_conn_write_connection_close` or - * `ngtcp2_conn_write_application_close` to handle error from this + * `ngtcp2_conn_write_connection_close` to handle error from this * function). Just keep calling `ngtcp2_conn_writev_stream`, * `ngtcp2_conn_write_pkt`, or `ngtcp2_conn_writev_datagram` until it * returns a positive number (which indicates a complete packet is - * ready). + * ready). If there is no stream data to include, call this function + * with |stream_id| as -1 to stop coalescing and write a packet. * * This function returns 0 if it cannot write any frame because buffer * is too small, or packet is congestion limited. Application should @@ -3879,6 +4473,12 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream( * This function must not be called from inside the callback * functions. * + * If pacing is enabled, `ngtcp2_conn_update_pkt_tx_time` must be + * called after this function. Application may call this function + * multiple times before calling `ngtcp2_conn_update_pkt_tx_time`. + * Packet pacing is enabled if BBR congestion controller algorithm is + * used. + * * This function returns the number of bytes written in |dest| if it * succeeds, or one of the following negative error codes: * @@ -3892,6 +4492,8 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream( * Packet number is exhausted, and cannot send any more packet. * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` * User callback failed + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + * The total length of stream data is too large. * :macro:`NGTCP2_ERR_STREAM_DATA_BLOCKED` * Stream is blocked because of flow control. * :macro:`NGTCP2_ERR_WRITE_MORE` @@ -3906,10 +4508,11 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream( * connection using `ngtcp2_conn_del`. It is undefined to call the * other library functions. */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream( - ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, ngtcp2_ssize *pdatalen, uint32_t flags, int64_t stream_id, - const ngtcp2_vec *datav, size_t datavcnt, ngtcp2_tstamp ts); +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream_versioned( + ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen, + uint32_t flags, int64_t stream_id, const ngtcp2_vec *datav, size_t datavcnt, + ngtcp2_tstamp ts); /** * @macrosection @@ -3922,7 +4525,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream( * * :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_NONE` indicates no flag set. */ -#define NGTCP2_WRITE_DATAGRAM_FLAG_NONE 0x00 +#define NGTCP2_WRITE_DATAGRAM_FLAG_NONE 0x00u /** * @macro @@ -3930,7 +4533,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream( * :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_MORE` indicates that more data * may come and should be coalesced into the same packet if possible. */ -#define NGTCP2_WRITE_DATAGRAM_FLAG_MORE 0x01 +#define NGTCP2_WRITE_DATAGRAM_FLAG_MORE 0x01u /** * @function @@ -3940,6 +4543,9 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream( * |dest| of length |destlen|. This function performs QUIC handshake * as well. * + * |destlen| should be at least + * :member:`ngtcp2_settings.max_udp_payload_size`. + * * For |path| and |pi| parameters, refer to * `ngtcp2_conn_writev_stream`. * @@ -3947,6 +4553,14 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream( * assigned to |*paccepted| if it is not NULL. The data in DATAGRAM * frame cannot be fragmented; writing partial data is not possible. * + * |dgram_id| is an opaque identifier which should uniquely identify + * the given DATAGRAM. It is passed to :type:`ngtcp2_ack_datagram` + * callback when a packet that contains DATAGRAM frame is + * acknowledged. It is passed to :type:`ngtcp2_lost_datagram` + * callback when a packet that contains DATAGRAM frame is declared + * lost. If an application uses neither of those callbacks, it can + * sets 0 to this parameter. + * * This function might write other frames other than DATAGRAM, just * like `ngtcp2_conn_writev_stream`. * @@ -3978,8 +4592,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream( * * When application sees :macro:`NGTCP2_ERR_WRITE_MORE`, it must not * call other ngtcp2 API functions (application can still call - * `ngtcp2_conn_write_connection_close` or - * `ngtcp2_conn_write_application_close` to handle error from this + * `ngtcp2_conn_write_connection_close` to handle error from this * function). Just keep calling `ngtcp2_conn_writev_datagram`, * `ngtcp2_conn_writev_stream` or `ngtcp2_conn_write_pkt` until it * returns a positive number (which indicates a complete packet is @@ -4011,96 +4624,17 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream( * connection using `ngtcp2_conn_del`. It is undefined to call the * other library functions. */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_datagram( - ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, int *paccepted, uint32_t flags, const ngtcp2_vec *datav, - size_t datavcnt, ngtcp2_tstamp ts); - -/** - * @function - * - * `ngtcp2_conn_write_connection_close` writes a packet which contains - * a CONNECTION_CLOSE frame (type 0x1c) in the buffer pointed by - * |dest| whose capacity is |datalen|. - * - * If |path| is not ``NULL``, this function stores the network path - * with which the packet should be sent. Each addr field must point - * to the buffer which should be at least ``sizeof(struct - * sockaddr_storage)`` bytes long. The assignment might not be done - * if nothing is written to |dest|. - * - * If |pi| is not ``NULL``, this function stores packet metadata in it - * if it succeeds. The metadata includes ECN markings. - * - * This function must not be called from inside the callback - * functions. - * - * At the moment, successful call to this function makes connection - * close. We may change this behaviour in the future to allow - * graceful shutdown. - * - * :macro:`NGTCP2_ERR_NOMEM` - * Out of memory - * :macro:`NGTCP2_ERR_NOBUF` - * Buffer is too small - * :macro:`NGTCP2_ERR_INVALID_STATE` - * The current state does not allow sending CONNECTION_CLOSE. - * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` - * Packet number is exhausted, and cannot send any more packet. - * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` - * User callback failed - */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_connection_close( - ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, uint64_t error_code, ngtcp2_tstamp ts); - -/** - * @function - * - * `ngtcp2_conn_write_application_close` writes a packet which - * contains a CONNECTION_CLOSE frame (type 0x1d) in the buffer pointed - * by |dest| whose capacity is |datalen|. - * - * If |path| is not ``NULL``, this function stores the network path - * with which the packet should be sent. Each addr field must point - * to the buffer which should be at least ``sizeof(struct - * sockaddr_storage)`` bytes long. The assignment might not be done - * if nothing is written to |dest|. - * - * If |pi| is not ``NULL``, this function stores packet metadata in it - * if it succeeds. The metadata includes ECN markings. - * - * If handshake has not been confirmed yet, CONNECTION_CLOSE (type - * 0x1c) with error code :macro:`NGTCP2_APPLICATION_ERROR` is written - * instead. - * - * This function must not be called from inside the callback - * functions. - * - * At the moment, successful call to this function makes connection - * close. We may change this behaviour in the future to allow - * graceful shutdown. - * - * :macro:`NGTCP2_ERR_NOMEM` - * Out of memory - * :macro:`NGTCP2_ERR_NOBUF` - * Buffer is too small - * :macro:`NGTCP2_ERR_INVALID_STATE` - * The current state does not allow sending CONNECTION_CLOSE. - * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` - * Packet number is exhausted, and cannot send any more packet. - * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` - * User callback failed - */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_application_close( - ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, uint64_t app_error_code, ngtcp2_tstamp ts); +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_datagram_versioned( + ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, int *paccepted, + uint32_t flags, uint64_t dgram_id, const ngtcp2_vec *datav, size_t datavcnt, + ngtcp2_tstamp ts); /** * @function * * `ngtcp2_conn_is_in_closing_period` returns nonzero if |conn| is in - * closing period. + * the closing period. */ NGTCP2_EXTERN int ngtcp2_conn_is_in_closing_period(ngtcp2_conn *conn); @@ -4108,7 +4642,7 @@ NGTCP2_EXTERN int ngtcp2_conn_is_in_closing_period(ngtcp2_conn *conn); * @function * * `ngtcp2_conn_is_in_draining_period` returns nonzero if |conn| is in - * draining period. + * the draining period. */ NGTCP2_EXTERN int ngtcp2_conn_is_in_draining_period(ngtcp2_conn *conn); @@ -4121,8 +4655,8 @@ NGTCP2_EXTERN int ngtcp2_conn_is_in_draining_period(ngtcp2_conn *conn); * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND` - * Stream was not found + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory. */ NGTCP2_EXTERN int ngtcp2_conn_extend_max_stream_offset(ngtcp2_conn *conn, int64_t stream_id, @@ -4175,6 +4709,16 @@ NGTCP2_EXTERN void ngtcp2_conn_extend_max_streams_uni(ngtcp2_conn *conn, */ NGTCP2_EXTERN const ngtcp2_cid *ngtcp2_conn_get_dcid(ngtcp2_conn *conn); +/** + * @function + * + * `ngtcp2_conn_get_client_initial_dcid` returns the non-NULL pointer + * to the Destination Connection ID that client sent in its Initial + * packet. + */ +NGTCP2_EXTERN const ngtcp2_cid * +ngtcp2_conn_get_client_initial_dcid(ngtcp2_conn *conn); + /** * @function * @@ -4249,49 +4793,47 @@ NGTCP2_EXTERN size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, /** * @function * - * `ngtcp2_conn_get_negotiated_version` returns the negotiated version. + * `ngtcp2_conn_get_client_chosen_version` returns the client chosen + * version. */ -NGTCP2_EXTERN uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn); +NGTCP2_EXTERN uint32_t ngtcp2_conn_get_client_chosen_version(ngtcp2_conn *conn); /** * @function * - * `ngtcp2_conn_early_data_rejected` tells |conn| that 0-RTT data was - * rejected by a server. + * `ngtcp2_conn_get_negotiated_version` returns the negotiated version. + * + * Until the version is negotiated, this function returns 0. */ -NGTCP2_EXTERN int ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn); +NGTCP2_EXTERN uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn); /** * @function * - * `ngtcp2_conn_get_conn_stat` assigns connection statistics data to - * |*cstat|. + * `ngtcp2_conn_early_data_rejected` tells |conn| that early data was + * rejected by a server. |conn| discards the following connection + * states: + * + * - Any opended streams. + * - Stream identifier allocations. + * - Max data extended by `ngtcp2_conn_extend_max_offset`. + * - Max bidi streams extended by `ngtcp2_conn_extend_max_streams_bidi`. + * - Max uni streams extended by `ngtcp2_conn_extend_max_streams_uni`. + * + * Application which wishes to retransmit early data, it has to open + * streams and send stream data again. */ -NGTCP2_EXTERN void ngtcp2_conn_get_conn_stat(ngtcp2_conn *conn, - ngtcp2_conn_stat *cstat); +NGTCP2_EXTERN void ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn); /** * @function * - * `ngtcp2_conn_on_loss_detection_timer` should be called when a timer - * returned from `ngtcp2_conn_earliest_expiry` fires. - * - * Application should call `ngtcp2_conn_handshake` if handshake has - * not completed, otherwise `ngtcp2_conn_write_pkt` (or - * `ngtcp2_conn_write_stream` if it has data to send) to send PTO - * probe packets. - * - * This function must not be called from inside the callback - * functions. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :macro:`NGTCP2_ERR_NOMEM` - * Out of memory + * `ngtcp2_conn_get_conn_stat` assigns connection statistics data to + * |*cstat|. */ -NGTCP2_EXTERN int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, - ngtcp2_tstamp ts); +NGTCP2_EXTERN void ngtcp2_conn_get_conn_stat_versioned(ngtcp2_conn *conn, + int conn_stat_version, + ngtcp2_conn_stat *cstat); /** * @function @@ -4300,9 +4842,8 @@ NGTCP2_EXTERN int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, * of length |datalen| to the library for transmission. The * encryption level is given in |crypto_level|. * - * Application should keep the buffer pointed by |data| alive until - * the data is acknowledged. The acknowledgement is notified by - * :type:`ngtcp2_acked_crypto_offset` callback. + * The library makes a copy of the buffer pointed by |data| of length + * |datalen|. Application can discard |data|. */ NGTCP2_EXTERN int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn, @@ -4333,7 +4874,7 @@ NGTCP2_EXTERN int ngtcp2_conn_submit_new_token(ngtcp2_conn *conn, * @function * * `ngtcp2_conn_set_local_addr` sets local endpoint address |addr| to - * |conn|. + * the current path of |conn|. */ NGTCP2_EXTERN void ngtcp2_conn_set_local_addr(ngtcp2_conn *conn, const ngtcp2_addr *addr); @@ -4341,11 +4882,11 @@ NGTCP2_EXTERN void ngtcp2_conn_set_local_addr(ngtcp2_conn *conn, /** * @function * - * `ngtcp2_conn_set_remote_addr` sets remote endpoint address |addr| - * to |conn|. + * `ngtcp2_conn_set_path_user_data` sets the |path_user_data| to the + * current path (see :member:`ngtcp2_path.user_data`). */ -NGTCP2_EXTERN void ngtcp2_conn_set_remote_addr(ngtcp2_conn *conn, - const ngtcp2_addr *addr); +NGTCP2_EXTERN void ngtcp2_conn_set_path_user_data(ngtcp2_conn *conn, + void *path_user_data); /** * @function @@ -4354,23 +4895,73 @@ NGTCP2_EXTERN void ngtcp2_conn_set_remote_addr(ngtcp2_conn *conn, */ NGTCP2_EXTERN const ngtcp2_path *ngtcp2_conn_get_path(ngtcp2_conn *conn); +/** + * @function + * + * `ngtcp2_conn_get_max_udp_payload_size` returns the maximum UDP + * payload size that this local endpoint would send. This is the + * value of :member:`ngtcp2_settings.max_udp_payload_size` that is + * passed to `ngtcp2_conn_client_new` or `ngtcp2_conn_server_new`. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_max_udp_payload_size(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_path_max_udp_payload_size` returns the maximum UDP + * payload size for the current path. If + * :member:`ngtcp2_settings.no_udp_payload_size_shaping` is set to + * nonzero, this function is equivalent to + * `ngtcp2_conn_get_max_udp_payload_size`. Otherwise, it returns the + * maximum UDP payload size that is probed for the current path. + */ +NGTCP2_EXTERN size_t +ngtcp2_conn_get_path_max_udp_payload_size(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_initiate_immediate_migration` starts connection + * migration to the given |path|. + * Only client can initiate migration. This function does + * immediate migration; it does not probe peer reachability from a new + * local address. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_INVALID_STATE` + * Migration is disabled; or handshake is not yet confirmed; or + * client is migrating to server's preferred address. + * :macro:`NGTCP2_ERR_CONN_ID_BLOCKED` + * No unused connection ID is available. + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + * |local_addr| equals the current local address. + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory + */ +NGTCP2_EXTERN int ngtcp2_conn_initiate_immediate_migration( + ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_tstamp ts); + /** * @function * * `ngtcp2_conn_initiate_migration` starts connection migration to the - * given |path| which must not be ``NULL``. Only client can initiate - * migration. This function does immediate migration; it does not - * probe peer reachability from a new local address. + * given |path|. Only client can initiate migration. Unlike + * `ngtcp2_conn_initiate_immediate_migration`, this function starts a + * path validation with a new path and migrate to the new path after + * successful path validation. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :macro:`NGTCP2_ERR_INVALID_STATE` - * Migration is disabled. + * Migration is disabled; or handshake is not yet confirmed; or + * client is migrating to server's preferred address. * :macro:`NGTCP2_ERR_CONN_ID_BLOCKED` * No unused connection ID is available. * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` - * |path| equals the current path. + * |local_addr| equals the current local address. * :macro:`NGTCP2_ERR_NOMEM` * Out of memory */ @@ -4394,6 +4985,16 @@ NGTCP2_EXTERN uint64_t ngtcp2_conn_get_max_local_streams_uni(ngtcp2_conn *conn); */ NGTCP2_EXTERN uint64_t ngtcp2_conn_get_max_data_left(ngtcp2_conn *conn); +/** + * @function + * + * `ngtcp2_conn_get_max_stream_data_left` returns the number of bytes + * that this local endpoint can send to a stream identified by + * |stream_id|. If no such stream is found, this function returns 0. + */ +NGTCP2_EXTERN uint64_t ngtcp2_conn_get_max_stream_data_left(ngtcp2_conn *conn, + int64_t stream_id); + /** * @function * @@ -4412,6 +5013,15 @@ NGTCP2_EXTERN uint64_t ngtcp2_conn_get_streams_bidi_left(ngtcp2_conn *conn); */ NGTCP2_EXTERN uint64_t ngtcp2_conn_get_streams_uni_left(ngtcp2_conn *conn); +/** + * @function + * + * `ngtcp2_conn_get_cwnd_left` returns the cwnd minus the number of + * bytes in flight on the current path. If the former is smaller than + * the latter, this function returns 0. + */ +NGTCP2_EXTERN uint64_t ngtcp2_conn_get_cwnd_left(ngtcp2_conn *conn); + /** * @function * @@ -4436,7 +5046,7 @@ ngtcp2_conn_get_initial_crypto_ctx(ngtcp2_conn *conn); /** * @function * - * `ngtcp2_conn_set_crypto_ctx` sets |ctx| for Handshake/Short packet + * `ngtcp2_conn_set_crypto_ctx` sets |ctx| for Handshake/1RTT packet * encryption. The passed data will be passed to * :type:`ngtcp2_encrypt`, :type:`ngtcp2_decrypt` and * :type:`ngtcp2_hp_mask` callbacks. @@ -4485,7 +5095,7 @@ ngtcp2_conn_set_retry_aead(ngtcp2_conn *conn, const ngtcp2_crypto_aead *aead, * @function * * `ngtcp2_conn_get_crypto_ctx` returns :type:`ngtcp2_crypto_ctx` - * object for Handshake/Short packet encryption. + * object for Handshake/1RTT packet encryption. */ NGTCP2_EXTERN const ngtcp2_crypto_ctx * ngtcp2_conn_get_crypto_ctx(ngtcp2_conn *conn); @@ -4528,33 +5138,213 @@ typedef enum ngtcp2_connection_close_error_code_type { * indicates the error code is application error code. */ NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION, + /** + * :enum:`NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION` + * is a special case of QUIC transport error, and it indicates that + * client receives Version Negotiation packet. + */ + NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION, + /** + * :enum:`NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE` + * is a special case of QUIC transport error, and it indicates that + * connection is closed because of idle timeout. + */ + NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE } ngtcp2_connection_close_error_code_type; /** * @struct * - * `ngtcp2_connection_close_error_code` contains connection error code - * and its type. + * :type:`ngtcp2_connection_close_error` contains connection + * error code, its type, and the optional reason phrase. */ -typedef struct ngtcp2_connection_close_error_code { +typedef struct ngtcp2_connection_close_error { + /** + * :member:`type` is the type of :member:`error_code`. + */ + ngtcp2_connection_close_error_code_type type; /** * :member:`error_code` is the error code for connection closure. */ uint64_t error_code; /** - * :member:`type` is the type of :member:`error_code`. + * :member:`frame_type` is the type of QUIC frame which triggers + * this connection error. This field is set to 0 if the frame type + * is unknown. */ - ngtcp2_connection_close_error_code_type type; -} ngtcp2_connection_close_error_code; + uint64_t frame_type; + /** + * :member:`reason` points to the buffer which contains a reason + * phrase. It may be NULL if there is no reason phrase. If it is + * received from a remote endpoint, it is truncated to at most 1024 + * bytes. + */ + uint8_t *reason; + /** + * :member:`reasonlen` is the length of data pointed by + * :member:`reason`. + */ + size_t reasonlen; +} ngtcp2_connection_close_error; + +/** + * @function + * + * `ngtcp2_connection_close_error_default` initializes |ccerr| with + * the default values. It sets the following fields: + * + * - :member:`type ` = + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT` + * - :member:`error_code ` = + * :macro:`NGTCP2_NO_ERROR`. + * - :member:`frame_type ` = + * 0 + * - :member:`reason ` = NULL + * - :member:`reasonlen ` = 0 + */ +NGTCP2_EXTERN void +ngtcp2_connection_close_error_default(ngtcp2_connection_close_error *ccerr); + +/** + * @function + * + * `ngtcp2_connection_close_error_set_transport_error` sets + * :member:`ccerr->type ` to + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`, + * and :member:`ccerr->error_code + * ` to |error_code|. + * |reason| is the reason phrase of length |reasonlen|. This function + * does not make a copy of the reason phrase. + */ +NGTCP2_EXTERN void ngtcp2_connection_close_error_set_transport_error( + ngtcp2_connection_close_error *ccerr, uint64_t error_code, + const uint8_t *reason, size_t reasonlen); + +/** + * @function + * + * `ngtcp2_connection_close_error_set_transport_error_liberr` sets + * type and error_code based on |liberr|. + * + * If |liberr| is :macro:`NGTCP2_ERR_RECV_VERSION_NEGOTIATION`, + * :member:`ccerr->type ` is set + * to + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION`, + * and :member:`ccerr->error_code + * ` to + * :macro:`NGTCP2_NO_ERROR`. If |liberr| is + * :macro:`NGTCP2_ERR_IDLE_CLOSE`, :member:`ccerr->type + * ` is set to + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE`, + * and :member:`ccerr->error_code + * ` to + * :macro:`NGTCP2_NO_ERROR`. Otherwise, :member:`ccerr->type + * ` is set to + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`, + * and :member:`ccerr->error_code + * ` is set to an error code + * inferred by |liberr| (see + * `ngtcp2_err_infer_quic_transport_error_code`). |reason| is the + * reason phrase of length |reasonlen|. This function does not make a + * copy of the reason phrase. + */ +NGTCP2_EXTERN void ngtcp2_connection_close_error_set_transport_error_liberr( + ngtcp2_connection_close_error *ccerr, int liberr, const uint8_t *reason, + size_t reasonlen); + +/** + * @function + * + * `ngtcp2_connection_close_error_set_transport_error_tls_alert` sets + * :member:`ccerr->type ` to + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`, + * and :member:`ccerr->error_code + * ` to bitwise-OR of + * :macro:`NGTCP2_CRYPTO_ERROR` and |tls_alert|. |reason| is the + * reason phrase of length |reasonlen|. This function does not make a + * copy of the reason phrase. + */ +NGTCP2_EXTERN void ngtcp2_connection_close_error_set_transport_error_tls_alert( + ngtcp2_connection_close_error *ccerr, uint8_t tls_alert, + const uint8_t *reason, size_t reasonlen); + +/** + * @function + * + * `ngtcp2_connection_close_error_set_application_error` sets + * :member:`ccerr->type ` to + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION`, + * and :member:`ccerr->error_code + * ` to |error_code|. + * |reason| is the reason phrase of length |reasonlen|. This function + * does not make a copy of the reason phrase. + */ +NGTCP2_EXTERN void ngtcp2_connection_close_error_set_application_error( + ngtcp2_connection_close_error *ccerr, uint64_t error_code, + const uint8_t *reason, size_t reasonlen); + +/** + * @function + * + * `ngtcp2_conn_write_connection_close` writes a packet which contains + * CONNECTION_CLOSE frame(s) (type 0x1c or 0x1d) in the buffer pointed + * by |dest| whose capacity is |destlen|. + * + * For client, |destlen| should be at least + * :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE`. + * + * If |path| is not ``NULL``, this function stores the network path + * with which the packet should be sent. Each addr field must point + * to the buffer which should be at least ``sizeof(struct + * sockaddr_storage)`` bytes long. The assignment might not be done + * if nothing is written to |dest|. + * + * If |pi| is not ``NULL``, this function stores packet metadata in it + * if it succeeds. The metadata includes ECN markings. + * + * If :member:`ccerr->type ` == + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`, + * this function sends CONNECTION_CLOSE (type 0x1c) frame. If + * :member:`ccerr->type ` == + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION`, + * it sends CONNECTION_CLOSE (type 0x1d) frame. Otherwise, it does + * not produce any data, and returns 0. + * + * This function must not be called from inside the callback + * functions. + * + * At the moment, successful call to this function makes connection + * close. We may change this behaviour in the future to allow + * graceful shutdown. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory + * :macro:`NGTCP2_ERR_NOBUF` + * Buffer is too small + * :macro:`NGTCP2_ERR_INVALID_STATE` + * The current state does not allow sending CONNECTION_CLOSE. + * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` + * Packet number is exhausted, and cannot send any more packet. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * User callback failed + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned( + ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, + const ngtcp2_connection_close_error *ccerr, ngtcp2_tstamp ts); /** * @function * - * `ngtcp2_conn_get_connection_close_error_code` stores the received - * connection close error code in |ccec|. + * `ngtcp2_conn_get_connection_close_error` stores the received + * connection close error in |ccerr|. */ -NGTCP2_EXTERN void ngtcp2_conn_get_connection_close_error_code( - ngtcp2_conn *conn, ngtcp2_connection_close_error_code *ccec); +NGTCP2_EXTERN void +ngtcp2_conn_get_connection_close_error(ngtcp2_conn *conn, + ngtcp2_connection_close_error *ccerr); /** * @function @@ -4597,13 +5387,48 @@ NGTCP2_EXTERN int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn, int64_t stream_id, void *stream_user_data); +/** + * @function + * + * `ngtcp2_conn_update_pkt_tx_time` sets the time instant of the next + * packet transmission. This function is noop if packet pacing is + * disabled. If packet pacing is enabled, this function must be + * called after (multiple invocation of) `ngtcp2_conn_writev_stream`. + * If packet aggregation (e.g., packet batching, GSO) is used, call + * this function after all aggregated datagrams are sent, which + * indicates multiple invocation of `ngtcp2_conn_writev_stream`. + */ +NGTCP2_EXTERN void ngtcp2_conn_update_pkt_tx_time(ngtcp2_conn *conn, + ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_get_send_quantum` returns the maximum number of bytes + * that can be sent in one go without packet spacing. If packet + * pacing is disabled, this function returns SIZE_MAX. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_send_quantum(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_stream_loss_count` returns the number of packets + * that contain STREAM frame for a stream identified by |stream_id| + * and are declared to be lost. The number may include the spurious + * losses. If no stream identified by |stream_id| is found, this + * function returns 0. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_stream_loss_count(ngtcp2_conn *conn, + int64_t stream_id); + /** * @function * * `ngtcp2_strerror` returns the text representation of |liberr|. * |liberr| must be one of ngtcp2 library error codes (which is * defined as NGTCP2_ERR_* macro, such as - * :macro:`NGTCP2_ERR_TLS_DECRYPT`). + * :macro:`NGTCP2_ERR_DECRYPT`). */ NGTCP2_EXTERN const char *ngtcp2_strerror(int liberr); @@ -4613,7 +5438,7 @@ NGTCP2_EXTERN const char *ngtcp2_strerror(int liberr); * `ngtcp2_err_is_fatal` returns nonzero if |liberr| is a fatal error. * |liberr| must be one of ngtcp2 library error codes (which is * defined as NGTCP2_ERR_* macro, such as - * :macro:`NGTCP2_ERR_TLS_DECRYPT`). + * :macro:`NGTCP2_ERR_DECRYPT`). */ NGTCP2_EXTERN int ngtcp2_err_is_fatal(int liberr); @@ -4623,7 +5448,7 @@ NGTCP2_EXTERN int ngtcp2_err_is_fatal(int liberr); * `ngtcp2_err_infer_quic_transport_error_code` returns a QUIC * transport error code which corresponds to |liberr|. |liberr| must * be one of ngtcp2 library error codes (which is defined as - * NGTCP2_ERR_* macro, such as :macro:`NGTCP2_ERR_TLS_DECRYPT`). + * NGTCP2_ERR_* macro, such as :macro:`NGTCP2_ERR_DECRYPT`). */ NGTCP2_EXTERN uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr); @@ -4634,8 +5459,22 @@ NGTCP2_EXTERN uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr); * returns |dest|. */ NGTCP2_EXTERN ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, - const struct sockaddr *addr, - size_t addrlen, void *user_data); + const ngtcp2_sockaddr *addr, + ngtcp2_socklen addrlen); + +/** + * @function + * + * `ngtcp2_addr_copy_byte` copies |addr| of length |addrlen| into the + * buffer pointed by :member:`dest->addr `. + * :member:`dest->addrlen ` is updated to have + * |addrlen|. This function assumes that :member:`dest->addr + * ` points to a buffer which has a sufficient + * capacity to store the copy. + */ +NGTCP2_EXTERN void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, + const ngtcp2_sockaddr *addr, + ngtcp2_socklen addrlen); /** * @function @@ -4644,12 +5483,11 @@ NGTCP2_EXTERN ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, * arguments. This function copies |local_addr| and |remote_addr|. */ NGTCP2_EXTERN void ngtcp2_path_storage_init(ngtcp2_path_storage *ps, - const struct sockaddr *local_addr, - size_t local_addrlen, - void *local_user_data, - const struct sockaddr *remote_addr, - size_t remote_addrlen, - void *remote_user_data); + const ngtcp2_sockaddr *local_addr, + ngtcp2_socklen local_addrlen, + const ngtcp2_sockaddr *remote_addr, + ngtcp2_socklen remote_addrlen, + void *user_data); /** * @function @@ -4671,8 +5509,13 @@ NGTCP2_EXTERN void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps); * * :type:`initial_rtt ` = * :macro:`NGTCP2_DEFAULT_INITIAL_RTT` * * :type:`ack_thresh ` = 2 + * * :type:`max_udp_payload_size + * ` = 1452 + * * :type:`handshake_timeout ` = + * :macro:`NGTCP2_DEFAULT_HANDSHAKE_TIMEOUT`. */ -NGTCP2_EXTERN void ngtcp2_settings_default(ngtcp2_settings *settings); +NGTCP2_EXTERN void ngtcp2_settings_default_versioned(int settings_version, + ngtcp2_settings *settings); /** * @function @@ -4683,7 +5526,7 @@ NGTCP2_EXTERN void ngtcp2_settings_default(ngtcp2_settings *settings); * * * :type:`max_udp_payload_size * ` = - * :macro:`NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE` + * :macro:`NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE` * * :type:`ack_delay_exponent * ` = * :macro:`NGTCP2_DEFAULT_ACK_DELAY_EXPONENT` @@ -4694,7 +5537,8 @@ NGTCP2_EXTERN void ngtcp2_settings_default(ngtcp2_settings *settings); * :macro:`NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT` */ NGTCP2_EXTERN void -ngtcp2_transport_params_default(ngtcp2_transport_params *params); +ngtcp2_transport_params_default_versioned(int transport_params_version, + ngtcp2_transport_params *params); /** * @function @@ -4746,13 +5590,14 @@ typedef struct ngtcp2_info { /** * @function * - * Returns a pointer to a ngtcp2_info struct with version information - * about the run-time library in use. The |least_version| argument - * can be set to a 24 bit numerical value for the least accepted - * version number and if the condition is not met, this function will - * return a ``NULL``. Pass in 0 to skip the version checking. + * `ngtcp2_version` returns a pointer to a ngtcp2_info struct with + * version information about the run-time library in use. The + * |least_version| argument can be set to a 24 bit numerical value for + * the least accepted version number and if the condition is not met, + * this function will return a ``NULL``. Pass in 0 to skip the + * version checking. */ -NGTCP2_EXTERN ngtcp2_info *ngtcp2_version(int least_version); +NGTCP2_EXTERN const ngtcp2_info *ngtcp2_version(int least_version); /** * @function @@ -4763,66 +5608,211 @@ NGTCP2_EXTERN ngtcp2_info *ngtcp2_version(int least_version); NGTCP2_EXTERN int ngtcp2_is_bidi_stream(int64_t stream_id); /** - * @enum + * @function * - * :type:`ngtcp2_log_event` defines an event of ngtcp2 library - * internal logger. + * `ngtcp2_path_copy` copies |src| into |dest|. This function assumes + * that |dest| has enough buffer to store the deep copy of + * :member:`src->local ` and :member:`src->remote + * `. */ -typedef enum { - /** - * :enum:`NGTCP2_LOG_EVENT_NONE` represents no event. - */ - NGTCP2_LOG_EVENT_NONE, - /** - * :enum:`NGTCP2_LOG_EVENT_CON` is a connection (catch-all) event - */ - NGTCP2_LOG_EVENT_CON, - /** - * :enum:`NGTCP2_LOG_EVENT_PKT` is a packet event. - */ - NGTCP2_LOG_EVENT_PKT, - /** - * :enum:`NGTCP2_LOG_EVENT_FRM` is a QUIC frame event. - */ - NGTCP2_LOG_EVENT_FRM, - /** - * :enum:`NGTCP2_LOG_EVENT_RCV` is a congestion and recovery event. - */ - NGTCP2_LOG_EVENT_RCV, - /** - * :enum:`NGTCP2_LOG_EVENT_CRY` is a crypto event. - */ - NGTCP2_LOG_EVENT_CRY, - /** - * :enum:`NGTCP2_LOG_EVENT_PTV` is a path validation event. - */ - NGTCP2_LOG_EVENT_PTV, -} ngtcp2_log_event; +NGTCP2_EXTERN void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src); /** * @function * - * `ngtcp2_log_info` writes info level log. + * `ngtcp2_path_eq` returns nonzero if |a| and |b| shares the same + * local and remote addresses. */ -NGTCP2_EXTERN void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, - const char *fmt, ...); +NGTCP2_EXTERN int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b); /** * @function * - * `ngtcp2_path_copy` copies |src| into |dest|. This function assumes - * that |dest| has enough buffer to store the deep copy of src->local - * and src->remote. + * `ngtcp2_is_supported_version` returns nonzero if the library supports + * QUIC version |version|. */ -NGTCP2_EXTERN void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src); +NGTCP2_EXTERN int ngtcp2_is_supported_version(uint32_t version); + +/* + * @function + * + * `ngtcp2_is_reserved_version` returns nonzero if |version| is a + * reserved version. + */ +NGTCP2_EXTERN int ngtcp2_is_reserved_version(uint32_t version); /** * @function * - * `ngtcp2_path_eq` returns nonzero if |a| and |b| shares the same - * local and remote addresses. + * `ngtcp2_select_version` selects and returns a version from the + * version set |offered_versions| of |offered_versionslen| elements. + * |preferred_versions| of |preferred_versionslen| elements specifies + * the preference of versions, which is sorted in the order of + * preference. All versions included in |preferred_versions| must be + * supported by the library, that is, passing a version to + * `ngtcp2_is_supported_version` must return nonzero. This function + * is intended to be used by client when it receives Version + * Negotiation packet. If no version is selected, this function + * returns 0. */ -NGTCP2_EXTERN int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b); +NGTCP2_EXTERN uint32_t ngtcp2_select_version(const uint32_t *preferred_versions, + size_t preferred_versionslen, + const uint32_t *offered_versions, + size_t offered_versionslen); + +/* + * Versioned function wrappers + */ + +/* + * `ngtcp2_conn_read_pkt` is a wrapper around + * `ngtcp2_conn_read_pkt_versioned` to set the correct struct version. + */ +#define ngtcp2_conn_read_pkt(CONN, PATH, PI, PKT, PKTLEN, TS) \ + ngtcp2_conn_read_pkt_versioned((CONN), (PATH), NGTCP2_PKT_INFO_VERSION, \ + (PI), (PKT), (PKTLEN), (TS)) + +/* + * `ngtcp2_conn_write_pkt` is a wrapper around + * `ngtcp2_conn_write_pkt_versioned` to set the correct struct + * version. + */ +#define ngtcp2_conn_write_pkt(CONN, PATH, PI, DEST, DESTLEN, TS) \ + ngtcp2_conn_write_pkt_versioned((CONN), (PATH), NGTCP2_PKT_INFO_VERSION, \ + (PI), (DEST), (DESTLEN), (TS)) + +/* + * `ngtcp2_conn_write_stream` is a wrapper around + * `ngtcp2_conn_write_stream_versioned` to set the correct struct + * version. + */ +#define ngtcp2_conn_write_stream(CONN, PATH, PI, DEST, DESTLEN, PDATALEN, \ + FLAGS, STREAM_ID, DATA, DATALEN, TS) \ + ngtcp2_conn_write_stream_versioned( \ + (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN), \ + (PDATALEN), (FLAGS), (STREAM_ID), (DATA), (DATALEN), (TS)) + +/* + * `ngtcp2_conn_writev_stream` is a wrapper around + * `ngtcp2_conn_writev_stream_versioned` to set the correct struct + * version. + */ +#define ngtcp2_conn_writev_stream(CONN, PATH, PI, DEST, DESTLEN, PDATALEN, \ + FLAGS, STREAM_ID, DATAV, DATAVCNT, TS) \ + ngtcp2_conn_writev_stream_versioned( \ + (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN), \ + (PDATALEN), (FLAGS), (STREAM_ID), (DATAV), (DATAVCNT), (TS)) + +/* + * `ngtcp2_conn_writev_datagram` is a wrapper around + * `ngtcp2_conn_writev_datagram_versioned` to set the correct struct + * version. + */ +#define ngtcp2_conn_writev_datagram(CONN, PATH, PI, DEST, DESTLEN, PACCEPTED, \ + FLAGS, DGRAM_ID, DATAV, DATAVCNT, TS) \ + ngtcp2_conn_writev_datagram_versioned( \ + (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN), \ + (PACCEPTED), (FLAGS), (DGRAM_ID), (DATAV), (DATAVCNT), (TS)) + +/* + * `ngtcp2_conn_write_connection_close` is a wrapper around + * `ngtcp2_conn_write_connection_close_versioned` to set the correct + * struct version. + */ +#define ngtcp2_conn_write_connection_close(CONN, PATH, PI, DEST, DESTLEN, \ + CCERR, TS) \ + ngtcp2_conn_write_connection_close_versioned( \ + (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN), \ + (CCERR), (TS)) + +/* + * `ngtcp2_encode_transport_params` is a wrapper around + * `ngtcp2_encode_transport_params_versioned` to set the correct + * struct version. + */ +#define ngtcp2_encode_transport_params(DEST, DESTLEN, EXTTYPE, PARAMS) \ + ngtcp2_encode_transport_params_versioned( \ + (DEST), (DESTLEN), (EXTTYPE), NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS)) + +/* + * `ngtcp2_decode_transport_params` is a wrapper around + * `ngtcp2_decode_transport_params_versioned` to set the correct + * struct version. + */ +#define ngtcp2_decode_transport_params(PARAMS, EXTTYPE, DATA, DATALEN) \ + ngtcp2_decode_transport_params_versioned( \ + NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS), (EXTTYPE), (DATA), (DATALEN)) + +/* + * `ngtcp2_conn_client_new` is a wrapper around + * `ngtcp2_conn_client_new_versioned` to set the correct struct + * version. + */ +#define ngtcp2_conn_client_new(PCONN, DCID, SCID, PATH, VERSION, CALLBACKS, \ + SETTINGS, PARAMS, MEM, USER_DATA) \ + ngtcp2_conn_client_new_versioned( \ + (PCONN), (DCID), (SCID), (PATH), (VERSION), NGTCP2_CALLBACKS_VERSION, \ + (CALLBACKS), NGTCP2_SETTINGS_VERSION, (SETTINGS), \ + NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS), (MEM), (USER_DATA)) + +/* + * `ngtcp2_conn_server_new` is a wrapper around + * `ngtcp2_conn_server_new_versioned` to set the correct struct + * version. + */ +#define ngtcp2_conn_server_new(PCONN, DCID, SCID, PATH, VERSION, CALLBACKS, \ + SETTINGS, PARAMS, MEM, USER_DATA) \ + ngtcp2_conn_server_new_versioned( \ + (PCONN), (DCID), (SCID), (PATH), (VERSION), NGTCP2_CALLBACKS_VERSION, \ + (CALLBACKS), NGTCP2_SETTINGS_VERSION, (SETTINGS), \ + NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS), (MEM), (USER_DATA)) + +/* + * `ngtcp2_conn_set_early_remote_transport_params` is a wrapper around + * `ngtcp2_conn_set_early_remote_transport_params_versioned` to set + * the correct struct version. + */ +#define ngtcp2_conn_set_early_remote_transport_params(CONN, PARAMS) \ + ngtcp2_conn_set_early_remote_transport_params_versioned( \ + (CONN), NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS)) + +/* + * `ngtcp2_conn_set_local_transport_params` is a wrapper around + * `ngtcp2_conn_set_local_transport_params_versioned` to set the + * correct struct version. + */ +#define ngtcp2_conn_set_local_transport_params(CONN, PARAMS) \ + ngtcp2_conn_set_local_transport_params_versioned( \ + (CONN), NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS)) + +/* + * `ngtcp2_transport_params_default` is a wrapper around + * `ngtcp2_transport_params_default_versioned` to set the correct + * struct version. + */ +#define ngtcp2_transport_params_default(PARAMS) \ + ngtcp2_transport_params_default_versioned(NGTCP2_TRANSPORT_PARAMS_VERSION, \ + (PARAMS)) + +/* + * `ngtcp2_conn_get_conn_stat` is a wrapper around + * `ngtcp2_conn_get_conn_stat_versioned` to set the correct struct + * version. + */ +#define ngtcp2_conn_get_conn_stat(CONN, CSTAT) \ + ngtcp2_conn_get_conn_stat_versioned((CONN), NGTCP2_CONN_STAT_VERSION, (CSTAT)) + +/* + * `ngtcp2_settings_default` is a wrapper around + * `ngtcp2_settings_default_versioned` to set the correct struct + * version. + */ +#define ngtcp2_settings_default(SETTINGS) \ + ngtcp2_settings_default_versioned(NGTCP2_SETTINGS_VERSION, (SETTINGS)) + +#ifdef _MSC_VER +# pragma warning(pop) +#endif #ifdef __cplusplus } diff --git a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h index fb0671de9fcb5d..9f7592b84a4585 100644 --- a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h +++ b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h @@ -36,7 +36,7 @@ * * Version number of the ngtcp2 library release. */ -#define NGTCP2_VERSION "0.1.0-DEV" +#define NGTCP2_VERSION "0.8.1" /** * @macro @@ -46,6 +46,6 @@ * number, 8 bits for minor and 8 bits for patch. Version 1.2.3 * becomes 0x010203. */ -#define NGTCP2_VERSION_NUM 0x000100 +#define NGTCP2_VERSION_NUM 0x000801 #endif /* VERSION_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c index 7a7f3e469a2f0e..02c45de90d112a 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c @@ -26,22 +26,31 @@ #include -int ngtcp2_acktr_entry_new(ngtcp2_acktr_entry **ent, int64_t pkt_num, - ngtcp2_tstamp tstamp, const ngtcp2_mem *mem) { - *ent = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_acktr_entry)); +#include "ngtcp2_macro.h" + +static void acktr_entry_init(ngtcp2_acktr_entry *ent, int64_t pkt_num, + ngtcp2_tstamp tstamp) { + ent->pkt_num = pkt_num; + ent->len = 1; + ent->tstamp = tstamp; +} + +int ngtcp2_acktr_entry_objalloc_new(ngtcp2_acktr_entry **ent, int64_t pkt_num, + ngtcp2_tstamp tstamp, + ngtcp2_objalloc *objalloc) { + *ent = ngtcp2_objalloc_acktr_entry_get(objalloc); if (*ent == NULL) { return NGTCP2_ERR_NOMEM; } - (*ent)->pkt_num = pkt_num; - (*ent)->len = 1; - (*ent)->tstamp = tstamp; + acktr_entry_init(*ent, pkt_num, tstamp); return 0; } -void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, const ngtcp2_mem *mem) { - ngtcp2_mem_free(mem, ent); +void ngtcp2_acktr_entry_objalloc_del(ngtcp2_acktr_entry *ent, + ngtcp2_objalloc *objalloc) { + ngtcp2_objalloc_acktr_entry_release(objalloc, ent); } static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { @@ -52,17 +61,15 @@ int ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log, const ngtcp2_mem *mem) { int rv; - rv = ngtcp2_ringbuf_init(&acktr->acks, 128, sizeof(ngtcp2_acktr_ack_entry), + ngtcp2_objalloc_acktr_entry_init(&acktr->objalloc, 32, mem); + + rv = ngtcp2_ringbuf_init(&acktr->acks, 32, sizeof(ngtcp2_acktr_ack_entry), mem); if (rv != 0) { return rv; } - rv = ngtcp2_ksl_init(&acktr->ents, greater, sizeof(int64_t), mem); - if (rv != 0) { - ngtcp2_ringbuf_free(&acktr->acks); - return rv; - } + ngtcp2_ksl_init(&acktr->ents, greater, sizeof(int64_t), mem); acktr->log = log; acktr->mem = mem; @@ -74,24 +81,31 @@ int ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log, } void ngtcp2_acktr_free(ngtcp2_acktr *acktr) { +#ifdef NOMEMPOOL ngtcp2_ksl_it it; +#endif /* NOMEMPOOL */ if (acktr == NULL) { return; } +#ifdef NOMEMPOOL for (it = ngtcp2_ksl_begin(&acktr->ents); !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { - ngtcp2_acktr_entry_del(ngtcp2_ksl_it_get(&it), acktr->mem); + ngtcp2_acktr_entry_objalloc_del(ngtcp2_ksl_it_get(&it), &acktr->objalloc); } +#endif /* NOMEMPOOL */ + ngtcp2_ksl_free(&acktr->ents); ngtcp2_ringbuf_free(&acktr->acks); + + ngtcp2_objalloc_free(&acktr->objalloc); } int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack, ngtcp2_tstamp ts) { - ngtcp2_ksl_it it; + ngtcp2_ksl_it it, prev_it; ngtcp2_acktr_entry *ent, *prev_ent, *delent; int rv; int added = 0; @@ -122,16 +136,17 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack, added = 1; } } else { - ngtcp2_ksl_it_prev(&it); - prev_ent = ngtcp2_ksl_it_get(&it); + prev_it = it; + ngtcp2_ksl_it_prev(&prev_it); + prev_ent = ngtcp2_ksl_it_get(&prev_it); assert(prev_ent->pkt_num >= pkt_num + (int64_t)prev_ent->len); if (ent->pkt_num + 1 == pkt_num) { if (prev_ent->pkt_num == pkt_num + (int64_t)prev_ent->len) { prev_ent->len += ent->len + 1; - ngtcp2_ksl_remove(&acktr->ents, NULL, &ent->pkt_num); - ngtcp2_acktr_entry_del(ent, acktr->mem); + ngtcp2_ksl_remove_hint(&acktr->ents, NULL, &it, &ent->pkt_num); + ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc); added = 1; } else { ngtcp2_ksl_update_key(&acktr->ents, &ent->pkt_num, &pkt_num); @@ -149,13 +164,13 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack, } if (!added) { - rv = ngtcp2_acktr_entry_new(&ent, pkt_num, ts, acktr->mem); + rv = ngtcp2_acktr_entry_objalloc_new(&ent, pkt_num, ts, &acktr->objalloc); if (rv != 0) { return rv; } rv = ngtcp2_ksl_insert(&acktr->ents, NULL, &ent->pkt_num, ent); if (rv != 0) { - ngtcp2_acktr_entry_del(ent, acktr->mem); + ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc); return rv; } } @@ -171,8 +186,8 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack, it = ngtcp2_ksl_end(&acktr->ents); ngtcp2_ksl_it_prev(&it); delent = ngtcp2_ksl_it_get(&it); - ngtcp2_ksl_remove(&acktr->ents, NULL, &delent->pkt_num); - ngtcp2_acktr_entry_del(delent, acktr->mem); + ngtcp2_ksl_remove_hint(&acktr->ents, NULL, &it, &delent->pkt_num); + ngtcp2_acktr_entry_objalloc_del(delent, &acktr->objalloc); } return 0; @@ -186,8 +201,8 @@ void ngtcp2_acktr_forget(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent) { for (; !ngtcp2_ksl_it_end(&it);) { ent = ngtcp2_ksl_it_get(&it); - ngtcp2_ksl_remove(&acktr->ents, &it, &ent->pkt_num); - ngtcp2_acktr_entry_del(ent, acktr->mem); + ngtcp2_ksl_remove_hint(&acktr->ents, &it, &it, &ent->pkt_num); + ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc); } } @@ -212,13 +227,14 @@ ngtcp2_acktr_ack_entry *ngtcp2_acktr_add_ack(ngtcp2_acktr *acktr, } /* - * acktr_remove removes |ent| from |acktr|. The iterator which points - * to the entry next to |ent| is assigned to |it|. + * acktr_remove removes |ent| from |acktr|. |it| must point to the + * node whose key identifies |ent|. The iterator which points to the + * entry next to |ent| is assigned to |it|. */ static void acktr_remove(ngtcp2_acktr *acktr, ngtcp2_ksl_it *it, ngtcp2_acktr_entry *ent) { - ngtcp2_ksl_remove(&acktr->ents, it, &ent->pkt_num); - ngtcp2_acktr_entry_del(ent, acktr->mem); + ngtcp2_ksl_remove_hint(&acktr->ents, it, it, &ent->pkt_num); + ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc); } static void acktr_on_ack(ngtcp2_acktr *acktr, ngtcp2_ringbuf *rb, @@ -310,7 +326,8 @@ void ngtcp2_acktr_commit_ack(ngtcp2_acktr *acktr) { int ngtcp2_acktr_require_active_ack(ngtcp2_acktr *acktr, ngtcp2_duration max_ack_delay, ngtcp2_tstamp ts) { - return acktr->first_unacked_ts <= ts - max_ack_delay; + return acktr->first_unacked_ts != UINT64_MAX && + acktr->first_unacked_ts + max_ack_delay <= ts; } void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr) { diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h index 51e1588e3c4a8f..1b00d64fe62cb6 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h @@ -35,6 +35,7 @@ #include "ngtcp2_ringbuf.h" #include "ngtcp2_ksl.h" #include "ngtcp2_pkt.h" +#include "ngtcp2_objalloc.h" /* NGTCP2_ACKTR_MAX_ENT is the maximum number of ngtcp2_acktr_entry which ngtcp2_acktr stores. */ @@ -46,22 +47,30 @@ typedef struct ngtcp2_log ngtcp2_log; * ngtcp2_acktr_entry is a range of packets which need to be acked. */ typedef struct ngtcp2_acktr_entry { - /* pkt_num is the largest packet number to acknowledge in this - range. */ - int64_t pkt_num; - /* len is the consecutive packets started from pkt_num which - includes pkt_num itself counting in decreasing order. So pkt_num - = 987 and len = 2, this entry includes packet 987 and 986. */ - size_t len; - /* tstamp is the timestamp when a packet denoted by pkt_num is - received. */ - ngtcp2_tstamp tstamp; + union { + struct { + /* pkt_num is the largest packet number to acknowledge in this + range. */ + int64_t pkt_num; + /* len is the consecutive packets started from pkt_num which + includes pkt_num itself counting in decreasing order. So pkt_num + = 987 and len = 2, this entry includes packet 987 and 986. */ + size_t len; + /* tstamp is the timestamp when a packet denoted by pkt_num is + received. */ + ngtcp2_tstamp tstamp; + }; + + ngtcp2_opl_entry oplent; + }; } ngtcp2_acktr_entry; +ngtcp2_objalloc_def(acktr_entry, ngtcp2_acktr_entry, oplent); + /* - * ngtcp2_acktr_entry_new allocates memory for ent, and initializes it - * with the given parameters. The pointer to the allocated object is - * stored to |*ent|. + * ngtcp2_acktr_entry_objalloc_new allocates memory for ent, and + * initializes it with the given parameters. The pointer to the + * allocated object is stored to |*ent|. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -69,14 +78,16 @@ typedef struct ngtcp2_acktr_entry { * NGTCP2_ERR_NOMEM * Out of memory. */ -int ngtcp2_acktr_entry_new(ngtcp2_acktr_entry **ent, int64_t pkt_num, - ngtcp2_tstamp tstamp, const ngtcp2_mem *mem); +int ngtcp2_acktr_entry_objalloc_new(ngtcp2_acktr_entry **ent, int64_t pkt_num, + ngtcp2_tstamp tstamp, + ngtcp2_objalloc *objalloc); /* - * ngtcp2_acktr_entry_del deallocates memory allocated for |ent|. It - * deallocates memory pointed by |ent|. + * ngtcp2_acktr_entry_objalloc_del deallocates memory allocated for + * |ent|. */ -void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, const ngtcp2_mem *mem); +void ngtcp2_acktr_entry_objalloc_del(ngtcp2_acktr_entry *ent, + ngtcp2_objalloc *objalloc); typedef struct ngtcp2_acktr_ack_entry { /* largest_ack is the largest packet number in outgoing ACK frame */ @@ -86,25 +97,22 @@ typedef struct ngtcp2_acktr_ack_entry { } ngtcp2_acktr_ack_entry; /* NGTCP2_ACKTR_FLAG_NONE indicates that no flag set. */ -#define NGTCP2_ACKTR_FLAG_NONE 0x00 +#define NGTCP2_ACKTR_FLAG_NONE 0x00u /* NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK indicates that immediate acknowledgement is required. */ -#define NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK 0x01 +#define NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK 0x01u /* NGTCP2_ACKTR_FLAG_ACTIVE_ACK indicates that there are pending protected packet to be acknowledged. */ -#define NGTCP2_ACKTR_FLAG_ACTIVE_ACK 0x02 -/* NGTCP2_ACKTR_FLAG_ACK_FINISHED_ACK is set when server received - acknowledgement for ACK which acknowledges the last handshake - packet from client (which contains TLSv1.3 Finished message). */ -#define NGTCP2_ACKTR_FLAG_ACK_FINISHED_ACK 0x80 +#define NGTCP2_ACKTR_FLAG_ACTIVE_ACK 0x02u /* NGTCP2_ACKTR_FLAG_CANCEL_TIMER is set when ACK delay timer is expired and canceled. */ -#define NGTCP2_ACKTR_FLAG_CANCEL_TIMER 0x0100 +#define NGTCP2_ACKTR_FLAG_CANCEL_TIMER 0x0100u /* * ngtcp2_acktr tracks received packets which we have to send ack. */ typedef struct ngtcp2_acktr { + ngtcp2_objalloc objalloc; ngtcp2_ringbuf acks; /* ents includes ngtcp2_acktr_entry sorted by decreasing order of packet number. */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c index 22af219a9e1d75..daab5dd7ce664b 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c @@ -27,55 +27,41 @@ #include #include -#ifdef WIN32 -# include -# include -#else -# include -# include -# include -# include -# include -# include -#endif - -ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, const struct sockaddr *addr, - size_t addrlen, void *user_data) { +ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, const ngtcp2_sockaddr *addr, + ngtcp2_socklen addrlen) { dest->addrlen = addrlen; - dest->addr = (struct sockaddr *)addr; - dest->user_data = user_data; + dest->addr = (ngtcp2_sockaddr *)addr; return dest; } void ngtcp2_addr_copy(ngtcp2_addr *dest, const ngtcp2_addr *src) { dest->addrlen = src->addrlen; if (src->addrlen) { - memcpy(dest->addr, src->addr, src->addrlen); + memcpy(dest->addr, src->addr, (size_t)src->addrlen); } - dest->user_data = src->user_data; } -void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const struct sockaddr *addr, - size_t addrlen) { +void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const ngtcp2_sockaddr *addr, + ngtcp2_socklen addrlen) { dest->addrlen = addrlen; if (addrlen) { - memcpy(dest->addr, addr, addrlen); + memcpy(dest->addr, addr, (size_t)addrlen); } } -static int sockaddr_eq(const struct sockaddr *a, const struct sockaddr *b) { +static int sockaddr_eq(const ngtcp2_sockaddr *a, const ngtcp2_sockaddr *b) { assert(a->sa_family == b->sa_family); switch (a->sa_family) { - case AF_INET: { - const struct sockaddr_in *ai = (const struct sockaddr_in *)(void *)a, - *bi = (const struct sockaddr_in *)(void *)b; + case NGTCP2_AF_INET: { + const ngtcp2_sockaddr_in *ai = (const ngtcp2_sockaddr_in *)(void *)a, + *bi = (const ngtcp2_sockaddr_in *)(void *)b; return ai->sin_port == bi->sin_port && memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr)) == 0; } - case AF_INET6: { - const struct sockaddr_in6 *ai = (const struct sockaddr_in6 *)(void *)a, - *bi = (const struct sockaddr_in6 *)(void *)b; + case NGTCP2_AF_INET6: { + const ngtcp2_sockaddr_in6 *ai = (const ngtcp2_sockaddr_in6 *)(void *)a, + *bi = (const ngtcp2_sockaddr_in6 *)(void *)b; return ai->sin6_port == bi->sin6_port && memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr)) == 0; } @@ -92,17 +78,17 @@ int ngtcp2_addr_eq(const ngtcp2_addr *a, const ngtcp2_addr *b) { uint32_t ngtcp2_addr_compare(const ngtcp2_addr *aa, const ngtcp2_addr *bb) { uint32_t flags = NGTCP2_ADDR_COMPARE_FLAG_NONE; - const struct sockaddr *a = aa->addr; - const struct sockaddr *b = bb->addr; + const ngtcp2_sockaddr *a = aa->addr; + const ngtcp2_sockaddr *b = bb->addr; if (a->sa_family != b->sa_family) { return NGTCP2_ADDR_COMPARE_FLAG_FAMILY; } switch (a->sa_family) { - case AF_INET: { - const struct sockaddr_in *ai = (const struct sockaddr_in *)(void *)a, - *bi = (const struct sockaddr_in *)(void *)b; + case NGTCP2_AF_INET: { + const ngtcp2_sockaddr_in *ai = (const ngtcp2_sockaddr_in *)(void *)a, + *bi = (const ngtcp2_sockaddr_in *)(void *)b; if (memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr))) { flags |= NGTCP2_ADDR_COMPARE_FLAG_ADDR; } @@ -111,9 +97,9 @@ uint32_t ngtcp2_addr_compare(const ngtcp2_addr *aa, const ngtcp2_addr *bb) { } return flags; } - case AF_INET6: { - const struct sockaddr_in6 *ai = (const struct sockaddr_in6 *)(void *)a, - *bi = (const struct sockaddr_in6 *)(void *)b; + case NGTCP2_AF_INET6: { + const ngtcp2_sockaddr_in6 *ai = (const ngtcp2_sockaddr_in6 *)(void *)a, + *bi = (const ngtcp2_sockaddr_in6 *)(void *)b; if (memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr))) { flags |= NGTCP2_ADDR_COMPARE_FLAG_ADDR; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h index 84c0c01ddbf2db..f1d7f7bdc70ea9 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h @@ -38,30 +38,21 @@ */ void ngtcp2_addr_copy(ngtcp2_addr *dest, const ngtcp2_addr *src); -/* - * ngtcp2_addr_copy_byte copies |addr| of length |addrlen| into the - * buffer pointed by dest->addr. dest->len is updated to have - * |addrlen|. This function assumes that dest->addr points to a - * buffer which have sufficient size to store the copy. - */ -void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const struct sockaddr *addr, - size_t addrlen); - /* * ngtcp2_addr_eq returns nonzero if |a| equals |b|. */ int ngtcp2_addr_eq(const ngtcp2_addr *a, const ngtcp2_addr *b); /* NGTCP2_ADDR_COMPARE_FLAG_NONE indicates that no flag set. */ -#define NGTCP2_ADDR_COMPARE_FLAG_NONE 0x0 +#define NGTCP2_ADDR_COMPARE_FLAG_NONE 0x0u /* NGTCP2_ADDR_COMPARE_FLAG_ADDR indicates IP addresses do not match. */ -#define NGTCP2_ADDR_COMPARE_FLAG_ADDR 0x1 +#define NGTCP2_ADDR_COMPARE_FLAG_ADDR 0x1u /* NGTCP2_ADDR_COMPARE_FLAG_PORT indicates ports do not match. */ -#define NGTCP2_ADDR_COMPARE_FLAG_PORT 0x2 +#define NGTCP2_ADDR_COMPARE_FLAG_PORT 0x2u /* NGTCP2_ADDR_COMPARE_FLAG_FAMILY indicates address families do not match. */ -#define NGTCP2_ADDR_COMPARE_FLAG_FAMILY 0x4 +#define NGTCP2_ADDR_COMPARE_FLAG_FAMILY 0x4u /* * ngtcp2_addr_compare compares address and port between |a| and |b|, diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_balloc.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_balloc.c new file mode 100644 index 00000000000000..5cc39ee3b03da4 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_balloc.c @@ -0,0 +1,90 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_balloc.h" + +#include + +#include "ngtcp2_mem.h" + +void ngtcp2_balloc_init(ngtcp2_balloc *balloc, size_t blklen, + const ngtcp2_mem *mem) { + assert((blklen & 0xfu) == 0); + + balloc->mem = mem; + balloc->blklen = blklen; + balloc->head = NULL; + ngtcp2_buf_init(&balloc->buf, (void *)"", 0); +} + +void ngtcp2_balloc_free(ngtcp2_balloc *balloc) { + if (balloc == NULL) { + return; + } + + ngtcp2_balloc_clear(balloc); +} + +void ngtcp2_balloc_clear(ngtcp2_balloc *balloc) { + ngtcp2_memblock_hd *p, *next; + + for (p = balloc->head; p; p = next) { + next = p->next; + ngtcp2_mem_free(balloc->mem, p); + } + + balloc->head = NULL; + ngtcp2_buf_init(&balloc->buf, (void *)"", 0); +} + +int ngtcp2_balloc_get(ngtcp2_balloc *balloc, void **pbuf, size_t n) { + uint8_t *p; + ngtcp2_memblock_hd *hd; + + assert(n <= balloc->blklen); + + if (ngtcp2_buf_left(&balloc->buf) < n) { + p = ngtcp2_mem_malloc(balloc->mem, + sizeof(ngtcp2_memblock_hd) + 0x10u + balloc->blklen); + if (p == NULL) { + return NGTCP2_ERR_NOMEM; + } + + hd = (ngtcp2_memblock_hd *)(void *)p; + hd->next = balloc->head; + balloc->head = hd; + ngtcp2_buf_init( + &balloc->buf, + (uint8_t *)(((uintptr_t)p + sizeof(ngtcp2_memblock_hd) + 0xfu) & + ~(uintptr_t)0xfu), + balloc->blklen); + } + + assert(((uintptr_t)balloc->buf.last & 0xfu) == 0); + + *pbuf = balloc->buf.last; + balloc->buf.last += (n + 0xfu) & ~(uintptr_t)0xfu; + + return 0; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_balloc.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_balloc.h new file mode 100644 index 00000000000000..1fb16325d9953d --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_balloc.h @@ -0,0 +1,91 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_BALLOC_H +#define NGTCP2_BALLOC_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_buf.h" + +typedef struct ngtcp2_memblock_hd ngtcp2_memblock_hd; + +/* + * ngtcp2_memblock_hd is the header of memory block. + */ +struct ngtcp2_memblock_hd { + ngtcp2_memblock_hd *next; +}; + +/* + * ngtcp2_balloc is a custom memory allocator. It allocates |blklen| + * bytes of memory at once on demand, and returns its slice when the + * allocation is requested. + */ +typedef struct ngtcp2_balloc { + /* mem is the underlying memory allocator. */ + const ngtcp2_mem *mem; + /* blklen is the size of memory block. */ + size_t blklen; + /* head points to the list of memory block allocated so far. */ + ngtcp2_memblock_hd *head; + /* buf wraps the current memory block for allocation requests. */ + ngtcp2_buf buf; +} ngtcp2_balloc; + +/* + * ngtcp2_balloc_init initializes |balloc| with |blklen| which is the + * size of memory block. + */ +void ngtcp2_balloc_init(ngtcp2_balloc *balloc, size_t blklen, + const ngtcp2_mem *mem); + +/* + * ngtcp2_balloc_free releases all allocated memory blocks. + */ +void ngtcp2_balloc_free(ngtcp2_balloc *balloc); + +/* + * ngtcp2_balloc_get allocates |n| bytes of memory and assigns its + * pointer to |*pbuf|. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_balloc_get(ngtcp2_balloc *balloc, void **pbuf, size_t n); + +/* + * ngtcp2_balloc_clear releases all allocated memory blocks and + * initializes its state. + */ +void ngtcp2_balloc_clear(ngtcp2_balloc *balloc); + +#endif /* NGTCP2_BALLOC_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c new file mode 100644 index 00000000000000..0816d69b816c52 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c @@ -0,0 +1,692 @@ +/* + * ngtcp2 + * + * Copyright (c) 2021 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_bbr.h" + +#include + +#include "ngtcp2_log.h" +#include "ngtcp2_macro.h" +#include "ngtcp2_mem.h" +#include "ngtcp2_rcvry.h" +#include "ngtcp2_rst.h" + +static const double pacing_gain_cycle[] = {1.25, 0.75, 1, 1, 1, 1, 1, 1}; + +#define NGTCP2_BBR_GAIN_CYCLELEN \ + (sizeof(pacing_gain_cycle) / sizeof(pacing_gain_cycle[0])) + +#define NGTCP2_BBR_HIGH_GAIN 2.89 +#define NGTCP2_BBR_PROBE_RTT_DURATION (200 * NGTCP2_MILLISECONDS) +#define NGTCP2_RTPROP_FILTERLEN (10 * NGTCP2_SECONDS) +#define NGTCP2_BBR_BTL_BW_FILTERLEN 10 + +static void bbr_update_on_ack(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); +static void bbr_update_model_and_state(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, + ngtcp2_tstamp ts); +static void bbr_update_control_parameters(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); +static void bbr_on_transmit(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat); +static void bbr_init_round_counting(ngtcp2_bbr_cc *cc); +static void bbr_update_round(ngtcp2_bbr_cc *cc, const ngtcp2_cc_ack *ack); +static void bbr_update_btl_bw(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); +static void bbr_update_rtprop(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); +static void bbr_init_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat); +static void bbr_set_pacing_rate_with_gain(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat, + double pacing_gain); +static void bbr_set_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat); +static void bbr_set_send_quantum(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat); +static void bbr_update_target_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat); +static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); +static void bbr_save_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat); +static void bbr_restore_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat); +static void bbr_modulate_cwnd_for_probe_rtt(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat); +static void bbr_set_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); +static void bbr_init(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp initial_ts); +static void bbr_enter_startup(ngtcp2_bbr_cc *cc); +static void bbr_init_full_pipe(ngtcp2_bbr_cc *cc); +static void bbr_check_full_pipe(ngtcp2_bbr_cc *cc); +static void bbr_enter_drain(ngtcp2_bbr_cc *cc); +static void bbr_check_drain(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); +static void bbr_enter_probe_bw(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts); +static void bbr_check_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); +static void bbr_advance_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts); +static int bbr_is_next_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); +static void bbr_handle_restart_from_idle(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat); +static void bbr_check_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); +static void bbr_enter_probe_rtt(ngtcp2_bbr_cc *cc); +static void bbr_handle_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); +static void bbr_exit_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts); + +void ngtcp2_bbr_cc_init(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_rst *rst, ngtcp2_tstamp initial_ts, + ngtcp2_rand rand, const ngtcp2_rand_ctx *rand_ctx, + ngtcp2_log *log) { + cc->ccb.log = log; + cc->rst = rst; + cc->rand = rand; + cc->rand_ctx = *rand_ctx; + cc->initial_cwnd = cstat->cwnd; + bbr_init(cc, cstat, initial_ts); +} + +void ngtcp2_bbr_cc_free(ngtcp2_bbr_cc *cc) { (void)cc; } + +int ngtcp2_cc_bbr_cc_init(ngtcp2_cc *cc, ngtcp2_log *log, + ngtcp2_conn_stat *cstat, ngtcp2_rst *rst, + ngtcp2_tstamp initial_ts, ngtcp2_rand rand, + const ngtcp2_rand_ctx *rand_ctx, + const ngtcp2_mem *mem) { + ngtcp2_bbr_cc *bbr_cc; + + bbr_cc = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_bbr_cc)); + if (bbr_cc == NULL) { + return NGTCP2_ERR_NOMEM; + } + + ngtcp2_bbr_cc_init(bbr_cc, cstat, rst, initial_ts, rand, rand_ctx, log); + + cc->ccb = &bbr_cc->ccb; + cc->on_pkt_acked = ngtcp2_cc_bbr_cc_on_pkt_acked; + cc->congestion_event = ngtcp2_cc_bbr_cc_congestion_event; + cc->on_spurious_congestion = ngtcp2_cc_bbr_cc_on_spurious_congestion; + cc->on_persistent_congestion = ngtcp2_cc_bbr_cc_on_persistent_congestion; + cc->on_ack_recv = ngtcp2_cc_bbr_cc_on_ack_recv; + cc->on_pkt_sent = ngtcp2_cc_bbr_cc_on_pkt_sent; + cc->new_rtt_sample = ngtcp2_cc_bbr_cc_new_rtt_sample; + cc->reset = ngtcp2_cc_bbr_cc_reset; + cc->event = ngtcp2_cc_bbr_cc_event; + + return 0; +} + +void ngtcp2_cc_bbr_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem) { + ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(cc->ccb, ngtcp2_bbr_cc, ccb); + + ngtcp2_bbr_cc_free(bbr_cc); + ngtcp2_mem_free(mem, bbr_cc); +} + +void ngtcp2_cc_bbr_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) { + (void)ccx; + (void)cstat; + (void)pkt; + (void)ts; +} + +static int in_congestion_recovery(const ngtcp2_conn_stat *cstat, + ngtcp2_tstamp sent_time) { + return cstat->congestion_recovery_start_ts != UINT64_MAX && + sent_time <= cstat->congestion_recovery_start_ts; +} + +void ngtcp2_cc_bbr_cc_congestion_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp sent_ts, + ngtcp2_tstamp ts) { + ngtcp2_bbr_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb); + + if (cc->in_loss_recovery || cc->congestion_recovery_start_ts != UINT64_MAX || + in_congestion_recovery(cstat, sent_ts)) { + return; + } + + cc->congestion_recovery_start_ts = ts; +} + +void ngtcp2_cc_bbr_cc_on_spurious_congestion(ngtcp2_cc *ccx, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + ngtcp2_bbr_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb); + (void)ts; + + cc->congestion_recovery_start_ts = UINT64_MAX; + cstat->congestion_recovery_start_ts = UINT64_MAX; + + if (cc->in_loss_recovery) { + cc->in_loss_recovery = 0; + cc->packet_conservation = 0; + bbr_restore_cwnd(cc, cstat); + } +} + +void ngtcp2_cc_bbr_cc_on_persistent_congestion(ngtcp2_cc *ccx, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + ngtcp2_bbr_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb); + (void)ts; + + cstat->congestion_recovery_start_ts = UINT64_MAX; + cc->congestion_recovery_start_ts = UINT64_MAX; + cc->in_loss_recovery = 0; + cc->packet_conservation = 0; + + bbr_save_cwnd(cc, cstat); + cstat->cwnd = 2 * cstat->max_udp_payload_size; +} + +void ngtcp2_cc_bbr_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { + ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb); + + bbr_update_on_ack(bbr_cc, cstat, ack, ts); +} + +void ngtcp2_cc_bbr_cc_on_pkt_sent(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt) { + ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb); + (void)pkt; + + bbr_on_transmit(bbr_cc, cstat); +} + +void ngtcp2_cc_bbr_cc_new_rtt_sample(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + (void)ccx; + (void)cstat; + (void)ts; +} + +void ngtcp2_cc_bbr_cc_reset(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb); + bbr_init(bbr_cc, cstat, ts); +} + +void ngtcp2_cc_bbr_cc_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_cc_event_type event, ngtcp2_tstamp ts) { + (void)ccx; + (void)cstat; + (void)event; + (void)ts; +} + +static void bbr_update_on_ack(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { + bbr_update_model_and_state(cc, cstat, ack, ts); + bbr_update_control_parameters(cc, cstat, ack); +} + +static void bbr_update_model_and_state(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, + ngtcp2_tstamp ts) { + bbr_update_btl_bw(cc, cstat, ack); + bbr_check_cycle_phase(cc, cstat, ack, ts); + bbr_check_full_pipe(cc); + bbr_check_drain(cc, cstat, ts); + bbr_update_rtprop(cc, cstat, ts); + bbr_check_probe_rtt(cc, cstat, ts); +} + +static void bbr_update_control_parameters(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + bbr_set_pacing_rate(cc, cstat); + bbr_set_send_quantum(cc, cstat); + bbr_set_cwnd(cc, cstat, ack); +} + +static void bbr_on_transmit(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) { + bbr_handle_restart_from_idle(cc, cstat); +} + +static void bbr_init_round_counting(ngtcp2_bbr_cc *cc) { + cc->next_round_delivered = 0; + cc->round_start = 0; + cc->round_count = 0; +} + +static void bbr_update_round(ngtcp2_bbr_cc *cc, const ngtcp2_cc_ack *ack) { + if (ack->pkt_delivered >= cc->next_round_delivered) { + cc->next_round_delivered = cc->rst->delivered; + ++cc->round_count; + cc->round_start = 1; + + return; + } + + cc->round_start = 0; +} + +static void bbr_handle_recovery(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + if (cc->in_loss_recovery) { + if (ack->pkt_delivered >= cc->congestion_recovery_next_round_delivered) { + cc->packet_conservation = 0; + } + + if (!in_congestion_recovery(cstat, ack->largest_acked_sent_ts)) { + cc->in_loss_recovery = 0; + cc->packet_conservation = 0; + bbr_restore_cwnd(cc, cstat); + } + + return; + } + + if (cc->congestion_recovery_start_ts != UINT64_MAX) { + cc->in_loss_recovery = 1; + bbr_save_cwnd(cc, cstat); + cstat->cwnd = cstat->bytes_in_flight + + ngtcp2_max(ack->bytes_delivered, cstat->max_udp_payload_size); + + cstat->congestion_recovery_start_ts = cc->congestion_recovery_start_ts; + cc->congestion_recovery_start_ts = UINT64_MAX; + cc->packet_conservation = 1; + cc->congestion_recovery_next_round_delivered = cc->rst->delivered; + } +} + +static void bbr_update_btl_bw(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + bbr_update_round(cc, ack); + bbr_handle_recovery(cc, cstat, ack); + + if (cstat->delivery_rate_sec < cc->btl_bw && cc->rst->rs.is_app_limited) { + return; + } + + ngtcp2_window_filter_update(&cc->btl_bw_filter, cstat->delivery_rate_sec, + cc->round_count); + + cc->btl_bw = ngtcp2_window_filter_get_best(&cc->btl_bw_filter); +} + +static void bbr_update_rtprop(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + cc->rtprop_expired = ts > cc->rtprop_stamp + NGTCP2_RTPROP_FILTERLEN; + + /* Need valid RTT sample */ + if (cstat->latest_rtt && + (cstat->latest_rtt <= cc->rt_prop || cc->rtprop_expired)) { + cc->rt_prop = cstat->latest_rtt; + cc->rtprop_stamp = ts; + + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr update RTprop=%" PRIu64, cc->rt_prop); + } +} + +static void bbr_init_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) { + double nominal_bandwidth = + (double)cc->initial_cwnd / (double)NGTCP2_MILLISECONDS; + + cstat->pacing_rate = cc->pacing_gain * nominal_bandwidth; +} + +static void bbr_set_pacing_rate_with_gain(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat, + double pacing_gain) { + double rate = pacing_gain * (double)cc->btl_bw / NGTCP2_SECONDS; + + if (cc->filled_pipe || rate > cstat->pacing_rate) { + cstat->pacing_rate = rate; + } +} + +static void bbr_set_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) { + bbr_set_pacing_rate_with_gain(cc, cstat, cc->pacing_gain); +} + +static void bbr_set_send_quantum(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) { + uint64_t send_quantum; + (void)cc; + + if (cstat->pacing_rate < 1.2 * 1024 * 1024 / 8 / NGTCP2_SECONDS) { + cstat->send_quantum = cstat->max_udp_payload_size; + } else if (cstat->pacing_rate < 24.0 * 1024 * 1024 / 8 / NGTCP2_SECONDS) { + cstat->send_quantum = cstat->max_udp_payload_size * 2; + } else { + send_quantum = + (uint64_t)(cstat->pacing_rate * (double)(cstat->min_rtt == UINT64_MAX + ? NGTCP2_MILLISECONDS + : cstat->min_rtt)); + cstat->send_quantum = (size_t)ngtcp2_min(send_quantum, 64 * 1024); + } + + cstat->send_quantum = + ngtcp2_max(cstat->send_quantum, cstat->max_udp_payload_size * 10); +} + +static uint64_t bbr_inflight(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + double gain) { + uint64_t quanta = 3 * cstat->send_quantum; + double estimated_bdp; + + if (cc->rt_prop == UINT64_MAX) { + /* no valid RTT samples yet */ + return cc->initial_cwnd; + } + + estimated_bdp = (double)cc->btl_bw * (double)cc->rt_prop / NGTCP2_SECONDS; + + return (uint64_t)(gain * estimated_bdp) + quanta; +} + +static void bbr_update_target_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) { + cc->target_cwnd = bbr_inflight(cc, cstat, cc->cwnd_gain); +} + +static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + if (ack->bytes_lost > 0) { + if (cstat->cwnd > ack->bytes_lost) { + cstat->cwnd -= ack->bytes_lost; + cstat->cwnd = ngtcp2_max(cstat->cwnd, 2 * cstat->max_udp_payload_size); + } else { + cstat->cwnd = cstat->max_udp_payload_size; + } + } + + if (cc->packet_conservation) { + cstat->cwnd = + ngtcp2_max(cstat->cwnd, cstat->bytes_in_flight + ack->bytes_delivered); + } +} + +static void bbr_save_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) { + if (!cc->in_loss_recovery && cc->state != NGTCP2_BBR_STATE_PROBE_RTT) { + cc->prior_cwnd = cstat->cwnd; + return; + } + + cc->prior_cwnd = ngtcp2_max(cc->prior_cwnd, cstat->cwnd); +} + +static void bbr_restore_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) { + cstat->cwnd = ngtcp2_max(cstat->cwnd, cc->prior_cwnd); +} + +static uint64_t min_pipe_cwnd(size_t max_udp_payload_size) { + return max_udp_payload_size * 4; +} + +static void bbr_modulate_cwnd_for_probe_rtt(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat) { + if (cc->state == NGTCP2_BBR_STATE_PROBE_RTT) { + cstat->cwnd = + ngtcp2_min(cstat->cwnd, min_pipe_cwnd(cstat->max_udp_payload_size)); + } +} + +static void bbr_set_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + bbr_update_target_cwnd(cc, cstat); + bbr_modulate_cwnd_for_recovery(cc, cstat, ack); + + if (!cc->packet_conservation) { + if (cc->filled_pipe) { + cstat->cwnd = + ngtcp2_min(cstat->cwnd + ack->bytes_delivered, cc->target_cwnd); + } else if (cstat->cwnd < cc->target_cwnd || + cc->rst->delivered < cc->initial_cwnd) { + cstat->cwnd += ack->bytes_delivered; + } + + cstat->cwnd = + ngtcp2_max(cstat->cwnd, min_pipe_cwnd(cstat->max_udp_payload_size)); + } + + bbr_modulate_cwnd_for_probe_rtt(cc, cstat); +} + +static void bbr_init(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp initial_ts) { + cc->pacing_gain = NGTCP2_BBR_HIGH_GAIN; + cc->prior_cwnd = 0; + cc->target_cwnd = 0; + cc->btl_bw = 0; + cc->rt_prop = UINT64_MAX; + cc->rtprop_stamp = initial_ts; + cc->cycle_stamp = UINT64_MAX; + cc->probe_rtt_done_stamp = UINT64_MAX; + cc->cycle_index = 0; + cc->rtprop_expired = 0; + cc->idle_restart = 0; + cc->packet_conservation = 0; + cc->probe_rtt_round_done = 0; + + cc->congestion_recovery_start_ts = UINT64_MAX; + cc->congestion_recovery_next_round_delivered = 0; + cc->in_loss_recovery = 0; + + cstat->send_quantum = cstat->max_udp_payload_size * 10; + + ngtcp2_window_filter_init(&cc->btl_bw_filter, NGTCP2_BBR_BTL_BW_FILTERLEN); + + bbr_init_round_counting(cc); + bbr_init_full_pipe(cc); + bbr_init_pacing_rate(cc, cstat); + bbr_enter_startup(cc); +} + +static void bbr_enter_startup(ngtcp2_bbr_cc *cc) { + cc->state = NGTCP2_BBR_STATE_STARTUP; + cc->pacing_gain = NGTCP2_BBR_HIGH_GAIN; + cc->cwnd_gain = NGTCP2_BBR_HIGH_GAIN; +} + +static void bbr_init_full_pipe(ngtcp2_bbr_cc *cc) { + cc->filled_pipe = 0; + cc->full_bw = 0; + cc->full_bw_count = 0; +} + +static void bbr_check_full_pipe(ngtcp2_bbr_cc *cc) { + if (cc->filled_pipe || !cc->round_start || cc->rst->rs.is_app_limited) { + /* no need to check for a full pipe now. */ + return; + } + + /* cc->btl_bw still growing? */ + if (cc->btl_bw * 100 >= cc->full_bw * 125) { + /* record new baseline level */ + cc->full_bw = cc->btl_bw; + cc->full_bw_count = 0; + return; + } + /* another round w/o much growth */ + ++cc->full_bw_count; + if (cc->full_bw_count >= 3) { + cc->filled_pipe = 1; + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr filled pipe, btl_bw=%" PRIu64, cc->btl_bw); + } +} + +static void bbr_enter_drain(ngtcp2_bbr_cc *cc) { + cc->state = NGTCP2_BBR_STATE_DRAIN; + /* pace slowly */ + cc->pacing_gain = 1.0 / NGTCP2_BBR_HIGH_GAIN; + /* maintain cwnd */ + cc->cwnd_gain = NGTCP2_BBR_HIGH_GAIN; +} + +static void bbr_check_drain(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + if (cc->state == NGTCP2_BBR_STATE_STARTUP && cc->filled_pipe) { + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr exit Startup and enter Drain"); + + bbr_enter_drain(cc); + } + + if (cc->state == NGTCP2_BBR_STATE_DRAIN && + cstat->bytes_in_flight <= bbr_inflight(cc, cstat, 1.0)) { + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr exit Drain and enter ProbeBW"); + + /* we estimate queue is drained */ + bbr_enter_probe_bw(cc, ts); + } +} + +static void bbr_enter_probe_bw(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts) { + uint8_t rand; + + cc->state = NGTCP2_BBR_STATE_PROBE_BW; + cc->pacing_gain = 1; + cc->cwnd_gain = 2; + + assert(cc->rand); + + cc->rand(&rand, 1, &cc->rand_ctx); + + cc->cycle_index = NGTCP2_BBR_GAIN_CYCLELEN - 1 - (size_t)(rand * 7 / 256); + bbr_advance_cycle_phase(cc, ts); +} + +static void bbr_check_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { + if (cc->state == NGTCP2_BBR_STATE_PROBE_BW && + bbr_is_next_cycle_phase(cc, cstat, ack, ts)) { + bbr_advance_cycle_phase(cc, ts); + } +} + +static void bbr_advance_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts) { + cc->cycle_stamp = ts; + cc->cycle_index = (cc->cycle_index + 1) & (NGTCP2_BBR_GAIN_CYCLELEN - 1); + cc->pacing_gain = pacing_gain_cycle[cc->cycle_index]; +} + +static int bbr_is_next_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { + int is_full_length = (ts - cc->cycle_stamp) > cc->rt_prop; + + if (cc->pacing_gain > 1) { + return is_full_length && (ack->bytes_lost > 0 || + ack->prior_bytes_in_flight >= + bbr_inflight(cc, cstat, cc->pacing_gain)); + } + + if (cc->pacing_gain < 1) { + return is_full_length || + ack->prior_bytes_in_flight <= bbr_inflight(cc, cstat, 1); + } + + return is_full_length; +} + +static void bbr_handle_restart_from_idle(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat) { + if (cstat->bytes_in_flight == 0 && cc->rst->app_limited) { + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr restart from idle"); + + cc->idle_restart = 1; + + if (cc->state == NGTCP2_BBR_STATE_PROBE_BW) { + bbr_set_pacing_rate_with_gain(cc, cstat, 1); + } + } +} + +static void bbr_check_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + if (cc->state != NGTCP2_BBR_STATE_PROBE_RTT && cc->rtprop_expired && + !cc->idle_restart) { + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr enter ProbeRTT"); + + bbr_enter_probe_rtt(cc); + bbr_save_cwnd(cc, cstat); + cc->probe_rtt_done_stamp = UINT64_MAX; + } + + if (cc->state == NGTCP2_BBR_STATE_PROBE_RTT) { + bbr_handle_probe_rtt(cc, cstat, ts); + } + + cc->idle_restart = 0; +} + +static void bbr_enter_probe_rtt(ngtcp2_bbr_cc *cc) { + cc->state = NGTCP2_BBR_STATE_PROBE_RTT; + cc->pacing_gain = 1; + cc->cwnd_gain = 1; +} + +static void bbr_handle_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + uint64_t app_limited = cc->rst->delivered + cstat->bytes_in_flight; + + /* Ignore low rate samples during NGTCP2_BBR_STATE_PROBE_RTT. */ + cc->rst->app_limited = app_limited ? app_limited : 1; + + if (cc->probe_rtt_done_stamp == UINT64_MAX && + cstat->bytes_in_flight <= min_pipe_cwnd(cstat->max_udp_payload_size)) { + cc->probe_rtt_done_stamp = ts + NGTCP2_BBR_PROBE_RTT_DURATION; + cc->probe_rtt_round_done = 0; + cc->next_round_delivered = cc->rst->delivered; + + return; + } + + if (cc->probe_rtt_done_stamp != UINT64_MAX) { + if (cc->round_start) { + cc->probe_rtt_round_done = 1; + } + + if (cc->probe_rtt_round_done && ts > cc->probe_rtt_done_stamp) { + cc->rtprop_stamp = ts; + bbr_restore_cwnd(cc, cstat); + bbr_exit_probe_rtt(cc, ts); + } + } +} + +static void bbr_exit_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts) { + if (cc->filled_pipe) { + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr exit ProbeRTT and enter ProbeBW"); + + bbr_enter_probe_bw(cc, ts); + + return; + } + + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr exit ProbeRTT and enter Startup"); + + bbr_enter_startup(cc); +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h new file mode 100644 index 00000000000000..7311f051e187bc --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h @@ -0,0 +1,156 @@ +/* + * ngtcp2 + * + * Copyright (c) 2021 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_BBR_H +#define NGTCP2_BBR_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_cc.h" +#include "ngtcp2_window_filter.h" + +typedef struct ngtcp2_rst ngtcp2_rst; + +typedef enum ngtcp2_bbr_state { + NGTCP2_BBR_STATE_STARTUP, + NGTCP2_BBR_STATE_DRAIN, + NGTCP2_BBR_STATE_PROBE_BW, + NGTCP2_BBR_STATE_PROBE_RTT, +} ngtcp2_bbr_state; + +/* + * ngtcp2_bbr_cc is BBR congestion controller, described in + * https://tools.ietf.org/html/draft-cardwell-iccrg-bbr-congestion-control-00 + */ +typedef struct ngtcp2_bbr_cc { + ngtcp2_cc_base ccb; + + /* The max filter used to estimate BBR.BtlBw. */ + ngtcp2_window_filter btl_bw_filter; + uint64_t initial_cwnd; + ngtcp2_rst *rst; + ngtcp2_rand rand; + ngtcp2_rand_ctx rand_ctx; + + /* BBR variables */ + + /* The dynamic gain factor used to scale BBR.BtlBw to + produce BBR.pacing_rate. */ + double pacing_gain; + /* The dynamic gain factor used to scale the estimated BDP to produce a + congestion window (cwnd). */ + double cwnd_gain; + uint64_t full_bw; + /* packet.delivered value denoting the end of a packet-timed round trip. */ + uint64_t next_round_delivered; + /* Count of packet-timed round trips. */ + uint64_t round_count; + uint64_t prior_cwnd; + /* target_cwnd is the upper bound on the volume of data BBR + allows in flight. */ + uint64_t target_cwnd; + /* BBR's estimated bottleneck bandwidth available to the + transport flow, estimated from the maximum delivery rate sample in a + sliding window. */ + uint64_t btl_bw; + /* BBR's estimated two-way round-trip propagation delay of + the path, estimated from the windowed minimum recent round-trip delay + sample. */ + ngtcp2_duration rt_prop; + /* The wall clock time at which the current BBR.RTProp + sample was obtained. */ + ngtcp2_tstamp rtprop_stamp; + ngtcp2_tstamp cycle_stamp; + ngtcp2_tstamp probe_rtt_done_stamp; + /* congestion_recovery_start_ts is the time when congestion recovery + period started.*/ + ngtcp2_tstamp congestion_recovery_start_ts; + uint64_t congestion_recovery_next_round_delivered; + size_t full_bw_count; + size_t cycle_index; + ngtcp2_bbr_state state; + /* A boolean that records whether BBR estimates that it has ever fully + utilized its available bandwidth ("filled the pipe"). */ + int filled_pipe; + /* A boolean that BBR sets to true once per packet-timed round trip, + on ACKs that advance BBR.round_count. */ + int round_start; + int rtprop_expired; + int idle_restart; + int packet_conservation; + int probe_rtt_round_done; + /* in_loss_recovery becomes nonzero when BBR enters loss recovery + period. */ + int in_loss_recovery; +} ngtcp2_bbr_cc; + +int ngtcp2_cc_bbr_cc_init(ngtcp2_cc *cc, ngtcp2_log *log, + ngtcp2_conn_stat *cstat, ngtcp2_rst *rst, + ngtcp2_tstamp initial_ts, ngtcp2_rand rand, + const ngtcp2_rand_ctx *rand_ctx, + const ngtcp2_mem *mem); + +void ngtcp2_cc_bbr_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem); + +void ngtcp2_bbr_cc_init(ngtcp2_bbr_cc *bbr_cc, ngtcp2_conn_stat *cstat, + ngtcp2_rst *rst, ngtcp2_tstamp initial_ts, + ngtcp2_rand rand, const ngtcp2_rand_ctx *rand_ctx, + ngtcp2_log *log); + +void ngtcp2_bbr_cc_free(ngtcp2_bbr_cc *cc); + +void ngtcp2_cc_bbr_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts); + +void ngtcp2_cc_bbr_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp sent_ts, ngtcp2_tstamp ts); + +void ngtcp2_cc_bbr_cc_on_spurious_congestion(ngtcp2_cc *ccx, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +void ngtcp2_cc_bbr_cc_on_persistent_congestion(ngtcp2_cc *cc, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +void ngtcp2_cc_bbr_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); + +void ngtcp2_cc_bbr_cc_on_pkt_sent(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt); + +void ngtcp2_cc_bbr_cc_new_rtt_sample(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +void ngtcp2_cc_bbr_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +void ngtcp2_cc_bbr_cc_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_cc_event_type event, ngtcp2_tstamp ts); + +#endif /* NGTCP2_BBR_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr2.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr2.c new file mode 100644 index 00000000000000..585ea11e8e29a5 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr2.c @@ -0,0 +1,1486 @@ +/* + * ngtcp2 + * + * Copyright (c) 2021 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_bbr2.h" + +#include + +#include "ngtcp2_log.h" +#include "ngtcp2_macro.h" +#include "ngtcp2_mem.h" +#include "ngtcp2_rcvry.h" +#include "ngtcp2_rst.h" + +#define NGTCP2_BBR_MAX_BW_FILTERLEN 2 + +#define NGTCP2_BBR_EXTRA_ACKED_FILTERLEN 10 + +#define NGTCP2_BBR_STARTUP_PACING_GAIN ((double)2.77) + +#define NGTCP2_BBR_STARTUP_CWND_GAIN 2 + +#define NGTCP2_BBR_PROBE_RTT_CWND_GAIN ((double)0.5) + +#define NGTCP2_BBR_BETA_NUMER 7 +#define NGTCP2_BBR_BETA_DENOM 10 + +#define NGTCP2_BBR_LOSS_THRESH_NUMER 2 +#define NGTCP2_BBR_LOSS_THRESH_DENOM 100 + +#define NGTCP2_BBR_HEADROOM_NUMER 15 +#define NGTCP2_BBR_HEADROOM_DENOM 100 + +#define NGTCP2_BBR_PROBE_RTT_INTERVAL (5 * NGTCP2_SECONDS) +#define NGTCP2_BBR_MIN_RTT_FILTERLEN (10 * NGTCP2_SECONDS) + +#define NGTCP2_BBR_PROBE_RTT_DURATION (200 * NGTCP2_MILLISECONDS) + +#define NGTCP2_BBR_PACING_MARGIN_PERCENT 1 + +static void bbr_on_init(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp initial_ts); + +static void bbr_on_transmit(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +static void bbr_reset_congestion_signals(ngtcp2_bbr2_cc *bbr); + +static void bbr_reset_lower_bounds(ngtcp2_bbr2_cc *bbr); + +static void bbr_init_round_counting(ngtcp2_bbr2_cc *bbr); + +static void bbr_init_full_pipe(ngtcp2_bbr2_cc *bbr); + +static void bbr_init_pacing_rate(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat); + +static void bbr_set_pacing_rate_with_gain(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + double pacing_gain); + +static void bbr_set_pacing_rate(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat); + +static void bbr_enter_startup(ngtcp2_bbr2_cc *bbr); + +static void bbr_check_startup_done(ngtcp2_bbr2_cc *bbr, + const ngtcp2_cc_ack *ack); + +static void bbr_update_on_ack(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); + +static void bbr_update_model_and_state(ngtcp2_bbr2_cc *cc, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, + ngtcp2_tstamp ts); + +static void bbr_update_control_parameters(ngtcp2_bbr2_cc *cc, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); + +static void bbr_update_on_loss(ngtcp2_bbr2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts); + +static void bbr_update_latest_delivery_signals(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static void bbr_advance_latest_delivery_signals(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static void bbr_update_congestion_signals(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); + +static void bbr_adapt_lower_bounds_from_congestion(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static void bbr_init_lower_bounds(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat); + +static void bbr_loss_lower_bounds(ngtcp2_bbr2_cc *bbr); + +static void bbr_bound_bw_for_model(ngtcp2_bbr2_cc *bbr); + +static void bbr_update_max_bw(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); + +static void bbr_update_round(ngtcp2_bbr2_cc *bbr, const ngtcp2_cc_ack *ack); + +static void bbr_start_round(ngtcp2_bbr2_cc *bbr); + +static int bbr_is_in_probe_bw_state(ngtcp2_bbr2_cc *bbr); + +static void bbr_update_ack_aggregation(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, + ngtcp2_tstamp ts); + +static void bbr_enter_drain(ngtcp2_bbr2_cc *bbr); + +static void bbr_check_drain(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +static void bbr_enter_probe_bw(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts); + +static void bbr_start_probe_bw_down(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts); + +static void bbr_start_probe_bw_cruise(ngtcp2_bbr2_cc *bbr); + +static void bbr_start_probe_bw_refill(ngtcp2_bbr2_cc *bbr); + +static void bbr_start_probe_bw_up(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +static void bbr_update_probe_bw_cycle_phase(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, + ngtcp2_tstamp ts); + +static int bbr_check_time_to_cruise(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts); + +static int bbr_has_elapsed_in_phase(ngtcp2_bbr2_cc *bbr, + ngtcp2_duration interval, ngtcp2_tstamp ts); + +static uint64_t bbr_inflight_with_headroom(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static void bbr_raise_inflight_hi_slope(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static void bbr_probe_inflight_hi_upward(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); + +static void bbr_adapt_upper_bounds(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); + +static int bbr_check_time_to_probe_bw(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +static void bbr_pick_probe_wait(ngtcp2_bbr2_cc *bbr); + +static int bbr_is_reno_coexistence_probe_time(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static uint64_t bbr_target_inflight(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static int bbr_check_inflight_too_high(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +static int is_inflight_too_high(const ngtcp2_rs *rs); + +static void bbr_handle_inflight_too_high(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_rs *rs, ngtcp2_tstamp ts); + +static void bbr_handle_lost_packet(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts); + +static uint64_t bbr_inflight_hi_from_lost_packet(ngtcp2_bbr2_cc *bbr, + const ngtcp2_rs *rs, + const ngtcp2_cc_pkt *pkt); + +static void bbr_update_min_rtt(ngtcp2_bbr2_cc *bbr, const ngtcp2_cc_ack *ack, + ngtcp2_tstamp ts); + +static void bbr_check_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +static void bbr_enter_probe_rtt(ngtcp2_bbr2_cc *bbr); + +static void bbr_handle_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +static void bbr_check_probe_rtt_done(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts); + +static void bbr_mark_connection_app_limited(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static void bbr_exit_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts); + +static void bbr_handle_restart_from_idle(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +static uint64_t bbr_bdp_multiple(ngtcp2_bbr2_cc *bbr, uint64_t bw, double gain); + +static uint64_t bbr_quantization_budget(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + uint64_t inflight); + +static uint64_t bbr_inflight(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + uint64_t bw, double gain); + +static void bbr_update_max_inflight(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static void bbr_update_offload_budget(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static uint64_t min_pipe_cwnd(size_t max_udp_payload_size); + +static void bbr_advance_max_bw_filter(ngtcp2_bbr2_cc *bbr); + +static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); + +static void bbr_save_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat); + +static void bbr_restore_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat); + +static uint64_t bbr_probe_rtt_cwnd(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static void bbr_bound_cwnd_for_probe_rtt(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static void bbr_set_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); + +static void bbr_bound_cwnd_for_model(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static void bbr_set_send_quantum(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat); + +static int in_congestion_recovery(const ngtcp2_conn_stat *cstat, + ngtcp2_tstamp sent_time); + +static void bbr_handle_recovery(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); + +static void bbr_on_init(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp initial_ts) { + ngtcp2_window_filter_init(&bbr->max_bw_filter, NGTCP2_BBR_MAX_BW_FILTERLEN); + ngtcp2_window_filter_init(&bbr->extra_acked_filter, + NGTCP2_BBR_EXTRA_ACKED_FILTERLEN); + + bbr->min_rtt = UINT64_MAX; + bbr->min_rtt_stamp = initial_ts; + /* remark: Use UINT64_MAX instead of 0 for consistency. */ + bbr->probe_rtt_done_stamp = UINT64_MAX; + bbr->probe_rtt_round_done = 0; + bbr->prior_cwnd = 0; + bbr->idle_restart = 0; + bbr->extra_acked_interval_start = initial_ts; + bbr->extra_acked_delivered = 0; + + bbr_reset_congestion_signals(bbr); + bbr_reset_lower_bounds(bbr); + bbr_init_round_counting(bbr); + bbr_init_full_pipe(bbr); + bbr_init_pacing_rate(bbr, cstat); + bbr_enter_startup(bbr); + + cstat->send_quantum = cstat->max_udp_payload_size * 10; + + /* Missing in documentation */ + bbr->loss_round_start = 0; + bbr->loss_round_delivered = UINT64_MAX; + + bbr->rounds_since_bw_probe = 0; + + bbr->max_bw = 0; + bbr->bw = 0; + + bbr->cycle_count = 0; + + bbr->extra_acked = 0; + + bbr->bytes_lost_in_round = 0; + bbr->loss_events_in_round = 0; + + bbr->offload_budget = 0; + + bbr->probe_up_cnt = UINT64_MAX; + bbr->cycle_stamp = UINT64_MAX; + bbr->ack_phase = 0; + bbr->bw_probe_wait = 0; + bbr->bw_probe_samples = 0; + bbr->bw_probe_up_rounds = 0; + bbr->bw_probe_up_acks = 0; + + bbr->inflight_hi = UINT64_MAX; + bbr->bw_hi = UINT64_MAX; + + bbr->probe_rtt_expired = 0; + bbr->probe_rtt_min_delay = UINT64_MAX; + bbr->probe_rtt_min_stamp = initial_ts; + + bbr->in_loss_recovery = 0; + bbr->packet_conservation = 0; + + bbr->max_inflight = 0; + + bbr->congestion_recovery_start_ts = UINT64_MAX; + bbr->congestion_recovery_next_round_delivered = 0; + + bbr->prior_inflight_lo = 0; + bbr->prior_inflight_hi = 0; + bbr->prior_bw_lo = 0; +} + +static void bbr_reset_congestion_signals(ngtcp2_bbr2_cc *bbr) { + bbr->loss_in_round = 0; + bbr->bw_latest = 0; + bbr->inflight_latest = 0; +} + +static void bbr_reset_lower_bounds(ngtcp2_bbr2_cc *bbr) { + bbr->bw_lo = UINT64_MAX; + bbr->inflight_lo = UINT64_MAX; +} + +static void bbr_init_round_counting(ngtcp2_bbr2_cc *bbr) { + bbr->next_round_delivered = 0; + bbr->round_start = 0; + bbr->round_count = 0; +} + +static void bbr_init_full_pipe(ngtcp2_bbr2_cc *bbr) { + bbr->filled_pipe = 0; + bbr->full_bw = 0; + bbr->full_bw_count = 0; +} + +static void bbr_check_startup_full_bandwidth(ngtcp2_bbr2_cc *bbr) { + if (bbr->filled_pipe || !bbr->round_start || bbr->rst->rs.is_app_limited) { + return; + } + + if (bbr->max_bw * 100 >= bbr->full_bw * 125) { + bbr->full_bw = bbr->max_bw; + bbr->full_bw_count = 0; + } + + ++bbr->full_bw_count; + + if (bbr->full_bw_count >= 3) { + bbr->filled_pipe = 1; + + ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr2 filled pipe, full_bw=%" PRIu64, bbr->full_bw); + } +} + +static void bbr_check_startup_high_loss(ngtcp2_bbr2_cc *bbr, + const ngtcp2_cc_ack *ack) { + if (bbr->filled_pipe || !bbr->round_start || bbr->rst->rs.is_app_limited) { + return; + } + + if (bbr->loss_events_in_round <= 3) { + return; + } + + /* loss_thresh = 2% */ + if (bbr->bytes_lost_in_round * 100 <= ack->prior_bytes_in_flight * 2) { + return; + } + + bbr->filled_pipe = 1; +} + +static void bbr_init_pacing_rate(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat) { + double nominal_bandwidth = (double)bbr->initial_cwnd; + + cstat->pacing_rate = NGTCP2_BBR_STARTUP_PACING_GAIN * nominal_bandwidth / + (double)NGTCP2_MILLISECONDS; +} + +static void bbr_set_pacing_rate_with_gain(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + double pacing_gain) { + double rate = pacing_gain * (double)bbr->bw * + (100 - NGTCP2_BBR_PACING_MARGIN_PERCENT) / 100 / NGTCP2_SECONDS; + + if (bbr->filled_pipe || rate > cstat->pacing_rate) { + cstat->pacing_rate = rate; + } +} + +static void bbr_set_pacing_rate(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat) { + bbr_set_pacing_rate_with_gain(bbr, cstat, bbr->pacing_gain); +} + +static void bbr_enter_startup(ngtcp2_bbr2_cc *bbr) { + ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr2 enter Startup"); + + bbr->state = NGTCP2_BBR2_STATE_STARTUP; + bbr->pacing_gain = NGTCP2_BBR_STARTUP_PACING_GAIN; + bbr->cwnd_gain = NGTCP2_BBR_STARTUP_CWND_GAIN; +} + +static void bbr_check_startup_done(ngtcp2_bbr2_cc *bbr, + const ngtcp2_cc_ack *ack) { + bbr_check_startup_full_bandwidth(bbr); + bbr_check_startup_high_loss(bbr, ack); + + if (bbr->state == NGTCP2_BBR2_STATE_STARTUP && bbr->filled_pipe) { + bbr_enter_drain(bbr); + } +} + +static void bbr_on_transmit(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + bbr_handle_restart_from_idle(bbr, cstat, ts); +} + +static void bbr_update_on_ack(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { + bbr_update_model_and_state(bbr, cstat, ack, ts); + bbr_update_control_parameters(bbr, cstat, ack); +} + +static void bbr_update_model_and_state(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, + ngtcp2_tstamp ts) { + bbr_update_latest_delivery_signals(bbr, cstat); + bbr_update_congestion_signals(bbr, cstat, ack); + bbr_update_ack_aggregation(bbr, cstat, ack, ts); + bbr_check_startup_done(bbr, ack); + bbr_check_drain(bbr, cstat, ts); + bbr_update_probe_bw_cycle_phase(bbr, cstat, ack, ts); + bbr_update_min_rtt(bbr, ack, ts); + bbr_check_probe_rtt(bbr, cstat, ts); + bbr_advance_latest_delivery_signals(bbr, cstat); + bbr_bound_bw_for_model(bbr); +} + +static void bbr_update_control_parameters(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + bbr_set_pacing_rate(bbr, cstat); + bbr_set_send_quantum(bbr, cstat); + bbr_set_cwnd(bbr, cstat, ack); +} + +static void bbr_update_on_loss(ngtcp2_bbr2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) { + bbr_handle_lost_packet(cc, cstat, pkt, ts); +} + +static void bbr_update_latest_delivery_signals(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + bbr->loss_round_start = 0; + bbr->bw_latest = ngtcp2_max(bbr->bw_latest, cstat->delivery_rate_sec); + bbr->inflight_latest = + ngtcp2_max(bbr->inflight_latest, bbr->rst->rs.delivered); + + if (bbr->rst->rs.prior_delivered >= bbr->loss_round_delivered) { + bbr->loss_round_delivered = bbr->rst->delivered; + bbr->loss_round_start = 1; + } +} + +static void bbr_advance_latest_delivery_signals(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + if (bbr->loss_round_start) { + bbr->bw_latest = cstat->delivery_rate_sec; + bbr->inflight_latest = bbr->rst->rs.delivered; + } +} + +static void bbr_update_congestion_signals(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + bbr_update_max_bw(bbr, cstat, ack); + + if (ack->bytes_lost) { + bbr->bytes_lost_in_round += ack->bytes_lost; + ++bbr->loss_events_in_round; + + if (!bbr->loss_in_round) { + bbr->loss_in_round = 1; + bbr->loss_round_delivered = bbr->rst->delivered; + } + } + + if (!bbr->loss_round_start) { + return; + } + + bbr_adapt_lower_bounds_from_congestion(bbr, cstat); + + bbr->loss_in_round = 0; +} + +static void bbr_adapt_lower_bounds_from_congestion(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + if (!bbr->filled_pipe || bbr_is_in_probe_bw_state(bbr)) { + return; + } + + if (bbr->loss_in_round) { + bbr_init_lower_bounds(bbr, cstat); + bbr_loss_lower_bounds(bbr); + } +} + +static void bbr_init_lower_bounds(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + if (bbr->bw_lo == UINT64_MAX) { + bbr->bw_lo = bbr->max_bw; + } + + if (bbr->inflight_lo == UINT64_MAX) { + bbr->inflight_lo = cstat->cwnd; + } +} + +static void bbr_loss_lower_bounds(ngtcp2_bbr2_cc *bbr) { + bbr->bw_lo = ngtcp2_max(bbr->bw_latest, bbr->bw_lo * NGTCP2_BBR_BETA_NUMER / + NGTCP2_BBR_BETA_DENOM); + bbr->inflight_lo = ngtcp2_max(bbr->inflight_latest, + bbr->inflight_lo * NGTCP2_BBR_BETA_NUMER / + NGTCP2_BBR_BETA_DENOM); +} + +static void bbr_bound_bw_for_model(ngtcp2_bbr2_cc *bbr) { + bbr->bw = ngtcp2_min(bbr->max_bw, bbr->bw_lo); + bbr->bw = ngtcp2_min(bbr->bw, bbr->bw_hi); +} + +static void bbr_update_max_bw(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + bbr_update_round(bbr, ack); + bbr_handle_recovery(bbr, cstat, ack); + + if (cstat->delivery_rate_sec >= bbr->max_bw || !bbr->rst->rs.is_app_limited) { + ngtcp2_window_filter_update(&bbr->max_bw_filter, cstat->delivery_rate_sec, + bbr->cycle_count); + + bbr->max_bw = ngtcp2_window_filter_get_best(&bbr->max_bw_filter); + } +} + +static void bbr_update_round(ngtcp2_bbr2_cc *bbr, const ngtcp2_cc_ack *ack) { + if (ack->pkt_delivered >= bbr->next_round_delivered) { + bbr_start_round(bbr); + + ++bbr->round_count; + ++bbr->rounds_since_bw_probe; + bbr->round_start = 1; + + bbr->bytes_lost_in_round = 0; + bbr->loss_events_in_round = 0; + + bbr->rst->is_cwnd_limited = 0; + + return; + } + + bbr->round_start = 0; +} + +static void bbr_start_round(ngtcp2_bbr2_cc *bbr) { + bbr->next_round_delivered = bbr->rst->delivered; +} + +static int bbr_is_in_probe_bw_state(ngtcp2_bbr2_cc *bbr) { + switch (bbr->state) { + case NGTCP2_BBR2_STATE_PROBE_BW_DOWN: + case NGTCP2_BBR2_STATE_PROBE_BW_CRUISE: + case NGTCP2_BBR2_STATE_PROBE_BW_REFILL: + case NGTCP2_BBR2_STATE_PROBE_BW_UP: + return 1; + default: + return 0; + } +} + +static void bbr_update_ack_aggregation(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, + ngtcp2_tstamp ts) { + ngtcp2_duration interval = ts - bbr->extra_acked_interval_start; + uint64_t expected_delivered = bbr->bw * interval / NGTCP2_SECONDS; + uint64_t extra; + + if (bbr->extra_acked_delivered <= expected_delivered) { + bbr->extra_acked_delivered = 0; + bbr->extra_acked_interval_start = ts; + expected_delivered = 0; + } + + bbr->extra_acked_delivered += ack->bytes_delivered; + extra = bbr->extra_acked_delivered - expected_delivered; + extra = ngtcp2_min(extra, cstat->cwnd); + + ngtcp2_window_filter_update(&bbr->extra_acked_filter, extra, + bbr->round_count); + + bbr->extra_acked = ngtcp2_window_filter_get_best(&bbr->extra_acked_filter); +} + +static void bbr_enter_drain(ngtcp2_bbr2_cc *bbr) { + ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr2 enter Drain"); + + bbr->state = NGTCP2_BBR2_STATE_DRAIN; + bbr->pacing_gain = 1. / NGTCP2_BBR_STARTUP_CWND_GAIN; + bbr->cwnd_gain = NGTCP2_BBR_STARTUP_CWND_GAIN; +} + +static void bbr_check_drain(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + if (bbr->state == NGTCP2_BBR2_STATE_DRAIN && + cstat->bytes_in_flight <= bbr_inflight(bbr, cstat, bbr->bw, 1.0)) { + bbr_enter_probe_bw(bbr, ts); + } +} + +static void bbr_enter_probe_bw(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts) { + bbr_start_probe_bw_down(bbr, ts); +} + +static void bbr_start_probe_bw_down(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts) { + ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr2 start ProbeBW_DOWN"); + + bbr_reset_congestion_signals(bbr); + + bbr->probe_up_cnt = UINT64_MAX; + + bbr_pick_probe_wait(bbr); + + bbr->cycle_stamp = ts; + bbr->ack_phase = NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STOPPING; + + bbr_start_round(bbr); + + bbr->state = NGTCP2_BBR2_STATE_PROBE_BW_DOWN; + bbr->pacing_gain = 0.9; + bbr->cwnd_gain = 2; +} + +static void bbr_start_probe_bw_cruise(ngtcp2_bbr2_cc *bbr) { + ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr2 start ProbeBW_CRUISE"); + + bbr->state = NGTCP2_BBR2_STATE_PROBE_BW_CRUISE; + bbr->pacing_gain = 1.0; + bbr->cwnd_gain = 2; +} + +static void bbr_start_probe_bw_refill(ngtcp2_bbr2_cc *bbr) { + ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr2 start ProbeBW_REFILL"); + + bbr_reset_lower_bounds(bbr); + + bbr->bw_probe_up_rounds = 0; + bbr->bw_probe_up_acks = 0; + bbr->ack_phase = NGTCP2_BBR2_ACK_PHASE_ACKS_REFILLING; + + bbr_start_round(bbr); + + bbr->state = NGTCP2_BBR2_STATE_PROBE_BW_REFILL; + bbr->pacing_gain = 1.0; + bbr->cwnd_gain = 2; +} + +static void bbr_start_probe_bw_up(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr2 start ProbeBW_UP"); + + bbr->ack_phase = NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STARTING; + + bbr_start_round(bbr); + + bbr->cycle_stamp = ts; + bbr->state = NGTCP2_BBR2_STATE_PROBE_BW_UP; + bbr->pacing_gain = 1.25; + bbr->cwnd_gain = 2; + + bbr_raise_inflight_hi_slope(bbr, cstat); +} + +static void bbr_update_probe_bw_cycle_phase(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, + ngtcp2_tstamp ts) { + if (!bbr->filled_pipe) { + return; + } + + bbr_adapt_upper_bounds(bbr, cstat, ack, ts); + + if (!bbr_is_in_probe_bw_state(bbr)) { + return; + } + + switch (bbr->state) { + case NGTCP2_BBR2_STATE_PROBE_BW_DOWN: + if (bbr_check_time_to_probe_bw(bbr, cstat, ts)) { + return; + } + + if (bbr_check_time_to_cruise(bbr, cstat, ts)) { + bbr_start_probe_bw_cruise(bbr); + } + + break; + case NGTCP2_BBR2_STATE_PROBE_BW_CRUISE: + if (bbr_check_time_to_probe_bw(bbr, cstat, ts)) { + return; + } + + break; + case NGTCP2_BBR2_STATE_PROBE_BW_REFILL: + if (bbr->round_start) { + bbr->bw_probe_samples = 1; + bbr_start_probe_bw_up(bbr, cstat, ts); + } + + break; + case NGTCP2_BBR2_STATE_PROBE_BW_UP: + if (bbr_has_elapsed_in_phase(bbr, bbr->min_rtt, ts) && + cstat->bytes_in_flight > bbr_inflight(bbr, cstat, bbr->max_bw, 1.25)) { + bbr_start_probe_bw_down(bbr, ts); + } + + break; + default: + break; + } +} + +static int bbr_check_time_to_cruise(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) { + (void)ts; + + if (cstat->bytes_in_flight > bbr_inflight_with_headroom(bbr, cstat)) { + return 0; + } + + if (cstat->bytes_in_flight <= bbr_inflight(bbr, cstat, bbr->max_bw, 1.0)) { + return 1; + } + + return 0; +} + +static int bbr_has_elapsed_in_phase(ngtcp2_bbr2_cc *bbr, + ngtcp2_duration interval, + ngtcp2_tstamp ts) { + return ts > bbr->cycle_stamp + interval; +} + +static uint64_t bbr_inflight_with_headroom(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + uint64_t headroom; + uint64_t mpcwnd; + if (bbr->inflight_hi == UINT64_MAX) { + return UINT64_MAX; + } + + headroom = ngtcp2_max(cstat->max_udp_payload_size, + bbr->inflight_hi * NGTCP2_BBR_HEADROOM_NUMER / + NGTCP2_BBR_HEADROOM_DENOM); + mpcwnd = min_pipe_cwnd(cstat->max_udp_payload_size); + + if (bbr->inflight_hi > headroom) { + return ngtcp2_max(bbr->inflight_hi - headroom, mpcwnd); + } + + return mpcwnd; +} + +static void bbr_raise_inflight_hi_slope(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + uint64_t growth_this_round = cstat->max_udp_payload_size + << bbr->bw_probe_up_rounds; + + bbr->bw_probe_up_rounds = ngtcp2_min(bbr->bw_probe_up_rounds + 1, 30); + bbr->probe_up_cnt = ngtcp2_max(cstat->cwnd / growth_this_round, 1) * + cstat->max_udp_payload_size; +} + +static void bbr_probe_inflight_hi_upward(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + uint64_t delta; + + if (!bbr->rst->is_cwnd_limited || cstat->cwnd < bbr->inflight_hi) { + return; + } + + bbr->bw_probe_up_acks += ack->bytes_delivered; + + if (bbr->bw_probe_up_acks >= bbr->probe_up_cnt) { + delta = bbr->bw_probe_up_acks / bbr->probe_up_cnt; + bbr->bw_probe_up_acks -= delta * bbr->probe_up_cnt; + bbr->inflight_hi += delta * cstat->max_udp_payload_size; + } + + if (bbr->round_start) { + bbr_raise_inflight_hi_slope(bbr, cstat); + } +} + +static void bbr_adapt_upper_bounds(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { + if (bbr->ack_phase == NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STARTING && + bbr->round_start) { + bbr->ack_phase = NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_FEEDBACK; + } + + if (bbr->ack_phase == NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STOPPING && + bbr->round_start) { + if (bbr_is_in_probe_bw_state(bbr) && !bbr->rst->rs.is_app_limited) { + bbr_advance_max_bw_filter(bbr); + } + } + + if (!bbr_check_inflight_too_high(bbr, cstat, ts)) { + /* bbr->bw_hi never be updated */ + if (bbr->inflight_hi == UINT64_MAX /* || bbr->bw_hi == UINT64_MAX */) { + return; + } + + if (bbr->rst->rs.tx_in_flight > bbr->inflight_hi) { + bbr->inflight_hi = bbr->rst->rs.tx_in_flight; + } + + if (cstat->delivery_rate_sec > bbr->bw_hi) { + bbr->bw_hi = cstat->delivery_rate_sec; + } + + if (bbr->state == NGTCP2_BBR2_STATE_PROBE_BW_UP) { + bbr_probe_inflight_hi_upward(bbr, cstat, ack); + } + } +} + +static int bbr_check_time_to_probe_bw(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + if (bbr_has_elapsed_in_phase(bbr, bbr->bw_probe_wait, ts) || + bbr_is_reno_coexistence_probe_time(bbr, cstat)) { + bbr_start_probe_bw_refill(bbr); + + return 1; + } + + return 0; +} + +static void bbr_pick_probe_wait(ngtcp2_bbr2_cc *bbr) { + uint8_t rand; + + bbr->rand(&rand, 1, &bbr->rand_ctx); + + bbr->rounds_since_bw_probe = (uint64_t)(rand * 2 / 256); + + bbr->rand(&rand, 1, &bbr->rand_ctx); + + bbr->bw_probe_wait = 2 * NGTCP2_SECONDS + + (ngtcp2_tstamp)((double)rand / 255. * NGTCP2_SECONDS); +} + +static int bbr_is_reno_coexistence_probe_time(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + uint64_t reno_rounds = + bbr_target_inflight(bbr, cstat) / cstat->max_udp_payload_size; + + return bbr->rounds_since_bw_probe >= ngtcp2_min(reno_rounds, 63); +} + +static uint64_t bbr_target_inflight(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + uint64_t bdp = bbr_inflight(bbr, cstat, bbr->bw, 1.0); + + return ngtcp2_min(bdp, cstat->cwnd); +} + +static int bbr_check_inflight_too_high(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + if (is_inflight_too_high(&bbr->rst->rs)) { + if (bbr->bw_probe_samples) { + bbr_handle_inflight_too_high(bbr, cstat, &bbr->rst->rs, ts); + } + + return 1; + } + + return 0; +} + +static int is_inflight_too_high(const ngtcp2_rs *rs) { + return rs->lost * NGTCP2_BBR_LOSS_THRESH_DENOM > + rs->tx_in_flight * NGTCP2_BBR_LOSS_THRESH_NUMER; +} + +static void bbr_handle_inflight_too_high(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_rs *rs, + ngtcp2_tstamp ts) { + bbr->bw_probe_samples = 0; + + if (!rs->is_app_limited) { + bbr->prior_inflight_hi = bbr->inflight_hi; + + bbr->inflight_hi = ngtcp2_max( + rs->tx_in_flight, bbr_target_inflight(bbr, cstat) * + NGTCP2_BBR_BETA_NUMER / NGTCP2_BBR_BETA_DENOM); + } + + if (bbr->state == NGTCP2_BBR2_STATE_PROBE_BW_UP) { + bbr_start_probe_bw_down(bbr, ts); + } +} + +static void bbr_handle_lost_packet(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) { + ngtcp2_rs rs = {0}; + + if (!bbr->bw_probe_samples) { + return; + } + + rs.tx_in_flight = pkt->tx_in_flight; + rs.lost = bbr->rst->lost - pkt->lost; + rs.is_app_limited = pkt->is_app_limited; + + if (is_inflight_too_high(&rs)) { + rs.tx_in_flight = bbr_inflight_hi_from_lost_packet(bbr, &rs, pkt); + + bbr_handle_inflight_too_high(bbr, cstat, &rs, ts); + } +} + +static uint64_t bbr_inflight_hi_from_lost_packet(ngtcp2_bbr2_cc *bbr, + const ngtcp2_rs *rs, + const ngtcp2_cc_pkt *pkt) { + uint64_t inflight_prev, lost_prefix; + (void)bbr; + + assert(rs->tx_in_flight >= pkt->pktlen); + + inflight_prev = rs->tx_in_flight - pkt->pktlen; + + assert(rs->lost >= pkt->pktlen); + + /* bbr->rst->lost is not incremented for pkt yet */ + + if (inflight_prev * NGTCP2_BBR_LOSS_THRESH_NUMER < + rs->lost * NGTCP2_BBR_LOSS_THRESH_DENOM) { + return inflight_prev; + } + + lost_prefix = (inflight_prev * NGTCP2_BBR_LOSS_THRESH_NUMER - + rs->lost * NGTCP2_BBR_LOSS_THRESH_DENOM) / + (NGTCP2_BBR_LOSS_THRESH_DENOM - NGTCP2_BBR_LOSS_THRESH_NUMER); + + return inflight_prev + lost_prefix; +} + +static void bbr_update_min_rtt(ngtcp2_bbr2_cc *bbr, const ngtcp2_cc_ack *ack, + ngtcp2_tstamp ts) { + int min_rtt_expired; + + bbr->probe_rtt_expired = + ts > bbr->probe_rtt_min_stamp + NGTCP2_BBR_PROBE_RTT_INTERVAL; + + if (ack->rtt != UINT64_MAX && + (ack->rtt < bbr->probe_rtt_min_delay || bbr->probe_rtt_expired)) { + bbr->probe_rtt_min_delay = ack->rtt; + bbr->probe_rtt_min_stamp = ts; + } + + min_rtt_expired = ts > bbr->min_rtt_stamp + NGTCP2_BBR_MIN_RTT_FILTERLEN; + + if (bbr->probe_rtt_min_delay < bbr->min_rtt || min_rtt_expired) { + bbr->min_rtt = bbr->probe_rtt_min_delay; + bbr->min_rtt_stamp = bbr->probe_rtt_min_stamp; + + ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr2 update min_rtt=%" PRIu64, bbr->min_rtt); + } +} + +static void bbr_check_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + if (bbr->state != NGTCP2_BBR2_STATE_PROBE_RTT && bbr->probe_rtt_expired && + !bbr->idle_restart) { + bbr_enter_probe_rtt(bbr); + bbr_save_cwnd(bbr, cstat); + + bbr->probe_rtt_done_stamp = UINT64_MAX; + bbr->ack_phase = NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STOPPING; + + bbr_start_round(bbr); + } + + if (bbr->state == NGTCP2_BBR2_STATE_PROBE_RTT) { + bbr_handle_probe_rtt(bbr, cstat, ts); + } + + if (bbr->rst->rs.delivered) { + bbr->idle_restart = 0; + } +} + +static void bbr_enter_probe_rtt(ngtcp2_bbr2_cc *bbr) { + ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr2 enter ProbeRTT"); + + bbr->state = NGTCP2_BBR2_STATE_PROBE_RTT; + bbr->pacing_gain = 1; + bbr->cwnd_gain = NGTCP2_BBR_PROBE_RTT_CWND_GAIN; +} + +static void bbr_handle_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + bbr_mark_connection_app_limited(bbr, cstat); + + if (bbr->probe_rtt_done_stamp == UINT64_MAX && + cstat->bytes_in_flight <= bbr_probe_rtt_cwnd(bbr, cstat)) { + bbr->probe_rtt_done_stamp = ts + NGTCP2_BBR_PROBE_RTT_DURATION; + bbr->probe_rtt_round_done = 0; + + bbr_start_round(bbr); + + return; + } + + if (bbr->probe_rtt_done_stamp != UINT64_MAX) { + if (bbr->round_start) { + bbr->probe_rtt_round_done = 1; + } + + if (bbr->probe_rtt_round_done) { + bbr_check_probe_rtt_done(bbr, cstat, ts); + } + } +} + +static void bbr_check_probe_rtt_done(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + if (bbr->probe_rtt_done_stamp != UINT64_MAX && + ts > bbr->probe_rtt_done_stamp) { + bbr->probe_rtt_min_stamp = ts; + bbr_restore_cwnd(bbr, cstat); + bbr_exit_probe_rtt(bbr, ts); + } +} + +static void bbr_mark_connection_app_limited(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + uint64_t app_limited = bbr->rst->delivered + cstat->bytes_in_flight; + + if (app_limited) { + bbr->rst->app_limited = app_limited; + } else { + bbr->rst->app_limited = cstat->max_udp_payload_size; + } +} + +static void bbr_exit_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts) { + bbr_reset_lower_bounds(bbr); + + if (bbr->filled_pipe) { + bbr_start_probe_bw_down(bbr, ts); + bbr_start_probe_bw_cruise(bbr); + } else { + bbr_enter_startup(bbr); + } +} + +static void bbr_handle_restart_from_idle(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + if (cstat->bytes_in_flight == 0 && bbr->rst->app_limited) { + ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr2 restart from idle"); + + bbr->idle_restart = 1; + bbr->extra_acked_interval_start = ts; + + if (bbr_is_in_probe_bw_state(bbr)) { + bbr_set_pacing_rate_with_gain(bbr, cstat, 1); + } else if (bbr->state == NGTCP2_BBR2_STATE_PROBE_RTT) { + bbr_check_probe_rtt_done(bbr, cstat, ts); + } + } +} + +static uint64_t bbr_bdp_multiple(ngtcp2_bbr2_cc *bbr, uint64_t bw, + double gain) { + uint64_t bdp; + + if (bbr->min_rtt == UINT64_MAX) { + return bbr->initial_cwnd; + } + + bdp = bw * bbr->min_rtt / NGTCP2_SECONDS; + + return (uint64_t)(gain * (double)bdp); +} + +static uint64_t min_pipe_cwnd(size_t max_udp_payload_size) { + return max_udp_payload_size * 4; +} + +static uint64_t bbr_quantization_budget(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + uint64_t inflight) { + bbr_update_offload_budget(bbr, cstat); + + inflight = ngtcp2_max(inflight, bbr->offload_budget); + inflight = ngtcp2_max(inflight, min_pipe_cwnd(cstat->max_udp_payload_size)); + + if (bbr->state == NGTCP2_BBR2_STATE_PROBE_BW_UP) { + inflight += 2 * cstat->max_udp_payload_size; + } + + return inflight; +} + +static uint64_t bbr_inflight(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + uint64_t bw, double gain) { + uint64_t inflight = bbr_bdp_multiple(bbr, bw, gain); + + return bbr_quantization_budget(bbr, cstat, inflight); +} + +static void bbr_update_max_inflight(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + uint64_t inflight; + + /* Not documented */ + /* bbr_update_aggregation_budget(bbr); */ + + inflight = bbr_bdp_multiple(bbr, bbr->bw, bbr->cwnd_gain) + bbr->extra_acked; + bbr->max_inflight = bbr_quantization_budget(bbr, cstat, inflight); +} + +static void bbr_update_offload_budget(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + bbr->offload_budget = 3 * cstat->send_quantum; +} + +static void bbr_advance_max_bw_filter(ngtcp2_bbr2_cc *bbr) { + ++bbr->cycle_count; +} + +static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + if (ack->bytes_lost > 0) { + if (cstat->cwnd > ack->bytes_lost) { + cstat->cwnd -= ack->bytes_lost; + cstat->cwnd = ngtcp2_max(cstat->cwnd, 2 * cstat->max_udp_payload_size); + } else { + cstat->cwnd = cstat->max_udp_payload_size; + } + } + + if (bbr->packet_conservation) { + cstat->cwnd = + ngtcp2_max(cstat->cwnd, cstat->bytes_in_flight + ack->bytes_delivered); + } +} + +static void bbr_save_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat) { + if (!bbr->in_loss_recovery && bbr->state != NGTCP2_BBR2_STATE_PROBE_RTT) { + bbr->prior_cwnd = cstat->cwnd; + return; + } + + bbr->prior_cwnd = ngtcp2_max(bbr->prior_cwnd, cstat->cwnd); +} + +static void bbr_restore_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat) { + cstat->cwnd = ngtcp2_max(cstat->cwnd, bbr->prior_cwnd); +} + +static uint64_t bbr_probe_rtt_cwnd(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + uint64_t probe_rtt_cwnd = + bbr_bdp_multiple(bbr, bbr->bw, NGTCP2_BBR_PROBE_RTT_CWND_GAIN); + uint64_t mpcwnd = min_pipe_cwnd(cstat->max_udp_payload_size); + + return ngtcp2_max(probe_rtt_cwnd, mpcwnd); +} + +static void bbr_bound_cwnd_for_probe_rtt(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + uint64_t probe_rtt_cwnd; + + if (bbr->state == NGTCP2_BBR2_STATE_PROBE_RTT) { + probe_rtt_cwnd = bbr_probe_rtt_cwnd(bbr, cstat); + + cstat->cwnd = ngtcp2_min(cstat->cwnd, probe_rtt_cwnd); + } +} + +static void bbr_set_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + uint64_t mpcwnd; + + bbr_update_max_inflight(bbr, cstat); + bbr_modulate_cwnd_for_recovery(bbr, cstat, ack); + + if (!bbr->packet_conservation) { + if (bbr->filled_pipe) { + cstat->cwnd += ack->bytes_delivered; + cstat->cwnd = ngtcp2_min(cstat->cwnd, bbr->max_inflight); + } else if (cstat->cwnd < bbr->max_inflight || + bbr->rst->delivered < bbr->initial_cwnd) { + cstat->cwnd += ack->bytes_delivered; + } + + mpcwnd = min_pipe_cwnd(cstat->max_udp_payload_size); + cstat->cwnd = ngtcp2_max(cstat->cwnd, mpcwnd); + } + + bbr_bound_cwnd_for_probe_rtt(bbr, cstat); + bbr_bound_cwnd_for_model(bbr, cstat); +} + +static void bbr_bound_cwnd_for_model(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + uint64_t cap = UINT64_MAX; + uint64_t mpcwnd = min_pipe_cwnd(cstat->max_udp_payload_size); + + if (bbr_is_in_probe_bw_state(bbr) && + bbr->state != NGTCP2_BBR2_STATE_PROBE_BW_CRUISE) { + cap = bbr->inflight_hi; + } else if (bbr->state == NGTCP2_BBR2_STATE_PROBE_RTT || + bbr->state == NGTCP2_BBR2_STATE_PROBE_BW_CRUISE) { + cap = bbr_inflight_with_headroom(bbr, cstat); + } + + cap = ngtcp2_min(cap, bbr->inflight_lo); + cap = ngtcp2_max(cap, mpcwnd); + + cstat->cwnd = ngtcp2_min(cstat->cwnd, cap); +} + +static void bbr_set_send_quantum(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat) { + size_t send_quantum = + (size_t)(cstat->pacing_rate * (double)(bbr->min_rtt == UINT64_MAX + ? NGTCP2_MILLISECONDS + : bbr->min_rtt)); + (void)bbr; + + cstat->send_quantum = ngtcp2_min(send_quantum, 64 * 1024); + cstat->send_quantum = + ngtcp2_max(cstat->send_quantum, cstat->max_udp_payload_size * 10); +} + +static int in_congestion_recovery(const ngtcp2_conn_stat *cstat, + ngtcp2_tstamp sent_time) { + return cstat->congestion_recovery_start_ts != UINT64_MAX && + sent_time <= cstat->congestion_recovery_start_ts; +} + +static void bbr_handle_recovery(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + if (bbr->in_loss_recovery) { + if (ack->pkt_delivered >= bbr->congestion_recovery_next_round_delivered) { + bbr->packet_conservation = 0; + } + + if (!in_congestion_recovery(cstat, ack->largest_acked_sent_ts)) { + bbr->in_loss_recovery = 0; + bbr->packet_conservation = 0; + bbr_restore_cwnd(bbr, cstat); + } + + return; + } + + if (bbr->congestion_recovery_start_ts != UINT64_MAX) { + bbr->in_loss_recovery = 1; + bbr_save_cwnd(bbr, cstat); + cstat->cwnd = cstat->bytes_in_flight + + ngtcp2_max(ack->bytes_delivered, cstat->max_udp_payload_size); + + cstat->congestion_recovery_start_ts = bbr->congestion_recovery_start_ts; + bbr->congestion_recovery_start_ts = UINT64_MAX; + bbr->packet_conservation = 1; + bbr->congestion_recovery_next_round_delivered = bbr->rst->delivered; + bbr->prior_inflight_lo = bbr->inflight_lo; + bbr->prior_bw_lo = bbr->bw_lo; + } +} + +static void bbr2_cc_init(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_rst *rst, ngtcp2_tstamp initial_ts, + ngtcp2_rand rand, const ngtcp2_rand_ctx *rand_ctx, + ngtcp2_log *log) { + bbr->ccb.log = log; + bbr->rst = rst; + bbr->rand = rand; + bbr->rand_ctx = *rand_ctx; + bbr->initial_cwnd = cstat->cwnd; + + bbr_on_init(bbr, cstat, initial_ts); +} + +static void bbr2_cc_free(ngtcp2_bbr2_cc *bbr) { (void)bbr; } + +static void bbr2_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) { + (void)ccx; + (void)cstat; + (void)pkt; + (void)ts; +} + +static void bbr2_cc_on_pkt_lost(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) { + ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb); + + bbr_update_on_loss(bbr, cstat, pkt, ts); +} + +static void bbr2_cc_congestion_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp sent_ts, ngtcp2_tstamp ts) { + ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb); + + if (!bbr->filled_pipe || bbr->in_loss_recovery || + bbr->congestion_recovery_start_ts != UINT64_MAX || + in_congestion_recovery(cstat, sent_ts)) { + return; + } + + bbr->congestion_recovery_start_ts = ts; +} + +static void bbr2_cc_on_spurious_congestion(ngtcp2_cc *ccx, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb); + (void)ts; + + bbr->congestion_recovery_start_ts = UINT64_MAX; + cstat->congestion_recovery_start_ts = UINT64_MAX; + + if (bbr->in_loss_recovery) { + bbr->in_loss_recovery = 0; + bbr->packet_conservation = 0; + bbr_restore_cwnd(bbr, cstat); + bbr->full_bw_count = 0; + bbr->loss_in_round = 0; + bbr->inflight_lo = ngtcp2_max(bbr->inflight_lo, bbr->prior_inflight_lo); + bbr->inflight_hi = ngtcp2_max(bbr->inflight_hi, bbr->prior_inflight_hi); + bbr->bw_lo = ngtcp2_max(bbr->bw_lo, bbr->prior_bw_lo); + } +} + +static void bbr2_cc_on_persistent_congestion(ngtcp2_cc *ccx, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb); + (void)ts; + + cstat->congestion_recovery_start_ts = UINT64_MAX; + bbr->congestion_recovery_start_ts = UINT64_MAX; + bbr->in_loss_recovery = 0; + bbr->packet_conservation = 0; + + bbr_save_cwnd(bbr, cstat); + cstat->cwnd = cstat->bytes_in_flight + cstat->max_udp_payload_size; + cstat->cwnd = + ngtcp2_max(cstat->cwnd, min_pipe_cwnd(cstat->max_udp_payload_size)); +} + +static void bbr2_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { + ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb); + + bbr_update_on_ack(bbr, cstat, ack, ts); +} + +static void bbr2_cc_on_pkt_sent(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt) { + ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb); + + bbr_on_transmit(bbr, cstat, pkt->sent_ts); +} + +static void bbr2_cc_new_rtt_sample(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + (void)ccx; + (void)cstat; + (void)ts; +} + +static void bbr2_cc_reset(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb); + + bbr_on_init(bbr, cstat, ts); +} + +static void bbr2_cc_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_cc_event_type event, ngtcp2_tstamp ts) { + (void)ccx; + (void)cstat; + (void)event; + (void)ts; +} + +int ngtcp2_cc_bbr2_cc_init(ngtcp2_cc *cc, ngtcp2_log *log, + ngtcp2_conn_stat *cstat, ngtcp2_rst *rst, + ngtcp2_tstamp initial_ts, ngtcp2_rand rand, + const ngtcp2_rand_ctx *rand_ctx, + const ngtcp2_mem *mem) { + ngtcp2_bbr2_cc *bbr; + + bbr = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_bbr2_cc)); + if (bbr == NULL) { + return NGTCP2_ERR_NOMEM; + } + + bbr2_cc_init(bbr, cstat, rst, initial_ts, rand, rand_ctx, log); + + cc->ccb = &bbr->ccb; + cc->on_pkt_acked = bbr2_cc_on_pkt_acked; + cc->on_pkt_lost = bbr2_cc_on_pkt_lost; + cc->congestion_event = bbr2_cc_congestion_event; + cc->on_spurious_congestion = bbr2_cc_on_spurious_congestion; + cc->on_persistent_congestion = bbr2_cc_on_persistent_congestion; + cc->on_ack_recv = bbr2_cc_on_ack_recv; + cc->on_pkt_sent = bbr2_cc_on_pkt_sent; + cc->new_rtt_sample = bbr2_cc_new_rtt_sample; + cc->reset = bbr2_cc_reset; + cc->event = bbr2_cc_event; + + return 0; +} + +void ngtcp2_cc_bbr2_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem) { + ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(cc->ccb, ngtcp2_bbr2_cc, ccb); + + bbr2_cc_free(bbr); + ngtcp2_mem_free(mem, bbr); +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr2.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr2.h new file mode 100644 index 00000000000000..50dc05a5f26121 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr2.h @@ -0,0 +1,149 @@ +/* + * ngtcp2 + * + * Copyright (c) 2021 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_BBR2_H +#define NGTCP2_BBR2_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_cc.h" +#include "ngtcp2_window_filter.h" + +typedef struct ngtcp2_rst ngtcp2_rst; + +typedef enum ngtcp2_bbr2_state { + NGTCP2_BBR2_STATE_STARTUP, + NGTCP2_BBR2_STATE_DRAIN, + NGTCP2_BBR2_STATE_PROBE_BW_DOWN, + NGTCP2_BBR2_STATE_PROBE_BW_CRUISE, + NGTCP2_BBR2_STATE_PROBE_BW_REFILL, + NGTCP2_BBR2_STATE_PROBE_BW_UP, + NGTCP2_BBR2_STATE_PROBE_RTT, +} ngtcp2_bbr2_state; + +typedef enum ngtcp2_bbr2_ack_phase { + NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STARTING, + NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STOPPING, + NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_FEEDBACK, + NGTCP2_BBR2_ACK_PHASE_ACKS_REFILLING, +} ngtcp2_bbr2_ack_phase; + +/* + * ngtcp2_bbr2_cc is BBR v2 congestion controller, described in + * https://datatracker.ietf.org/doc/html/draft-cardwell-iccrg-bbr-congestion-control-01 + */ +typedef struct ngtcp2_bbr2_cc { + ngtcp2_cc_base ccb; + + uint64_t initial_cwnd; + ngtcp2_rst *rst; + ngtcp2_rand rand; + ngtcp2_rand_ctx rand_ctx; + + /* max_bw_filter for tracking the maximum recent delivery rate + samples for estimating max_bw. */ + ngtcp2_window_filter max_bw_filter; + + ngtcp2_window_filter extra_acked_filter; + + ngtcp2_duration min_rtt; + ngtcp2_tstamp min_rtt_stamp; + ngtcp2_tstamp probe_rtt_done_stamp; + int probe_rtt_round_done; + uint64_t prior_cwnd; + int idle_restart; + ngtcp2_tstamp extra_acked_interval_start; + uint64_t extra_acked_delivered; + + /* Congestion signals */ + int loss_in_round; + uint64_t bw_latest; + uint64_t inflight_latest; + + /* Lower bounds */ + uint64_t bw_lo; + uint64_t inflight_lo; + + /* Round counting */ + uint64_t next_round_delivered; + int round_start; + uint64_t round_count; + + /* Full pipe */ + int filled_pipe; + uint64_t full_bw; + size_t full_bw_count; + + /* Pacing rate */ + double pacing_gain; + + ngtcp2_bbr2_state state; + double cwnd_gain; + + int loss_round_start; + uint64_t loss_round_delivered; + uint64_t rounds_since_bw_probe; + uint64_t max_bw; + uint64_t bw; + uint64_t cycle_count; + uint64_t extra_acked; + uint64_t bytes_lost_in_round; + size_t loss_events_in_round; + uint64_t offload_budget; + uint64_t probe_up_cnt; + ngtcp2_tstamp cycle_stamp; + ngtcp2_bbr2_ack_phase ack_phase; + ngtcp2_duration bw_probe_wait; + int bw_probe_samples; + size_t bw_probe_up_rounds; + uint64_t bw_probe_up_acks; + uint64_t inflight_hi; + uint64_t bw_hi; + int probe_rtt_expired; + ngtcp2_duration probe_rtt_min_delay; + ngtcp2_tstamp probe_rtt_min_stamp; + int in_loss_recovery; + int packet_conservation; + uint64_t max_inflight; + ngtcp2_tstamp congestion_recovery_start_ts; + uint64_t congestion_recovery_next_round_delivered; + + uint64_t prior_inflight_lo; + uint64_t prior_inflight_hi; + uint64_t prior_bw_lo; +} ngtcp2_bbr2_cc; + +int ngtcp2_cc_bbr2_cc_init(ngtcp2_cc *cc, ngtcp2_log *log, + ngtcp2_conn_stat *cstat, ngtcp2_rst *rst, + ngtcp2_tstamp initial_ts, ngtcp2_rand rand, + const ngtcp2_rand_ctx *rand_ctx, + const ngtcp2_mem *mem); + +void ngtcp2_cc_bbr2_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem); + +#endif /* NGTCP2_BBR2_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.c index 373f23d91aed7c..75326d6b76b9ca 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.c @@ -23,6 +23,7 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "ngtcp2_buf.h" +#include "ngtcp2_mem.h" void ngtcp2_buf_init(ngtcp2_buf *buf, uint8_t *begin, size_t len) { buf->begin = buf->pos = buf->last = begin; @@ -31,14 +32,25 @@ void ngtcp2_buf_init(ngtcp2_buf *buf, uint8_t *begin, size_t len) { void ngtcp2_buf_reset(ngtcp2_buf *buf) { buf->pos = buf->last = buf->begin; } -size_t ngtcp2_buf_left(const ngtcp2_buf *buf) { - return (size_t)(buf->end - buf->last); +size_t ngtcp2_buf_cap(const ngtcp2_buf *buf) { + return (size_t)(buf->end - buf->begin); } -size_t ngtcp2_buf_len(const ngtcp2_buf *buf) { - return (size_t)(buf->last - buf->pos); +int ngtcp2_buf_chain_new(ngtcp2_buf_chain **pbufchain, size_t len, + const ngtcp2_mem *mem) { + *pbufchain = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_buf_chain) + len); + if (*pbufchain == NULL) { + return NGTCP2_ERR_NOMEM; + } + + (*pbufchain)->next = NULL; + + ngtcp2_buf_init(&(*pbufchain)->buf, + (uint8_t *)(*pbufchain) + sizeof(ngtcp2_buf_chain), len); + + return 0; } -size_t ngtcp2_buf_cap(const ngtcp2_buf *buf) { - return (size_t)(buf->end - buf->begin); +void ngtcp2_buf_chain_del(ngtcp2_buf_chain *bufchain, const ngtcp2_mem *mem) { + ngtcp2_mem_free(mem, bufchain); } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h index 8f2c36a1bbb218..107d413382da20 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h @@ -62,13 +62,13 @@ void ngtcp2_buf_reset(ngtcp2_buf *buf); * written to the underlying buffer. In other words, it returns * buf->end - buf->last. */ -size_t ngtcp2_buf_left(const ngtcp2_buf *buf); +#define ngtcp2_buf_left(BUF) (size_t)((BUF)->end - (BUF)->last) /* * ngtcp2_buf_len returns the number of bytes left to read. In other * words, it returns buf->last - buf->pos. */ -size_t ngtcp2_buf_len(const ngtcp2_buf *buf); +#define ngtcp2_buf_len(BUF) (size_t)((BUF)->last - (BUF)->pos) /* * ngtcp2_buf_cap returns the capacity of the buffer. In other words, @@ -76,4 +76,33 @@ size_t ngtcp2_buf_len(const ngtcp2_buf *buf); */ size_t ngtcp2_buf_cap(const ngtcp2_buf *buf); +/* + * ngtcp2_buf_chain is a linked list of ngtcp2_buf. + */ +typedef struct ngtcp2_buf_chain ngtcp2_buf_chain; + +struct ngtcp2_buf_chain { + ngtcp2_buf_chain *next; + ngtcp2_buf buf; +}; + +/* + * ngtcp2_buf_chain_new creates new ngtcp2_buf_chain and initializes + * the internal buffer with |len| bytes space. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_buf_chain_new(ngtcp2_buf_chain **pbufchain, size_t len, + const ngtcp2_mem *mem); + +/* + * ngtcp2_buf_chain_del deletes the resource allocated by |bufchain|. + * It also deletes the memory pointed by |bufchain|. + */ +void ngtcp2_buf_chain_del(ngtcp2_buf_chain *bufchain, const ngtcp2_mem *mem); + #endif /* NGTCP2_BUF_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c index f4670805c73261..1ee7d96b04776e 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c @@ -43,11 +43,15 @@ uint64_t ngtcp2_cc_compute_initcwnd(size_t max_udp_payload_size) { ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num, size_t pktlen, ngtcp2_pktns_id pktns_id, - ngtcp2_tstamp ts_sent) { + ngtcp2_tstamp sent_ts, uint64_t lost, + uint64_t tx_in_flight, int is_app_limited) { pkt->pkt_num = pkt_num; pkt->pktlen = pktlen; pkt->pktns_id = pktns_id; - pkt->ts_sent = ts_sent; + pkt->sent_ts = sent_ts; + pkt->lost = lost; + pkt->tx_in_flight = tx_in_flight; + pkt->is_app_limited = is_app_limited; return pkt; } @@ -106,7 +110,7 @@ void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, uint64_t m; (void)ts; - if (in_congestion_recovery(cstat, pkt->ts_sent)) { + if (in_congestion_recovery(cstat, pkt->sent_ts)) { return; } @@ -129,12 +133,12 @@ void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, } void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts_sent, + ngtcp2_tstamp sent_ts, ngtcp2_tstamp ts) { ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb); uint64_t min_cwnd; - if (in_congestion_recovery(cstat, ts_sent)) { + if (in_congestion_recovery(cstat, sent_ts)) { return; } @@ -162,9 +166,10 @@ void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *ccx, } void ngtcp2_cc_reno_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts) { + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb); uint64_t target_cwnd, initcwnd; + (void)ack; (void)ts; /* TODO Use sliding window for min rtt measurement */ @@ -184,8 +189,12 @@ void ngtcp2_cc_reno_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, } } -void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *ccx) { +void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb); + (void)cstat; + (void)ts; + reno_cc_reset(cc); } @@ -198,6 +207,14 @@ static void cubic_cc_reset(ngtcp2_cubic_cc *cc) { cc->epoch_start = UINT64_MAX; cc->k = 0; + cc->prior.cwnd = 0; + cc->prior.ssthresh = 0; + cc->prior.w_last_max = 0; + cc->prior.w_tcp = 0; + cc->prior.origin_point = 0; + cc->prior.epoch_start = UINT64_MAX; + cc->prior.k = 0; + cc->rtt_sample_count = 0; cc->current_round_min_rtt = UINT64_MAX; cc->last_round_min_rtt = UINT64_MAX; @@ -225,6 +242,7 @@ int ngtcp2_cc_cubic_cc_init(ngtcp2_cc *cc, ngtcp2_log *log, cc->ccb = &cubic_cc->ccb; cc->on_pkt_acked = ngtcp2_cc_cubic_cc_on_pkt_acked; cc->congestion_event = ngtcp2_cc_cubic_cc_congestion_event; + cc->on_spurious_congestion = ngtcp2_cc_cubic_cc_on_spurious_congestion; cc->on_persistent_congestion = ngtcp2_cc_cubic_cc_on_persistent_congestion; cc->on_ack_recv = ngtcp2_cc_cubic_cc_on_ack_recv; cc->on_pkt_sent = ngtcp2_cc_cubic_cc_on_pkt_sent; @@ -300,7 +318,7 @@ void ngtcp2_cc_cubic_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, cc->window_end = -1; } - if (in_congestion_recovery(cstat, pkt->ts_sent)) { + if (in_congestion_recovery(cstat, pkt->sent_ts)) { return; } @@ -421,15 +439,25 @@ void ngtcp2_cc_cubic_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts_sent, + ngtcp2_tstamp sent_ts, ngtcp2_tstamp ts) { ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb); uint64_t min_cwnd; - if (in_congestion_recovery(cstat, ts_sent)) { + if (in_congestion_recovery(cstat, sent_ts)) { return; } + if (cc->prior.cwnd < cstat->cwnd) { + cc->prior.cwnd = cstat->cwnd; + cc->prior.ssthresh = cstat->ssthresh; + cc->prior.w_last_max = cc->w_last_max; + cc->prior.w_tcp = cc->w_tcp; + cc->prior.origin_point = cc->origin_point; + cc->prior.epoch_start = cc->epoch_start; + cc->prior.k = cc->k; + } + cstat->congestion_recovery_start_ts = ts; cc->epoch_start = UINT64_MAX; @@ -449,6 +477,40 @@ void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *ccx, cstat->cwnd); } +void ngtcp2_cc_cubic_cc_on_spurious_congestion(ngtcp2_cc *ccx, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb); + (void)ts; + + if (cstat->cwnd >= cc->prior.cwnd) { + return; + } + + cstat->congestion_recovery_start_ts = UINT64_MAX; + + cstat->cwnd = cc->prior.cwnd; + cstat->ssthresh = cc->prior.ssthresh; + cc->w_last_max = cc->prior.w_last_max; + cc->w_tcp = cc->prior.w_tcp; + cc->origin_point = cc->prior.origin_point; + cc->epoch_start = cc->prior.epoch_start; + cc->k = cc->prior.k; + + cc->prior.cwnd = 0; + cc->prior.ssthresh = 0; + cc->prior.w_last_max = 0; + cc->prior.w_tcp = 0; + cc->prior.origin_point = 0; + cc->prior.epoch_start = UINT64_MAX; + cc->prior.k = 0; + + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, + "spurious congestion is detected and congestion state is " + "restored cwnd=%" PRIu64, + cstat->cwnd); +} + void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) { @@ -460,9 +522,11 @@ void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *ccx, } void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb); uint64_t target_cwnd, initcwnd; + (void)ack; (void)ts; /* TODO Use sliding window for min rtt measurement */ @@ -511,8 +575,12 @@ void ngtcp2_cc_cubic_cc_new_rtt_sample(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, ++cc->rtt_sample_count; } -void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *ccx) { +void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb); + (void)cstat; + (void)ts; + cubic_cc_reset(cc); } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h index e1ca7594802994..6d9e0c2459ece4 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h @@ -36,6 +36,273 @@ typedef struct ngtcp2_log ngtcp2_log; +/** + * @struct + * + * :type:`ngtcp2_cc_base` is the base structure of custom congestion + * control algorithm. It must be the first field of custom congestion + * controller. + */ +typedef struct ngtcp2_cc_base { + /** + * :member:`log` is ngtcp2 library internal logger. + */ + ngtcp2_log *log; +} ngtcp2_cc_base; + +/** + * @struct + * + * :type:`ngtcp2_cc_pkt` is a convenient structure to include + * acked/lost/sent packet. + */ +typedef struct ngtcp2_cc_pkt { + /** + * :member:`pkt_num` is the packet number + */ + int64_t pkt_num; + /** + * :member:`pktlen` is the length of packet. + */ + size_t pktlen; + /** + * :member:`pktns_id` is the ID of packet number space which this + * packet belongs to. + */ + ngtcp2_pktns_id pktns_id; + /** + * :member:`sent_ts` is the timestamp when packet is sent. + */ + ngtcp2_tstamp sent_ts; + /** + * :member:`lost` is the number of bytes lost when this packet was + * sent. + */ + uint64_t lost; + /** + * :member:`tx_in_flight` is the bytes in flight when this packet + * was sent. + */ + uint64_t tx_in_flight; + /** + * :member:`is_app_limited` is nonzero if the connection is + * app-limited when this packet was sent. + */ + int is_app_limited; +} ngtcp2_cc_pkt; + +/** + * @struct + * + * :type:`ngtcp2_cc_ack` is a convenient structure which stores + * acknowledged and lost bytes. + */ +typedef struct ngtcp2_cc_ack { + /** + * :member:`prior_bytes_in_flight` is the in-flight bytes before + * processing this ACK. + */ + uint64_t prior_bytes_in_flight; + /** + * :member:`bytes_delivered` is the number of bytes acknowledged. + */ + uint64_t bytes_delivered; + /** + * :member:`bytes_lost` is the number of bytes declared lost. + */ + uint64_t bytes_lost; + /** + * :member:`pkt_delivered` is the cumulative acknowledged bytes when + * the last packet acknowledged by this ACK was sent. + */ + uint64_t pkt_delivered; + /** + * :member:`largest_acked_sent_ts` is the time when the largest + * acknowledged packet was sent. + */ + ngtcp2_tstamp largest_acked_sent_ts; + /** + * :member:`rtt` is the RTT sample. It is UINT64_MAX if no RTT + * sample is available. + */ + ngtcp2_duration rtt; +} ngtcp2_cc_ack; + +typedef struct ngtcp2_cc ngtcp2_cc; + +/** + * @functypedef + * + * :type:`ngtcp2_cc_on_pkt_acked` is a callback function which is + * called with an acknowledged packet. + */ +typedef void (*ngtcp2_cc_on_pkt_acked)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, + ngtcp2_tstamp ts); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_on_pkt_lost` is a callback function which is + * called with a lost packet. + */ +typedef void (*ngtcp2_cc_on_pkt_lost)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, + ngtcp2_tstamp ts); +/** + * @functypedef + * + * :type:`ngtcp2_cc_congestion_event` is a callback function which is + * called when congestion event happens (e.g., when packet is lost). + */ +typedef void (*ngtcp2_cc_congestion_event)(ngtcp2_cc *cc, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp sent_ts, + ngtcp2_tstamp ts); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_on_spurious_congestion` is a callback function + * which is called when a spurious congestion is detected. + */ +typedef void (*ngtcp2_cc_on_spurious_congestion)(ngtcp2_cc *cc, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_on_persistent_congestion` is a callback function + * which is called when persistent congestion is established. + */ +typedef void (*ngtcp2_cc_on_persistent_congestion)(ngtcp2_cc *cc, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_on_ack_recv` is a callback function which is + * called when an acknowledgement is received. + */ +typedef void (*ngtcp2_cc_on_ack_recv)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, + ngtcp2_tstamp ts); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_on_pkt_sent` is a callback function which is + * called when an ack-eliciting packet is sent. + */ +typedef void (*ngtcp2_cc_on_pkt_sent)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_new_rtt_sample` is a callback function which is + * called when new RTT sample is obtained. + */ +typedef void (*ngtcp2_cc_new_rtt_sample)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_reset` is a callback function which is called when + * congestion state must be reset. + */ +typedef void (*ngtcp2_cc_reset)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +/** + * @enum + * + * :type:`ngtcp2_cc_event_type` defines congestion control events. + */ +typedef enum ngtcp2_cc_event_type { + /** + * :enum:`NGTCP2_CC_EVENT_TX_START` occurs when ack-eliciting packet + * is sent and no other ack-eliciting packet is present. + */ + NGTCP2_CC_EVENT_TYPE_TX_START +} ngtcp2_cc_event_type; + +/** + * @functypedef + * + * :type:`ngtcp2_cc_event` is a callback function which is called when + * a specific event happens. + */ +typedef void (*ngtcp2_cc_event)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_cc_event_type event, ngtcp2_tstamp ts); + +/** + * @struct + * + * :type:`ngtcp2_cc` is congestion control algorithm interface to + * allow custom implementation. + */ +typedef struct ngtcp2_cc { + /** + * :member:`ccb` is a pointer to :type:`ngtcp2_cc_base` which + * usually contains a state. + */ + ngtcp2_cc_base *ccb; + /** + * :member:`on_pkt_acked` is a callback function which is called + * when a packet is acknowledged. + */ + ngtcp2_cc_on_pkt_acked on_pkt_acked; + /** + * :member:`on_pkt_lost` is a callback function which is called when + * a packet is lost. + */ + ngtcp2_cc_on_pkt_lost on_pkt_lost; + /** + * :member:`congestion_event` is a callback function which is called + * when congestion event happens (.e.g, packet is lost). + */ + ngtcp2_cc_congestion_event congestion_event; + /** + * :member:`on_spurious_congestion` is a callback function which is + * called when a spurious congestion is detected. + */ + ngtcp2_cc_on_spurious_congestion on_spurious_congestion; + /** + * :member:`on_persistent_congestion` is a callback function which + * is called when persistent congestion is established. + */ + ngtcp2_cc_on_persistent_congestion on_persistent_congestion; + /** + * :member:`on_ack_recv` is a callback function which is called when + * an acknowledgement is received. + */ + ngtcp2_cc_on_ack_recv on_ack_recv; + /** + * :member:`on_pkt_sent` is a callback function which is called when + * ack-eliciting packet is sent. + */ + ngtcp2_cc_on_pkt_sent on_pkt_sent; + /** + * :member:`new_rtt_sample` is a callback function which is called + * when new RTT sample is obtained. + */ + ngtcp2_cc_new_rtt_sample new_rtt_sample; + /** + * :member:`reset` is a callback function which is called when + * congestion control state must be reset. + */ + ngtcp2_cc_reset reset; + /** + * :member:`event` is a callback function which is called when a + * specific event happens. + */ + ngtcp2_cc_event event; +} ngtcp2_cc; + /* * ngtcp2_cc_compute_initcwnd computes initial cwnd. */ @@ -43,7 +310,8 @@ uint64_t ngtcp2_cc_compute_initcwnd(size_t max_packet_size); ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num, size_t pktlen, ngtcp2_pktns_id pktns_id, - ngtcp2_tstamp ts_sent); + ngtcp2_tstamp sent_ts, uint64_t lost, + uint64_t tx_in_flight, int is_app_limited); /* ngtcp2_reno_cc is the RENO congestion controller. */ typedef struct ngtcp2_reno_cc { @@ -66,7 +334,7 @@ void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts); void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts_sent, + ngtcp2_tstamp sent_ts, ngtcp2_tstamp ts); void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc, @@ -74,9 +342,10 @@ void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc, ngtcp2_tstamp ts); void ngtcp2_cc_reno_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts); + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); -void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *cc); +void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); /* ngtcp2_cubic_cc is CUBIC congestion controller. */ typedef struct ngtcp2_cubic_cc { @@ -88,6 +357,18 @@ typedef struct ngtcp2_cubic_cc { uint64_t origin_point; ngtcp2_tstamp epoch_start; uint64_t k; + /* prior stores the congestion state when a congestion event occurs + in order to restore the state when it turns out that the event is + spurious. */ + struct { + uint64_t cwnd; + uint64_t ssthresh; + uint64_t w_last_max; + uint64_t w_tcp; + uint64_t origin_point; + ngtcp2_tstamp epoch_start; + uint64_t k; + } prior; /* HyStart++ variables */ size_t rtt_sample_count; uint64_t current_round_min_rtt; @@ -111,15 +392,19 @@ void ngtcp2_cc_cubic_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts); void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts_sent, + ngtcp2_tstamp sent_ts, ngtcp2_tstamp ts); +void ngtcp2_cc_cubic_cc_on_spurious_congestion(ngtcp2_cc *ccx, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts); void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts); + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); void ngtcp2_cc_cubic_cc_on_pkt_sent(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, const ngtcp2_cc_pkt *pkt); @@ -127,7 +412,8 @@ void ngtcp2_cc_cubic_cc_on_pkt_sent(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, void ngtcp2_cc_cubic_cc_new_rtt_sample(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts); -void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *cc); +void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); void ngtcp2_cc_cubic_cc_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, ngtcp2_cc_event_type event, ngtcp2_tstamp ts); diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c index cdf31f8f624768..f3b92b569ec928 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c @@ -30,7 +30,7 @@ #include "ngtcp2_path.h" #include "ngtcp2_str.h" -void ngtcp2_cid_zero(ngtcp2_cid *cid) { cid->datalen = 0; } +void ngtcp2_cid_zero(ngtcp2_cid *cid) { memset(cid, 0, sizeof(*cid)); } void ngtcp2_cid_init(ngtcp2_cid *cid, const uint8_t *data, size_t datalen) { assert(datalen <= NGTCP2_MAX_CIDLEN); @@ -56,23 +56,17 @@ int ngtcp2_cid_less(const ngtcp2_cid *lhs, const ngtcp2_cid *rhs) { int ngtcp2_cid_empty(const ngtcp2_cid *cid) { return cid->datalen == 0; } -void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid, - const uint8_t *token) { +void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid) { scid->pe.index = NGTCP2_PQ_BAD_INDEX; scid->seq = seq; scid->cid = *cid; - scid->ts_retired = UINT64_MAX; + scid->retired_ts = UINT64_MAX; scid->flags = NGTCP2_SCID_FLAG_NONE; - if (token) { - memcpy(scid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN); - } else { - memset(scid->token, 0, NGTCP2_STATELESS_RESET_TOKENLEN); - } } void ngtcp2_scid_copy(ngtcp2_scid *dest, const ngtcp2_scid *src) { - ngtcp2_scid_init(dest, src->seq, &src->cid, src->token); - dest->ts_retired = src->ts_retired; + ngtcp2_scid_init(dest, src->seq, &src->cid); + dest->retired_ts = src->retired_ts; dest->flags = src->flags; } @@ -82,35 +76,58 @@ void ngtcp2_dcid_init(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid, dcid->cid = *cid; if (token) { memcpy(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN); + dcid->flags = NGTCP2_DCID_FLAG_TOKEN_PRESENT; } else { - memset(dcid->token, 0, NGTCP2_STATELESS_RESET_TOKENLEN); + dcid->flags = NGTCP2_DCID_FLAG_NONE; } ngtcp2_path_storage_zero(&dcid->ps); - dcid->ts_retired = UINT64_MAX; - dcid->flags = NGTCP2_DCID_FLAG_NONE; + dcid->retired_ts = UINT64_MAX; + dcid->bound_ts = UINT64_MAX; dcid->bytes_sent = 0; dcid->bytes_recv = 0; + dcid->max_udp_payload_size = NGTCP2_MAX_UDP_PAYLOAD_SIZE; +} + +void ngtcp2_dcid_set_token(ngtcp2_dcid *dcid, const uint8_t *token) { + assert(token); + + dcid->flags |= NGTCP2_DCID_FLAG_TOKEN_PRESENT; + memcpy(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN); +} + +void ngtcp2_dcid_set_path(ngtcp2_dcid *dcid, const ngtcp2_path *path) { + ngtcp2_path_copy(&dcid->ps.path, path); } void ngtcp2_dcid_copy(ngtcp2_dcid *dest, const ngtcp2_dcid *src) { - ngtcp2_dcid_init(dest, src->seq, &src->cid, src->token); + ngtcp2_dcid_init(dest, src->seq, &src->cid, + (src->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) ? src->token + : NULL); ngtcp2_path_copy(&dest->ps.path, &src->ps.path); - dest->ts_retired = src->ts_retired; + dest->retired_ts = src->retired_ts; + dest->bound_ts = src->bound_ts; dest->flags = src->flags; dest->bytes_sent = src->bytes_sent; dest->bytes_recv = src->bytes_recv; + dest->max_udp_payload_size = src->max_udp_payload_size; } void ngtcp2_dcid_copy_cid_token(ngtcp2_dcid *dest, const ngtcp2_dcid *src) { dest->seq = src->seq; dest->cid = src->cid; - memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN); + if (src->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) { + dest->flags |= NGTCP2_DCID_FLAG_TOKEN_PRESENT; + memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN); + } else if (dest->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) { + dest->flags &= (uint8_t)~NGTCP2_DCID_FLAG_TOKEN_PRESENT; + } } int ngtcp2_dcid_verify_uniqueness(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid, const uint8_t *token) { if (dcid->seq == seq) { return ngtcp2_cid_eq(&dcid->cid, cid) && + (dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) && memcmp(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN) == 0 ? 0 @@ -119,3 +136,12 @@ int ngtcp2_dcid_verify_uniqueness(ngtcp2_dcid *dcid, uint64_t seq, return !ngtcp2_cid_eq(&dcid->cid, cid) ? 0 : NGTCP2_ERR_PROTO; } + +int ngtcp2_dcid_verify_stateless_reset_token(const ngtcp2_dcid *dcid, + const uint8_t *token) { + return (dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) && + ngtcp2_cmemeq(dcid->token, token, + NGTCP2_STATELESS_RESET_TOKENLEN) + ? 0 + : NGTCP2_ERR_INVALID_ARGUMENT; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h index a356b432d4f548..0b37441178c72a 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h @@ -34,14 +34,14 @@ #include "ngtcp2_pq.h" #include "ngtcp2_path.h" -/* NGTCP2_SCID_FLAG_NONE indicats that no flag is set. */ -#define NGTCP2_SCID_FLAG_NONE 0x00 +/* NGTCP2_SCID_FLAG_NONE indicates that no flag is set. */ +#define NGTCP2_SCID_FLAG_NONE 0x00u /* NGTCP2_SCID_FLAG_USED indicates that a local endpoint observed that a remote endpoint uses a particular Connection ID. */ -#define NGTCP2_SCID_FLAG_USED 0x01 +#define NGTCP2_SCID_FLAG_USED 0x01u /* NGTCP2_SCID_FLAG_RETIRED indicates that a particular Connection ID is retired. */ -#define NGTCP2_SCID_FLAG_RETIRED 0x02 +#define NGTCP2_SCID_FLAG_RETIRED 0x02u typedef struct ngtcp2_scid { ngtcp2_pq_entry pe; @@ -49,22 +49,21 @@ typedef struct ngtcp2_scid { uint64_t seq; /* cid is a connection ID */ ngtcp2_cid cid; - /* ts_retired is the timestamp when peer tells that this CID is + /* retired_ts is the timestamp when peer tells that this CID is retired. */ - ngtcp2_tstamp ts_retired; + ngtcp2_tstamp retired_ts; /* flags is the bitwise OR of zero or more of NGTCP2_SCID_FLAG_*. */ uint8_t flags; - /* token is a stateless reset token associated to this CID. - Actually, the stateless reset token is tied to the connection, - not to the particular connection ID. */ - uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN]; } ngtcp2_scid; /* NGTCP2_DCID_FLAG_NONE indicates that no flag is set. */ -#define NGTCP2_DCID_FLAG_NONE 0x00 +#define NGTCP2_DCID_FLAG_NONE 0x00u /* NGTCP2_DCID_FLAG_PATH_VALIDATED indicates that an associated path has been validated. */ -#define NGTCP2_DCID_FLAG_PATH_VALIDATED 0x01 +#define NGTCP2_DCID_FLAG_PATH_VALIDATED 0x01u +/* NGTCP2_DCID_FLAG_TOKEN_PRESENT indicates that a stateless reset + token is set in token field. */ +#define NGTCP2_DCID_FLAG_TOKEN_PRESENT 0x02u typedef struct ngtcp2_dcid { /* seq is the sequence number associated to the CID. */ @@ -74,14 +73,21 @@ typedef struct ngtcp2_dcid { /* path is a path which cid is bound to. The addresses are zero length if cid has not been bound to a particular path yet. */ ngtcp2_path_storage ps; - /* ts_retired is the timestamp when peer tells that this CID is + /* retired_ts is the timestamp when peer tells that this CID is retired. */ - ngtcp2_tstamp ts_retired; + ngtcp2_tstamp retired_ts; + /* bound_ts is the timestamp when this connection ID is bound to a + particular path. It is only assigned when a connection ID is + used just for sending PATH_RESPONSE and is not zero-length. */ + ngtcp2_tstamp bound_ts; /* bytes_sent is the number of bytes sent to an associated path. */ uint64_t bytes_sent; /* bytes_recv is the number of bytes received from an associated path. */ uint64_t bytes_recv; + /* max_udp_payload_size is the maximum size of UDP payload that is + allowed to send to this path. */ + size_t max_udp_payload_size; /* flags is bitwise OR of zero or more of NGTCP2_DCID_FLAG_*. */ uint8_t flags; /* token is a stateless reset token associated to this CID. @@ -93,12 +99,6 @@ typedef struct ngtcp2_dcid { /* ngtcp2_cid_zero makes |cid| zero-length. */ void ngtcp2_cid_zero(ngtcp2_cid *cid); -/* - * ngtcp2_cid_eq returns nonzero if |cid| and |other| share the same - * connection ID. - */ -int ngtcp2_cid_eq(const ngtcp2_cid *cid, const ngtcp2_cid *other); - /* * ngtcp2_cid_less returns nonzero if |lhs| is lexicographical smaller * than |rhs|. @@ -112,12 +112,9 @@ int ngtcp2_cid_less(const ngtcp2_cid *lhs, const ngtcp2_cid *rhs); int ngtcp2_cid_empty(const ngtcp2_cid *cid); /* - * ngtcp2_scid_init initializes |scid| with the given parameters. If - * |token| is NULL, the function fills scid->token it with 0. |token| - * must be NGTCP2_STATELESS_RESET_TOKENLEN bytes long. + * ngtcp2_scid_init initializes |scid| with the given parameters. */ -void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid, - const uint8_t *token); +void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid); /* * ngtcp2_scid_copy copies |src| into |dest|. @@ -132,6 +129,19 @@ void ngtcp2_scid_copy(ngtcp2_scid *dest, const ngtcp2_scid *src); void ngtcp2_dcid_init(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid, const uint8_t *token); +/* + * ngtcp2_dcid_set_token sets |token| to |dcid|. |token| must not be + * NULL and must be NGTCP2_STATELESS_RESET_TOKENLEN bytes long. + */ +void ngtcp2_dcid_set_token(ngtcp2_dcid *dcid, const uint8_t *token); + +/* + * ngtcp2_dcid_set_path sets |path| to |dcid|. It sets + * max_udp_payload_size to the minimum UDP payload size supported + * by the IP protocol version. + */ +void ngtcp2_dcid_set_path(ngtcp2_dcid *dcid, const ngtcp2_path *path); + /* * ngtcp2_dcid_copy copies |src| into |dest|. */ @@ -150,4 +160,16 @@ void ngtcp2_dcid_copy_cid_token(ngtcp2_dcid *dest, const ngtcp2_dcid *src); int ngtcp2_dcid_verify_uniqueness(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid, const uint8_t *token); +/* + * ngtcp2_dcid_verify_stateless_reset_token verifies stateless reset + * token |token| against the one included in |dcid|. This function + * returns 0 if the verification succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * Tokens do not match; or |dcid| does not contain a token. + */ +int ngtcp2_dcid_verify_stateless_reset_token(const ngtcp2_dcid *dcid, + const uint8_t *token); + #endif /* NGTCP2_CID_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c index c8b1d15db5a6f7..a3135680ca160f 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c @@ -26,7 +26,6 @@ #include #include -#include #include "ngtcp2_macro.h" #include "ngtcp2_log.h" @@ -43,6 +42,9 @@ /* NGTCP2_FLOW_WINDOW_SCALING_FACTOR is the growth factor of flow control window. */ #define NGTCP2_FLOW_WINDOW_SCALING_FACTOR 2 +/* NGTCP2_MIN_COALESCED_PAYLOADLEN is the minimum length of QUIC + packet payload that should be coalesced to a long packet. */ +#define NGTCP2_MIN_COALESCED_PAYLOADLEN 128 /* * conn_local_stream returns nonzero if |stream_id| indicates that it @@ -58,6 +60,15 @@ static int conn_local_stream(ngtcp2_conn *conn, int64_t stream_id) { */ static int bidi_stream(int64_t stream_id) { return (stream_id & 0x2) == 0; } +/* + * conn_is_handshake_completed returns nonzero if QUIC handshake has + * completed. + */ +static int conn_is_handshake_completed(ngtcp2_conn *conn) { + return (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) && + conn->pktns.crypto.rx.ckm && conn->pktns.crypto.tx.ckm; +} + static int conn_call_recv_client_initial(ngtcp2_conn *conn, const ngtcp2_cid *dcid) { int rv; @@ -123,6 +134,8 @@ static int conn_call_recv_crypto_data(ngtcp2_conn *conn, case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM: case NGTCP2_ERR_TRANSPORT_PARAM: case NGTCP2_ERR_PROTO: + case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE: + case NGTCP2_ERR_NOMEM: case NGTCP2_ERR_CALLBACK_FAILURE: return rv; default: @@ -145,16 +158,21 @@ static int conn_call_stream_open(ngtcp2_conn *conn, ngtcp2_strm *strm) { return 0; } -static int conn_call_stream_close(ngtcp2_conn *conn, ngtcp2_strm *strm, - uint64_t app_error_code) { +static int conn_call_stream_close(ngtcp2_conn *conn, ngtcp2_strm *strm) { int rv; + uint32_t flags = NGTCP2_STREAM_CLOSE_FLAG_NONE; if (!conn->callbacks.stream_close) { return 0; } - rv = conn->callbacks.stream_close(conn, strm->stream_id, app_error_code, - conn->user_data, strm->stream_user_data); + if (strm->flags & NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET) { + flags |= NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET; + } + + rv = conn->callbacks.stream_close(conn, flags, strm->stream_id, + strm->app_error_code, conn->user_data, + strm->stream_user_data); if (rv != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -245,15 +263,21 @@ static int conn_call_remove_connection_id(ngtcp2_conn *conn, return 0; } -static int conn_call_path_validation(ngtcp2_conn *conn, const ngtcp2_path *path, +static int conn_call_path_validation(ngtcp2_conn *conn, const ngtcp2_pv *pv, ngtcp2_path_validation_result res) { int rv; + uint32_t flags = NGTCP2_PATH_VALIDATION_FLAG_NONE; if (!conn->callbacks.path_validation) { return 0; } - rv = conn->callbacks.path_validation(conn, path, res, conn->user_data); + if (pv->flags & NGTCP2_PV_FLAG_PREFERRED_ADDR) { + flags |= NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR; + } + + rv = conn->callbacks.path_validation(conn, flags, &pv->dcid.ps.path, res, + conn->user_data); if (rv != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -262,17 +286,18 @@ static int conn_call_path_validation(ngtcp2_conn *conn, const ngtcp2_path *path, } static int conn_call_select_preferred_addr(ngtcp2_conn *conn, - ngtcp2_addr *dest) { + ngtcp2_path *dest) { int rv; if (!conn->callbacks.select_preferred_addr) { return 0; } - assert(conn->remote.transport_params.preferred_address_present); + assert(conn->remote.transport_params); + assert(conn->remote.transport_params->preferred_address_present); rv = conn->callbacks.select_preferred_addr( - conn, dest, &conn->remote.transport_params.preferred_address, + conn, dest, &conn->remote.transport_params->preferred_address, conn->user_data); if (rv != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; @@ -345,8 +370,7 @@ static int conn_call_dcid_status(ngtcp2_conn *conn, rv = conn->callbacks.dcid_status( conn, (int)type, dcid->seq, &dcid->cid, - ngtcp2_check_invalid_stateless_reset_token(dcid->token) ? NULL - : dcid->token, + (dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) ? dcid->token : NULL, conn->user_data); if (rv != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; @@ -366,6 +390,24 @@ static int conn_call_deactivate_dcid(ngtcp2_conn *conn, conn, NGTCP2_CONNECTION_ID_STATUS_TYPE_DEACTIVATE, dcid); } +static int conn_call_stream_stop_sending(ngtcp2_conn *conn, int64_t stream_id, + uint64_t app_error_code, + void *stream_user_data) { + int rv; + + if (!conn->callbacks.stream_stop_sending) { + return 0; + } + + rv = conn->callbacks.stream_stop_sending(conn, stream_id, app_error_code, + conn->user_data, stream_user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + static void conn_call_delete_crypto_aead_ctx(ngtcp2_conn *conn, ngtcp2_crypto_aead_ctx *aead_ctx) { if (!aead_ctx->native_handle) { @@ -389,6 +431,210 @@ conn_call_delete_crypto_cipher_ctx(ngtcp2_conn *conn, conn->callbacks.delete_crypto_cipher_ctx(conn, cipher_ctx, conn->user_data); } +static int conn_call_client_initial(ngtcp2_conn *conn) { + int rv; + + assert(conn->callbacks.client_initial); + + rv = conn->callbacks.client_initial(conn, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_get_path_challenge_data(ngtcp2_conn *conn, uint8_t *data) { + int rv; + + assert(conn->callbacks.get_path_challenge_data); + + rv = conn->callbacks.get_path_challenge_data(conn, data, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_recv_version_negotiation(ngtcp2_conn *conn, + const ngtcp2_pkt_hd *hd, + const uint32_t *sv, size_t nsv) { + int rv; + + if (!conn->callbacks.recv_version_negotiation) { + return 0; + } + + rv = conn->callbacks.recv_version_negotiation(conn, hd, sv, nsv, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_recv_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd) { + int rv; + + assert(conn->callbacks.recv_retry); + + rv = conn->callbacks.recv_retry(conn, hd, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int +conn_call_recv_stateless_reset(ngtcp2_conn *conn, + const ngtcp2_pkt_stateless_reset *sr) { + int rv; + + if (!conn->callbacks.recv_stateless_reset) { + return 0; + } + + rv = conn->callbacks.recv_stateless_reset(conn, sr, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_recv_new_token(ngtcp2_conn *conn, + const ngtcp2_vec *token) { + int rv; + + if (!conn->callbacks.recv_new_token) { + return 0; + } + + rv = conn->callbacks.recv_new_token(conn, token, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_handshake_confirmed(ngtcp2_conn *conn) { + int rv; + + if (!conn->callbacks.handshake_confirmed) { + return 0; + } + + rv = conn->callbacks.handshake_confirmed(conn, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_recv_datagram(ngtcp2_conn *conn, + const ngtcp2_datagram *fr) { + const uint8_t *data; + size_t datalen; + int rv; + uint32_t flags = NGTCP2_DATAGRAM_FLAG_NONE; + + if (!conn->callbacks.recv_datagram) { + return 0; + } + + if (fr->datacnt) { + assert(fr->datacnt == 1); + + data = fr->data->base; + datalen = fr->data->len; + } else { + data = NULL; + datalen = 0; + } + + if (!conn_is_handshake_completed(conn)) { + flags |= NGTCP2_DATAGRAM_FLAG_EARLY; + } + + rv = conn->callbacks.recv_datagram(conn, flags, data, datalen, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int +conn_call_update_key(ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, + ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv, + ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv, + const uint8_t *current_rx_secret, + const uint8_t *current_tx_secret, size_t secretlen) { + int rv; + + assert(conn->callbacks.update_key); + + rv = conn->callbacks.update_key( + conn, rx_secret, tx_secret, rx_aead_ctx, rx_iv, tx_aead_ctx, tx_iv, + current_rx_secret, current_tx_secret, secretlen, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_version_negotiation(ngtcp2_conn *conn, uint32_t version, + const ngtcp2_cid *dcid) { + int rv; + + assert(conn->callbacks.version_negotiation); + + rv = + conn->callbacks.version_negotiation(conn, version, dcid, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_recv_rx_key(ngtcp2_conn *conn, ngtcp2_crypto_level level) { + int rv; + + if (!conn->callbacks.recv_rx_key) { + return 0; + } + + rv = conn->callbacks.recv_rx_key(conn, level, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_recv_tx_key(ngtcp2_conn *conn, ngtcp2_crypto_level level) { + int rv; + + if (!conn->callbacks.recv_tx_key) { + return 0; + } + + rv = conn->callbacks.recv_tx_key(conn, level, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + static int crypto_offset_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { return *(int64_t *)lhs < *(int64_t *)rhs; @@ -396,15 +642,13 @@ static int crypto_offset_less(const ngtcp2_ksl_key *lhs, static int pktns_init(ngtcp2_pktns *pktns, ngtcp2_pktns_id pktns_id, ngtcp2_rst *rst, ngtcp2_cc *cc, ngtcp2_log *log, - ngtcp2_qlog *qlog, const ngtcp2_mem *mem) { + ngtcp2_qlog *qlog, ngtcp2_objalloc *rtb_entry_objalloc, + ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) { int rv; memset(pktns, 0, sizeof(*pktns)); - rv = ngtcp2_gaptr_init(&pktns->rx.pngap, mem); - if (rv != 0) { - return rv; - } + ngtcp2_gaptr_init(&pktns->rx.pngap, mem); pktns->tx.last_pkt_num = -1; pktns->rx.max_pkt_num = -1; @@ -415,27 +659,17 @@ static int pktns_init(ngtcp2_pktns *pktns, ngtcp2_pktns_id pktns_id, goto fail_acktr_init; } - rv = ngtcp2_strm_init(&pktns->crypto.strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, - NULL, mem); - if (rv != 0) { - goto fail_crypto_init; - } + ngtcp2_strm_init(&pktns->crypto.strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, + NULL, mem); - rv = ngtcp2_ksl_init(&pktns->crypto.tx.frq, crypto_offset_less, - sizeof(uint64_t), mem); - if (rv != 0) { - goto fail_tx_frq_init; - } + ngtcp2_ksl_init(&pktns->crypto.tx.frq, crypto_offset_less, sizeof(uint64_t), + mem); ngtcp2_rtb_init(&pktns->rtb, pktns_id, &pktns->crypto.strm, rst, cc, log, - qlog, mem); + qlog, rtb_entry_objalloc, frc_objalloc, mem); return 0; -fail_tx_frq_init: - ngtcp2_strm_free(&pktns->crypto.strm); -fail_crypto_init: - ngtcp2_acktr_free(&pktns->acktr); fail_acktr_init: ngtcp2_gaptr_free(&pktns->rx.pngap); @@ -444,7 +678,8 @@ static int pktns_init(ngtcp2_pktns *pktns, ngtcp2_pktns_id pktns_id, static int pktns_new(ngtcp2_pktns **ppktns, ngtcp2_pktns_id pktns_id, ngtcp2_rst *rst, ngtcp2_cc *cc, ngtcp2_log *log, - ngtcp2_qlog *qlog, const ngtcp2_mem *mem) { + ngtcp2_qlog *qlog, ngtcp2_objalloc *rtb_entry_objalloc, + ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) { int rv; *ppktns = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pktns)); @@ -452,7 +687,8 @@ static int pktns_new(ngtcp2_pktns **ppktns, ngtcp2_pktns_id pktns_id, return NGTCP2_ERR_NOMEM; } - rv = pktns_init(*ppktns, pktns_id, rst, cc, log, qlog, mem); + rv = pktns_init(*ppktns, pktns_id, rst, cc, log, qlog, rtb_entry_objalloc, + frc_objalloc, mem); if (rv != 0) { ngtcp2_mem_free(mem, *ppktns); } @@ -481,13 +717,27 @@ static void delete_buffed_pkts(ngtcp2_pkt_chain *pc, const ngtcp2_mem *mem) { } } +static void delete_buf_chain(ngtcp2_buf_chain *bufchain, + const ngtcp2_mem *mem) { + ngtcp2_buf_chain *next; + + for (; bufchain;) { + next = bufchain->next; + ngtcp2_buf_chain_del(bufchain, mem); + bufchain = next; + } +} + static void pktns_free(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) { ngtcp2_frame_chain *frc; ngtcp2_ksl_it it; + delete_buf_chain(pktns->crypto.tx.data, mem); + delete_buffed_pkts(pktns->rx.buffed_pkts, mem); - ngtcp2_frame_chain_list_del(pktns->tx.frq, mem); + ngtcp2_frame_chain_list_objalloc_del(pktns->tx.frq, pktns->rtb.frc_objalloc, + mem); ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, mem); ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, mem); @@ -495,7 +745,7 @@ static void pktns_free(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) { for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { frc = ngtcp2_ksl_it_get(&it); - ngtcp2_frame_chain_del(frc, mem); + ngtcp2_frame_chain_objalloc_del(frc, pktns->rtb.frc_objalloc, mem); } ngtcp2_ksl_free(&pktns->crypto.tx.frq); @@ -524,6 +774,12 @@ static void cc_del(ngtcp2_cc *cc, ngtcp2_cc_algo cc_algo, case NGTCP2_CC_ALGO_CUBIC: ngtcp2_cc_cubic_cc_free(cc, mem); break; + case NGTCP2_CC_ALGO_BBR: + ngtcp2_cc_bbr_cc_free(cc, mem); + break; + case NGTCP2_CC_ALGO_BBR2: + ngtcp2_cc_bbr2_cc_free(cc, mem); + break; default: break; } @@ -533,12 +789,12 @@ static int cid_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { return ngtcp2_cid_less(lhs, rhs); } -static int ts_retired_less(const ngtcp2_pq_entry *lhs, +static int retired_ts_less(const ngtcp2_pq_entry *lhs, const ngtcp2_pq_entry *rhs) { const ngtcp2_scid *a = ngtcp2_struct_of(lhs, ngtcp2_scid, pe); const ngtcp2_scid *b = ngtcp2_struct_of(rhs, ngtcp2_scid, pe); - return a->ts_retired < b->ts_retired; + return a->retired_ts < b->retired_ts; } /* @@ -559,6 +815,8 @@ static void conn_reset_conn_stat_cc(ngtcp2_conn *conn, cstat->congestion_recovery_start_ts = UINT64_MAX; cstat->bytes_in_flight = 0; cstat->delivery_rate_sec = 0; + cstat->pacing_rate = 0.0; + cstat->send_quantum = SIZE_MAX; } /* @@ -566,7 +824,7 @@ static void conn_reset_conn_stat_cc(ngtcp2_conn *conn, * function */ static void reset_conn_stat_recovery(ngtcp2_conn_stat *cstat) { - // Initializes them with UINT64_MAX. + /* Initializes them with UINT64_MAX. */ memset(cstat->loss_time, 0xff, sizeof(cstat->loss_time)); memset(cstat->last_tx_pkt_ts, 0xff, sizeof(cstat->last_tx_pkt_ts)); } @@ -605,10 +863,14 @@ static ngtcp2_duration compute_pto(ngtcp2_duration smoothed_rtt, static ngtcp2_duration conn_compute_initial_pto(ngtcp2_conn *conn, ngtcp2_pktns *pktns) { ngtcp2_duration initial_rtt = conn->local.settings.initial_rtt; - ngtcp2_duration max_ack_delay = - pktns->rtb.pktns_id == NGTCP2_PKTNS_ID_APPLICATION - ? conn->remote.transport_params.max_ack_delay - : 0; + ngtcp2_duration max_ack_delay; + + if (pktns->rtb.pktns_id == NGTCP2_PKTNS_ID_APPLICATION && + conn->remote.transport_params) { + max_ack_delay = conn->remote.transport_params->max_ack_delay; + } else { + max_ack_delay = 0; + } return compute_pto(initial_rtt, initial_rtt / 2, max_ack_delay); } @@ -618,15 +880,24 @@ static ngtcp2_duration conn_compute_initial_pto(ngtcp2_conn *conn, static ngtcp2_duration conn_compute_pto(ngtcp2_conn *conn, ngtcp2_pktns *pktns) { ngtcp2_conn_stat *cstat = &conn->cstat; - ngtcp2_duration max_ack_delay = - pktns->rtb.pktns_id == NGTCP2_PKTNS_ID_APPLICATION - ? conn->remote.transport_params.max_ack_delay - : 0; + ngtcp2_duration max_ack_delay; + + if (pktns->rtb.pktns_id == NGTCP2_PKTNS_ID_APPLICATION && + conn->remote.transport_params) { + max_ack_delay = conn->remote.transport_params->max_ack_delay; + } else { + max_ack_delay = 0; + } return compute_pto(cstat->smoothed_rtt, cstat->rttvar, max_ack_delay); } +ngtcp2_duration ngtcp2_conn_compute_pto(ngtcp2_conn *conn, + ngtcp2_pktns *pktns) { + return conn_compute_pto(conn, pktns); +} + static void conn_handle_tx_ecn(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, - uint8_t *prtb_entry_flags, ngtcp2_pktns *pktns, + uint16_t *prtb_entry_flags, ngtcp2_pktns *pktns, const ngtcp2_pkt_hd *hd, ngtcp2_tstamp ts) { assert(pi); @@ -718,23 +989,103 @@ static void conn_reset_ecn_validation_state(ngtcp2_conn *conn) { pktns->tx.ecn.validation_pkt_lost = 0; } +/* server_default_other_versions is the default other_versions field + sent by server. */ +static uint8_t server_default_other_versions[] = {0, 0, 0, 1}; + +/* + * other_versions_new allocates new buffer, and writes |versions| of + * length |versionslen| in network byte order, suitable for sending in + * other_versions field of version_information QUIC transport + * parameter. The pointer to the allocated buffer is assigned to + * |*pbuf|. + * + * This function returns 0 if it succeeds, or one of the negative + * error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int other_versions_new(uint8_t **pbuf, const uint32_t *versions, + size_t versionslen, const ngtcp2_mem *mem) { + size_t i; + uint8_t *buf = ngtcp2_mem_malloc(mem, sizeof(uint32_t) * versionslen); + + if (buf == NULL) { + return NGTCP2_ERR_NOMEM; + } + + *pbuf = buf; + + for (i = 0; i < versionslen; ++i) { + buf = ngtcp2_put_uint32be(buf, versions[i]); + } + + return 0; +} + +static void +conn_set_local_transport_params(ngtcp2_conn *conn, + const ngtcp2_transport_params *params) { + ngtcp2_transport_params *p = &conn->local.transport_params; + uint32_t chosen_version = p->version_info.chosen_version; + + *p = *params; + + /* grease_quic_bit is always enabled. */ + p->grease_quic_bit = 1; + + if (conn->server) { + p->version_info.chosen_version = chosen_version; + } else { + p->version_info.chosen_version = conn->client_chosen_version; + } + p->version_info.other_versions = conn->vneg.other_versions; + p->version_info.other_versionslen = conn->vneg.other_versionslen; + p->version_info_present = 1; +} + static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, const ngtcp2_path *path, - uint32_t version, const ngtcp2_callbacks *callbacks, + uint32_t client_chosen_version, int callbacks_version, + const ngtcp2_callbacks *callbacks, int settings_version, const ngtcp2_settings *settings, + int transport_params_version, const ngtcp2_transport_params *params, const ngtcp2_mem *mem, void *user_data, int server) { int rv; ngtcp2_scid *scident; uint8_t *buf; + uint8_t fixed_bit_byte; + size_t i; + uint32_t *preferred_versions; + (void)callbacks_version; + (void)settings_version; + (void)transport_params_version; assert(settings->max_window <= NGTCP2_MAX_VARINT); assert(settings->max_stream_window <= NGTCP2_MAX_VARINT); + assert(settings->max_udp_payload_size); + assert(settings->max_udp_payload_size <= NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE); assert(params->active_connection_id_limit <= NGTCP2_MAX_DCID_POOL_SIZE); assert(params->initial_max_data <= NGTCP2_MAX_VARINT); assert(params->initial_max_stream_data_bidi_local <= NGTCP2_MAX_VARINT); assert(params->initial_max_stream_data_bidi_remote <= NGTCP2_MAX_VARINT); assert(params->initial_max_stream_data_uni <= NGTCP2_MAX_VARINT); + assert(server || callbacks->client_initial); + assert(!server || callbacks->recv_client_initial); + assert(callbacks->recv_crypto_data); + assert(callbacks->encrypt); + assert(callbacks->decrypt); + assert(callbacks->hp_mask); + assert(server || callbacks->recv_retry); + assert(callbacks->rand); + assert(callbacks->get_new_connection_id); + assert(callbacks->update_key); + assert(callbacks->delete_crypto_aead_ctx); + assert(callbacks->delete_crypto_cipher_ctx); + assert(callbacks->get_path_challenge_data); + assert(!server || !ngtcp2_is_reserved_version(client_chosen_version)); if (mem == NULL) { mem = ngtcp2_mem_default(); @@ -746,60 +1097,31 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, goto fail_conn; } - rv = ngtcp2_ringbuf_init(&(*pconn)->dcid.bound, - NGTCP2_MAX_BOUND_DCID_POOL_SIZE, sizeof(ngtcp2_dcid), - mem); - if (rv != 0) { - goto fail_dcid_bound_init; - } + ngtcp2_objalloc_frame_chain_init(&(*pconn)->frc_objalloc, 64, mem); + ngtcp2_objalloc_rtb_entry_init(&(*pconn)->rtb_entry_objalloc, 64, mem); + ngtcp2_objalloc_strm_init(&(*pconn)->strm_objalloc, 64, mem); - rv = ngtcp2_ringbuf_init(&(*pconn)->dcid.unused, NGTCP2_MAX_DCID_POOL_SIZE, - sizeof(ngtcp2_dcid), mem); - if (rv != 0) { - goto fail_dcid_unused_init; - } + ngtcp2_static_ringbuf_dcid_bound_init(&(*pconn)->dcid.bound); - rv = - ngtcp2_ringbuf_init(&(*pconn)->dcid.retired, NGTCP2_MAX_DCID_RETIRED_SIZE, - sizeof(ngtcp2_dcid), mem); - if (rv != 0) { - goto fail_dcid_retired_init; - } + ngtcp2_static_ringbuf_dcid_unused_init(&(*pconn)->dcid.unused); - rv = ngtcp2_gaptr_init(&(*pconn)->dcid.seqgap, mem); - if (rv != 0) { - goto fail_seqgap_init; - } + ngtcp2_static_ringbuf_dcid_retired_init(&(*pconn)->dcid.retired); - rv = ngtcp2_ksl_init(&(*pconn)->scid.set, cid_less, sizeof(ngtcp2_cid), mem); - if (rv != 0) { - goto fail_scid_set_init; - } + ngtcp2_gaptr_init(&(*pconn)->dcid.seqgap, mem); - ngtcp2_pq_init(&(*pconn)->scid.used, ts_retired_less, mem); + ngtcp2_ksl_init(&(*pconn)->scid.set, cid_less, sizeof(ngtcp2_cid), mem); - rv = ngtcp2_map_init(&(*pconn)->strms, mem); - if (rv != 0) { - goto fail_strms_init; - } + ngtcp2_pq_init(&(*pconn)->scid.used, retired_ts_less, mem); + + ngtcp2_map_init(&(*pconn)->strms, mem); ngtcp2_pq_init(&(*pconn)->tx.strmq, cycle_less, mem); - rv = ngtcp2_idtr_init(&(*pconn)->remote.bidi.idtr, !server, mem); - if (rv != 0) { - goto fail_remote_bidi_idtr_init; - } + ngtcp2_idtr_init(&(*pconn)->remote.bidi.idtr, !server, mem); - rv = ngtcp2_idtr_init(&(*pconn)->remote.uni.idtr, !server, mem); - if (rv != 0) { - goto fail_remote_uni_idtr_init; - } + ngtcp2_idtr_init(&(*pconn)->remote.uni.idtr, !server, mem); - rv = ngtcp2_ringbuf_init(&(*pconn)->rx.path_challenge, 4, - sizeof(ngtcp2_path_challenge_entry), mem); - if (rv != 0) { - goto fail_rx_path_challenge_init; - } + ngtcp2_static_ringbuf_path_challenge_init(&(*pconn)->rx.path_challenge); ngtcp2_log_init(&(*pconn)->log, scid, settings->log_printf, settings->initial_ts, user_data); @@ -808,17 +1130,18 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, if ((*pconn)->qlog.write) { buf = ngtcp2_mem_malloc(mem, NGTCP2_QLOG_BUFLEN); if (buf == NULL) { + rv = NGTCP2_ERR_NOMEM; goto fail_qlog_buf; } ngtcp2_buf_init(&(*pconn)->qlog.buf, buf, NGTCP2_QLOG_BUFLEN); } (*pconn)->local.settings = *settings; - (*pconn)->local.transport_params = *params; if (settings->token.len) { buf = ngtcp2_mem_malloc(mem, settings->token.len); if (buf == NULL) { + rv = NGTCP2_ERR_NOMEM; goto fail_token; } memcpy(buf, settings->token.base, settings->token.len); @@ -828,8 +1151,8 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, (*pconn)->local.settings.token.len = 0; } - if (settings->max_udp_payload_size == 0) { - (*pconn)->local.settings.max_udp_payload_size = NGTCP2_DEFAULT_MAX_PKTLEN; + if (!(*pconn)->local.settings.original_version) { + (*pconn)->local.settings.original_version = client_chosen_version; } conn_reset_conn_stat(*pconn, &(*pconn)->cstat); @@ -854,29 +1177,43 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, goto fail_cc_init; } break; - case NGTCP2_CC_ALGO_CUSTOM: - assert(settings->cc); - (*pconn)->cc = *settings->cc; - (*pconn)->cc.ccb->log = &(*pconn)->log; + case NGTCP2_CC_ALGO_BBR: + rv = ngtcp2_cc_bbr_cc_init(&(*pconn)->cc, &(*pconn)->log, &(*pconn)->cstat, + &(*pconn)->rst, settings->initial_ts, + callbacks->rand, &settings->rand_ctx, mem); + if (rv != 0) { + goto fail_cc_init; + } + break; + case NGTCP2_CC_ALGO_BBR2: + rv = ngtcp2_cc_bbr2_cc_init(&(*pconn)->cc, &(*pconn)->log, &(*pconn)->cstat, + &(*pconn)->rst, settings->initial_ts, + callbacks->rand, &settings->rand_ctx, mem); + if (rv != 0) { + goto fail_cc_init; + } break; default: assert(0); } rv = pktns_new(&(*pconn)->in_pktns, NGTCP2_PKTNS_ID_INITIAL, &(*pconn)->rst, - &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, mem); + &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, + &(*pconn)->rtb_entry_objalloc, &(*pconn)->frc_objalloc, mem); if (rv != 0) { goto fail_in_pktns_init; } rv = pktns_new(&(*pconn)->hs_pktns, NGTCP2_PKTNS_ID_HANDSHAKE, &(*pconn)->rst, - &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, mem); + &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, + &(*pconn)->rtb_entry_objalloc, &(*pconn)->frc_objalloc, mem); if (rv != 0) { goto fail_hs_pktns_init; } rv = pktns_init(&(*pconn)->pktns, NGTCP2_PKTNS_ID_APPLICATION, &(*pconn)->rst, - &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, mem); + &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, + &(*pconn)->rtb_entry_objalloc, &(*pconn)->frc_objalloc, mem); if (rv != 0) { goto fail_pktns_init; } @@ -889,7 +1226,7 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, /* Set stateless reset token later if it is available in the local transport parameters */ - ngtcp2_scid_init(scident, 0, scid, NULL); + ngtcp2_scid_init(scident, 0, scid); rv = ngtcp2_ksl_insert(&(*pconn)->scid.set, NULL, &scident->cid, scident); if (rv != 0) { @@ -899,22 +1236,111 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, scident = NULL; ngtcp2_dcid_init(&(*pconn)->dcid.current, 0, dcid, NULL); - ngtcp2_path_copy(&(*pconn)->dcid.current.ps.path, path); + ngtcp2_dcid_set_path(&(*pconn)->dcid.current, path); rv = ngtcp2_gaptr_push(&(*pconn)->dcid.seqgap, 0, 1); if (rv != 0) { goto fail_seqgap_push; } + if (settings->preferred_versionslen) { + if (!server && !ngtcp2_is_reserved_version(client_chosen_version)) { + for (i = 0; i < settings->preferred_versionslen; ++i) { + if (settings->preferred_versions[i] == client_chosen_version) { + break; + } + } + + assert(i < settings->preferred_versionslen); + } + + preferred_versions = ngtcp2_mem_malloc( + mem, sizeof(uint32_t) * settings->preferred_versionslen); + if (preferred_versions == NULL) { + rv = NGTCP2_ERR_NOMEM; + goto fail_preferred_versions; + } + + for (i = 0; i < settings->preferred_versionslen; ++i) { + assert(ngtcp2_is_supported_version(settings->preferred_versions[i])); + + preferred_versions[i] = settings->preferred_versions[i]; + } + + (*pconn)->vneg.preferred_versions = preferred_versions; + (*pconn)->vneg.preferred_versionslen = settings->preferred_versionslen; + } + + if (settings->other_versionslen) { + if (!server && !ngtcp2_is_reserved_version(client_chosen_version)) { + for (i = 0; i < settings->other_versionslen; ++i) { + if (settings->other_versions[i] == client_chosen_version) { + break; + } + } + + assert(i < settings->other_versionslen); + } + + for (i = 0; i < settings->other_versionslen; ++i) { + assert(ngtcp2_is_reserved_version(settings->other_versions[i]) || + ngtcp2_is_supported_version(settings->other_versions[i])); + } + + rv = other_versions_new(&buf, settings->other_versions, + settings->other_versionslen, mem); + if (rv != 0) { + goto fail_other_versions; + } + + (*pconn)->vneg.other_versions = buf; + (*pconn)->vneg.other_versionslen = + sizeof(uint32_t) * settings->other_versionslen; + } else if (server) { + if (settings->preferred_versionslen) { + rv = other_versions_new(&buf, settings->preferred_versions, + settings->preferred_versionslen, mem); + if (rv != 0) { + goto fail_other_versions; + } + + (*pconn)->vneg.other_versions = buf; + (*pconn)->vneg.other_versionslen = + sizeof(uint32_t) * settings->preferred_versionslen; + } else { + (*pconn)->vneg.other_versions = server_default_other_versions; + (*pconn)->vneg.other_versionslen = sizeof(server_default_other_versions); + } + } else if (!server && !ngtcp2_is_reserved_version(client_chosen_version)) { + rv = other_versions_new(&buf, &client_chosen_version, 1, mem); + if (rv != 0) { + goto fail_other_versions; + } + + (*pconn)->vneg.other_versions = buf; + (*pconn)->vneg.other_versionslen = sizeof(uint32_t); + } + + (*pconn)->client_chosen_version = client_chosen_version; + + conn_set_local_transport_params(*pconn, params); + + callbacks->rand(&fixed_bit_byte, 1, &settings->rand_ctx); + if (fixed_bit_byte & 1) { + (*pconn)->flags |= NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT; + } + + (*pconn)->keep_alive.last_ts = UINT64_MAX; + (*pconn)->server = server; (*pconn)->oscid = *scid; (*pconn)->callbacks = *callbacks; - (*pconn)->version = version; (*pconn)->mem = mem; (*pconn)->user_data = user_data; (*pconn)->idle_ts = settings->initial_ts; (*pconn)->crypto.key_update.confirmed_ts = UINT64_MAX; (*pconn)->tx.last_max_data_ts = UINT64_MAX; + (*pconn)->tx.pacing.next_ts = UINT64_MAX; (*pconn)->early.discard_started_ts = UINT64_MAX; conn_reset_ecn_validation_state(*pconn); @@ -924,12 +1350,13 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, return 0; +fail_other_versions: + ngtcp2_mem_free(mem, (*pconn)->vneg.preferred_versions); +fail_preferred_versions: fail_seqgap_push: fail_scid_set_insert: ngtcp2_mem_free(mem, scident); fail_scident: - ngtcp2_mem_free(mem, (*pconn)->local.settings.token.base); -fail_token: pktns_free(&(*pconn)->pktns, mem); fail_pktns_init: pktns_del((*pconn)->hs_pktns, mem); @@ -938,41 +1365,36 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, fail_in_pktns_init: cc_del(&(*pconn)->cc, settings->cc_algo, mem); fail_cc_init: + ngtcp2_mem_free(mem, (*pconn)->local.settings.token.base); +fail_token: ngtcp2_mem_free(mem, (*pconn)->qlog.buf.begin); fail_qlog_buf: - ngtcp2_ringbuf_free(&(*pconn)->rx.path_challenge); -fail_rx_path_challenge_init: ngtcp2_idtr_free(&(*pconn)->remote.uni.idtr); -fail_remote_uni_idtr_init: ngtcp2_idtr_free(&(*pconn)->remote.bidi.idtr); -fail_remote_bidi_idtr_init: ngtcp2_map_free(&(*pconn)->strms); -fail_strms_init: delete_scid(&(*pconn)->scid.set, mem); ngtcp2_ksl_free(&(*pconn)->scid.set); -fail_scid_set_init: ngtcp2_gaptr_free(&(*pconn)->dcid.seqgap); -fail_seqgap_init: - ngtcp2_ringbuf_free(&(*pconn)->dcid.retired); -fail_dcid_retired_init: - ngtcp2_ringbuf_free(&(*pconn)->dcid.unused); -fail_dcid_unused_init: - ngtcp2_ringbuf_free(&(*pconn)->dcid.bound); -fail_dcid_bound_init: + ngtcp2_objalloc_free(&(*pconn)->strm_objalloc); + ngtcp2_objalloc_free(&(*pconn)->rtb_entry_objalloc); + ngtcp2_objalloc_free(&(*pconn)->frc_objalloc); ngtcp2_mem_free(mem, *pconn); fail_conn: return rv; } -int ngtcp2_conn_client_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, const ngtcp2_path *path, - uint32_t version, const ngtcp2_callbacks *callbacks, - const ngtcp2_settings *settings, - const ngtcp2_transport_params *params, - const ngtcp2_mem *mem, void *user_data) { +int ngtcp2_conn_client_new_versioned( + ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, + const ngtcp2_path *path, uint32_t client_chosen_version, + int callbacks_version, const ngtcp2_callbacks *callbacks, + int settings_version, const ngtcp2_settings *settings, + int transport_params_version, const ngtcp2_transport_params *params, + const ngtcp2_mem *mem, void *user_data) { int rv; - rv = conn_new(pconn, dcid, scid, path, version, callbacks, settings, params, - mem, user_data, 0); + + rv = conn_new(pconn, dcid, scid, path, client_chosen_version, + callbacks_version, callbacks, settings_version, settings, + transport_params_version, params, mem, user_data, 0); if (rv != 0) { return rv; } @@ -990,18 +1412,22 @@ int ngtcp2_conn_client_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, return 0; } -int ngtcp2_conn_server_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, const ngtcp2_path *path, - uint32_t version, const ngtcp2_callbacks *callbacks, - const ngtcp2_settings *settings, - const ngtcp2_transport_params *params, - const ngtcp2_mem *mem, void *user_data) { +int ngtcp2_conn_server_new_versioned( + ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, + const ngtcp2_path *path, uint32_t client_chosen_version, + int callbacks_version, const ngtcp2_callbacks *callbacks, + int settings_version, const ngtcp2_settings *settings, + int transport_params_version, const ngtcp2_transport_params *params, + const ngtcp2_mem *mem, void *user_data) { int rv; - rv = conn_new(pconn, dcid, scid, path, version, callbacks, settings, params, - mem, user_data, 1); + + rv = conn_new(pconn, dcid, scid, path, client_chosen_version, + callbacks_version, callbacks, settings_version, settings, + transport_params_version, params, mem, user_data, 1); if (rv != 0) { return rv; } + (*pconn)->state = NGTCP2_CS_SERVER_INITIAL; (*pconn)->local.bidi.next_stream_id = 1; (*pconn)->local.uni.next_stream_id = 3; @@ -1029,22 +1455,37 @@ static uint64_t conn_fc_credits(ngtcp2_conn *conn, ngtcp2_strm *strm) { * sent to the given stream. |len| might be shorted because of * available flow control credits. */ -static size_t conn_enforce_flow_control(ngtcp2_conn *conn, ngtcp2_strm *strm, - size_t len) { +static uint64_t conn_enforce_flow_control(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint64_t len) { uint64_t fc_credits = conn_fc_credits(conn, strm); - return (size_t)ngtcp2_min((uint64_t)len, fc_credits); + return ngtcp2_min(len, fc_credits); } -static int delete_strms_each(ngtcp2_map_entry *ent, void *ptr) { - const ngtcp2_mem *mem = ptr; - ngtcp2_strm *s = ngtcp2_struct_of(ent, ngtcp2_strm, me); +static int delete_strms_each(void *data, void *ptr) { + ngtcp2_conn *conn = ptr; + ngtcp2_strm *s = data; ngtcp2_strm_free(s); - ngtcp2_mem_free(mem, s); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, s); return 0; } +static void conn_vneg_crypto_free(ngtcp2_conn *conn) { + if (conn->vneg.rx.ckm) { + conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.rx.ckm->aead_ctx); + } + conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.rx.hp_ctx); + + if (conn->vneg.tx.ckm) { + conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.tx.ckm->aead_ctx); + } + conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.tx.hp_ctx); + + ngtcp2_crypto_km_del(conn->vneg.rx.ckm, conn->mem); + ngtcp2_crypto_km_del(conn->vneg.tx.ckm, conn->mem); +} + void ngtcp2_conn_del(ngtcp2_conn *conn) { if (conn == NULL) { return; @@ -1111,6 +1552,16 @@ void ngtcp2_conn_del(ngtcp2_conn *conn) { conn_call_delete_crypto_aead_ctx(conn, &conn->crypto.retry_aead_ctx); + ngtcp2_transport_params_del(conn->remote.transport_params, conn->mem); + ngtcp2_transport_params_del(conn->remote.pending_transport_params, conn->mem); + + conn_vneg_crypto_free(conn); + + ngtcp2_mem_free(conn->mem, conn->vneg.preferred_versions); + if (conn->vneg.other_versions != server_default_other_versions) { + ngtcp2_mem_free(conn->mem, conn->vneg.other_versions); + } + ngtcp2_mem_free(conn->mem, conn->crypto.decrypt_buf.base); ngtcp2_mem_free(conn->mem, conn->crypto.decrypt_hp_buf.base); ngtcp2_mem_free(conn->mem, conn->local.settings.token.base); @@ -1128,24 +1579,26 @@ void ngtcp2_conn_del(ngtcp2_conn *conn) { ngtcp2_mem_free(conn->mem, conn->qlog.buf.begin); - ngtcp2_ringbuf_free(&conn->rx.path_challenge); - + ngtcp2_pmtud_del(conn->pmtud); ngtcp2_pv_del(conn->pv); + ngtcp2_mem_free(conn->mem, conn->rx.ccerr.reason); + ngtcp2_idtr_free(&conn->remote.uni.idtr); ngtcp2_idtr_free(&conn->remote.bidi.idtr); ngtcp2_mem_free(conn->mem, conn->tx.ack); ngtcp2_pq_free(&conn->tx.strmq); - ngtcp2_map_each_free(&conn->strms, delete_strms_each, (void *)conn->mem); + ngtcp2_map_each_free(&conn->strms, delete_strms_each, (void *)conn); ngtcp2_map_free(&conn->strms); ngtcp2_pq_free(&conn->scid.used); delete_scid(&conn->scid.set, conn->mem); ngtcp2_ksl_free(&conn->scid.set); ngtcp2_gaptr_free(&conn->dcid.seqgap); - ngtcp2_ringbuf_free(&conn->dcid.retired); - ngtcp2_ringbuf_free(&conn->dcid.unused); - ngtcp2_ringbuf_free(&conn->dcid.bound); + + ngtcp2_objalloc_free(&conn->strm_objalloc); + ngtcp2_objalloc_free(&conn->rtb_entry_objalloc); + ngtcp2_objalloc_free(&conn->frc_objalloc); ngtcp2_mem_free(conn->mem, conn); } @@ -1284,7 +1737,7 @@ static int conn_create_ack_frame(ngtcp2_conn *conn, ngtcp2_frame **pfr, ack->first_ack_blklen = 0; } - if (type == NGTCP2_PKT_SHORT) { + if (type == NGTCP2_PKT_1RTT) { ack->ack_delay_unscaled = ts - largest_ack_ts; ack->ack_delay = ack->ack_delay_unscaled / NGTCP2_MICROSECONDS / (1ULL << ack_delay_exponent); @@ -1412,11 +1865,11 @@ static size_t pktns_select_pkt_numlen(ngtcp2_pktns *pktns) { ngtcp2_rtb *rtb = &pktns->rtb; int64_t n = pkt_num - rtb->largest_acked_tx_pkt_num; - if (NGTCP2_MAX_PKT_NUM / 2 <= pkt_num) { + if (NGTCP2_MAX_PKT_NUM / 2 < n) { return 4; } - n = n * 2 + 1; + n = n * 2 - 1; if (n > 0xffffff) { return 4; @@ -1430,16 +1883,28 @@ static size_t pktns_select_pkt_numlen(ngtcp2_pktns *pktns) { return 1; } +/* + * conn_get_cwnd returns cwnd for the current path. + */ +static uint64_t conn_get_cwnd(ngtcp2_conn *conn) { + return conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) + ? ngtcp2_cc_compute_initcwnd(conn->cstat.max_udp_payload_size) + : conn->cstat.cwnd; +} + /* * conn_cwnd_is_zero returns nonzero if the number of bytes the local * endpoint can sent at this time is zero. */ static uint64_t conn_cwnd_is_zero(ngtcp2_conn *conn) { uint64_t bytes_in_flight = conn->cstat.bytes_in_flight; - uint64_t cwnd = - conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) - ? ngtcp2_cc_compute_initcwnd(conn->cstat.max_udp_payload_size) - : conn->cstat.cwnd; + uint64_t cwnd = conn_get_cwnd(conn); + + if (bytes_in_flight >= cwnd) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, + "cwnd limited bytes_in_flight=%lu cwnd=%lu", + bytes_in_flight, cwnd); + } return bytes_in_flight >= cwnd; } @@ -1447,11 +1912,12 @@ static uint64_t conn_cwnd_is_zero(ngtcp2_conn *conn) { /* * conn_retry_early_payloadlen returns the estimated wire length of * the first STREAM frame of 0-RTT packet which should be - * retransmitted due to Retry frame + * retransmitted due to Retry packet. */ -static size_t conn_retry_early_payloadlen(ngtcp2_conn *conn) { +static uint64_t conn_retry_early_payloadlen(ngtcp2_conn *conn) { ngtcp2_frame_chain *frc; ngtcp2_strm *strm; + uint64_t len; if (conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) { return 0; @@ -1465,8 +1931,13 @@ static size_t conn_retry_early_payloadlen(ngtcp2_conn *conn) { } frc = ngtcp2_strm_streamfrq_top(strm); - return ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt) + - NGTCP2_STREAM_OVERHEAD; + + len = ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt) + + NGTCP2_STREAM_OVERHEAD; + + /* Take the min because in conn_should_pad_pkt we take max in + order to deal with unbreakable DATAGRAM. */ + return ngtcp2_min(len, NGTCP2_MIN_COALESCED_PAYLOADLEN); } return 0; @@ -1479,7 +1950,7 @@ static void conn_cryptofrq_clear(ngtcp2_conn *conn, ngtcp2_pktns *pktns) { for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { frc = ngtcp2_ksl_it_get(&it); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); } ngtcp2_ksl_clear(&pktns->crypto.tx.frq); } @@ -1496,7 +1967,7 @@ static uint64_t conn_cryptofrq_unacked_offset(ngtcp2_conn *conn, ngtcp2_range gap; ngtcp2_rtb *rtb = &pktns->rtb; ngtcp2_ksl_it it; - size_t datalen; + uint64_t datalen; (void)conn; @@ -1539,7 +2010,7 @@ static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns, frc = ngtcp2_ksl_it_get(&it); fr = &frc->fr.crypto; - ngtcp2_ksl_remove(&pktns->crypto.tx.frq, &it, &fr->offset); + ngtcp2_ksl_remove_hint(&pktns->crypto.tx.frq, &it, &it, &fr->offset); idx = 0; offset = fr->offset; @@ -1561,7 +2032,7 @@ static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns, } if (idx == fr->datacnt) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); continue; } @@ -1600,10 +2071,10 @@ static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns, return 0; } - rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, fr->datacnt - end_idx, - conn->mem); + rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new( + &nfrc, fr->datacnt - end_idx, &conn->frc_objalloc, conn->mem); if (rv != 0) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } @@ -1622,8 +2093,8 @@ static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns, rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, conn->mem); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } @@ -1656,7 +2127,7 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, ngtcp2_frame_chain *frc, *nfrc; int rv; size_t nmerged; - size_t datalen; + uint64_t datalen; ngtcp2_vec a[NGTCP2_MAX_CRYPTO_DATACNT]; ngtcp2_vec b[NGTCP2_MAX_CRYPTO_DATACNT]; size_t acnt, bcnt; @@ -1684,10 +2155,11 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, assert(acnt > 0); assert(bcnt > 0); - rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, bcnt, conn->mem); + rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new( + &nfrc, bcnt, &conn->frc_objalloc, conn->mem); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } @@ -1700,15 +2172,16 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, conn->mem); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } - rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, acnt, conn->mem); + rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new( + &nfrc, acnt, &conn->frc_objalloc, conn->mem); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } @@ -1717,14 +2190,14 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, nfr->datacnt = acnt; ngtcp2_vec_copy(nfr->data, a, acnt); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); *pfrc = nfrc; return 0; } - left -= datalen; + left -= (size_t)datalen; ngtcp2_vec_copy(a, fr->data, fr->datacnt); acnt = fr->datacnt; @@ -1742,7 +2215,7 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, rv = conn_cryptofrq_unacked_pop(conn, pktns, &nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } if (nfrc == NULL) { @@ -1757,8 +2230,8 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, conn->mem); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } break; @@ -1768,7 +2241,7 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, left -= nmerged; if (nfr->datacnt == 0) { - ngtcp2_frame_chain_del(nfrc, conn->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem); continue; } @@ -1776,8 +2249,8 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc); if (rv != 0) { - ngtcp2_frame_chain_del(nfrc, conn->mem); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } @@ -1794,9 +2267,10 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, assert(acnt > fr->datacnt); - rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, acnt, conn->mem); + rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new( + &nfrc, acnt, &conn->frc_objalloc, conn->mem); if (rv != 0) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } @@ -1805,7 +2279,7 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, nfr->datacnt = acnt; ngtcp2_vec_copy(nfr->data, a, acnt); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); *pfrc = nfrc; @@ -1866,59 +2340,74 @@ static int conn_verify_dcid(ngtcp2_conn *conn, int *pnew_cid_used, /* * conn_should_pad_pkt returns nonzero if the packet should be padded. * |type| is the type of packet. |left| is the space left in packet - * buffer. |early_datalen| is the number of bytes which will be sent - * in the next, coalesced 0-RTT packet. + * buffer. |write_datalen| is the number of bytes which will be sent + * in the next, coalesced 0-RTT or 1RTT packet. */ static int conn_should_pad_pkt(ngtcp2_conn *conn, uint8_t type, size_t left, - size_t early_datalen, int ack_eliciting) { - size_t min_payloadlen; + uint64_t write_datalen, int ack_eliciting, + int require_padding) { + uint64_t min_payloadlen; - if (conn->server) { - if (type != NGTCP2_PKT_INITIAL || !ack_eliciting) { - return 0; - } + if (type == NGTCP2_PKT_INITIAL) { + if (conn->server) { + if (!ack_eliciting) { + return 0; + } - if (conn->hs_pktns->crypto.tx.ckm && - (conn->hs_pktns->rtb.probe_pkt_left || - ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq) || - !ngtcp2_acktr_empty(&conn->hs_pktns->acktr))) { - /* If we have something to send in Handshake packet, then add - PADDING in Handshake packet. */ - min_payloadlen = 128; + if (conn->hs_pktns->crypto.tx.ckm && + (conn->hs_pktns->rtb.probe_pkt_left || + ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq) || + !ngtcp2_acktr_empty(&conn->hs_pktns->acktr))) { + /* If we have something to send in Handshake packet, then add + PADDING in Handshake packet. */ + min_payloadlen = NGTCP2_MIN_COALESCED_PAYLOADLEN; + } else { + return 1; + } } else { - return 1; + if (conn->hs_pktns->crypto.tx.ckm && + (conn->hs_pktns->rtb.probe_pkt_left || + ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq) || + !ngtcp2_acktr_empty(&conn->hs_pktns->acktr))) { + /* If we have something to send in Handshake packet, then add + PADDING in Handshake packet. */ + min_payloadlen = NGTCP2_MIN_COALESCED_PAYLOADLEN; + } else if ((!conn->early.ckm && !conn->pktns.crypto.tx.ckm) || + write_datalen == 0) { + return 1; + } else { + /* If we have something to send in 0RTT or 1RTT packet, then + add PADDING in that packet. Take maximum in case that + write_datalen includes DATAGRAM which cannot be split. */ + min_payloadlen = + ngtcp2_max(write_datalen, NGTCP2_MIN_COALESCED_PAYLOADLEN); + } } } else { - if (type == NGTCP2_PKT_HANDSHAKE) { - return conn->in_pktns != NULL; - } - - if (conn->hs_pktns->crypto.tx.ckm && - (conn->hs_pktns->rtb.probe_pkt_left || - ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq) || - !ngtcp2_acktr_empty(&conn->hs_pktns->acktr))) { - /* If we have something to send in Handshake packet, then add - PADDING in Handshake packet. */ - min_payloadlen = 128; - } else if (!conn->early.ckm || early_datalen == 0) { + assert(type == NGTCP2_PKT_HANDSHAKE); + + if (!require_padding) { + return 0; + } + + if (!conn->pktns.crypto.tx.ckm || write_datalen == 0) { return 1; - } else { - /* If we have something to send in 0RTT packet, then add PADDING - in 0RTT packet. */ - min_payloadlen = ngtcp2_min(early_datalen, 128); } + + min_payloadlen = ngtcp2_max(write_datalen, NGTCP2_MIN_COALESCED_PAYLOADLEN); } + /* TODO the next packet type should be taken into account */ return left < /* TODO Assuming that pkt_num is encoded in 1 byte. */ NGTCP2_MIN_LONG_HEADERLEN + conn->dcid.current.cid.datalen + - conn->oscid.datalen + 1 /* payloadlen bytes - 1 */ + - min_payloadlen + NGTCP2_MAX_AEAD_OVERHEAD; + conn->oscid.datalen + NGTCP2_PKT_LENGTHLEN - 1 + min_payloadlen + + NGTCP2_MAX_AEAD_OVERHEAD; } static void conn_restart_timer_on_write(ngtcp2_conn *conn, ngtcp2_tstamp ts) { conn->idle_ts = ts; - conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE; + conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE; } static void conn_restart_timer_on_read(ngtcp2_conn *conn, ngtcp2_tstamp ts) { @@ -1926,14 +2415,110 @@ static void conn_restart_timer_on_read(ngtcp2_conn *conn, ngtcp2_tstamp ts) { conn->flags |= NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE; } -/* NGTCP2_WRITE_PKT_FLAG_NONE indicates that no flag is set. */ -#define NGTCP2_WRITE_PKT_FLAG_NONE 0x00 -/* NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING indicates that packet should - be padded */ -#define NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING 0x01 -/* NGTCP2_WRITE_PKT_FLAG_MORE indicates that more frames might come - and it should be encoded into the current packet. */ -#define NGTCP2_WRITE_PKT_FLAG_MORE 0x02 +/* + * conn_keep_alive_enabled returns nonzero if keep-alive is enabled. + */ +static int conn_keep_alive_enabled(ngtcp2_conn *conn) { + return conn->keep_alive.last_ts != UINT64_MAX && conn->keep_alive.timeout; +} + +/* + * conn_keep_alive_expired returns nonzero if keep-alive timer has + * expired. + */ +static int conn_keep_alive_expired(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + return conn_keep_alive_enabled(conn) && + conn->keep_alive.last_ts + conn->keep_alive.timeout <= ts; +} + +/* + * conn_keep_alive_expiry returns the expiry time of keep-alive timer. + */ +static ngtcp2_tstamp conn_keep_alive_expiry(ngtcp2_conn *conn) { + if ((conn->flags & NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED) || + !conn_keep_alive_enabled(conn)) { + return UINT64_MAX; + } + + return conn->keep_alive.last_ts + conn->keep_alive.timeout; +} + +/* + * conn_cancel_expired_keep_alive_timer cancels the expired keep-alive + * timer. + */ +static void conn_cancel_expired_keep_alive_timer(ngtcp2_conn *conn, + ngtcp2_tstamp ts) { + if (!(conn->flags & NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED) && + conn_keep_alive_expired(conn, ts)) { + conn->flags |= NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED; + } +} + +/* + * conn_update_keep_alive_last_ts updates the base time point of + * keep-alive timer. + */ +static void conn_update_keep_alive_last_ts(ngtcp2_conn *conn, + ngtcp2_tstamp ts) { + conn->keep_alive.last_ts = ts; + conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED; +} + +void ngtcp2_conn_set_keep_alive_timeout(ngtcp2_conn *conn, + ngtcp2_duration timeout) { + conn->keep_alive.timeout = timeout; +} + +/* + * NGTCP2_PKT_PACING_OVERHEAD defines overhead of userspace event + * loop. Packet pacing might require sub milliseconds packet spacing, + * but userspace event loop might not offer such precision. + * Typically, if delay is 0.5 microseconds, the actual delay after + * which we can send packet is well over 1 millisecond when event loop + * is involved (which includes other stuff, like reading packets etc + * in a typical single threaded use case). + */ +#define NGTCP2_PKT_PACING_OVERHEAD NGTCP2_MILLISECONDS + +static void conn_cancel_expired_pkt_tx_timer(ngtcp2_conn *conn, + ngtcp2_tstamp ts) { + if (conn->tx.pacing.next_ts == UINT64_MAX) { + return; + } + + if (conn->tx.pacing.next_ts > ts + NGTCP2_PKT_PACING_OVERHEAD) { + return; + } + + conn->tx.pacing.next_ts = UINT64_MAX; +} + +static int conn_pacing_pkt_tx_allowed(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + return conn->tx.pacing.next_ts == UINT64_MAX || + conn->tx.pacing.next_ts <= ts + NGTCP2_PKT_PACING_OVERHEAD; +} + +static uint8_t conn_pkt_flags(ngtcp2_conn *conn) { + if (conn->remote.transport_params && + conn->remote.transport_params->grease_quic_bit && + (conn->flags & NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT)) { + return NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR; + } + + return NGTCP2_PKT_FLAG_NONE; +} + +static uint8_t conn_pkt_flags_long(ngtcp2_conn *conn) { + return NGTCP2_PKT_FLAG_LONG_FORM | conn_pkt_flags(conn); +} + +static uint8_t conn_pkt_flags_short(ngtcp2_conn *conn) { + return (uint8_t)(conn_pkt_flags(conn) | ((conn->pktns.crypto.tx.ckm->flags & + NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) + ? NGTCP2_PKT_FLAG_KEY_PHASE + : NGTCP2_PKT_FLAG_NONE)); +} /* * conn_write_handshake_pkt writes handshake packet in the buffer @@ -1941,6 +2526,9 @@ static void conn_restart_timer_on_read(ngtcp2_conn *conn, ngtcp2_tstamp ts) { * packet type. It should be either NGTCP2_PKT_INITIAL or * NGTCP2_PKT_HANDSHAKE_PKT. * + * |write_datalen| is the minimum length of application data ready to + * send in subsequent 0RTT or 1RTT packet. + * * This function returns the number of bytes written in |dest| if it * succeeds, or one of the following negative error codes: * @@ -1952,7 +2540,7 @@ static void conn_restart_timer_on_read(ngtcp2_conn *conn, ngtcp2_tstamp ts) { static ngtcp2_ssize conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, uint8_t type, uint8_t flags, - size_t early_datalen, ngtcp2_tstamp ts) { + uint64_t write_datalen, ngtcp2_tstamp ts) { int rv; ngtcp2_ppe ppe; ngtcp2_pkt_hd hd; @@ -1964,13 +2552,14 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, ngtcp2_rtb_entry *rtbent; ngtcp2_pktns *pktns; size_t left; - uint8_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE; + uint16_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE; int require_padding = (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) != 0; int pkt_empty = 1; int padded = 0; int hd_logged = 0; uint64_t crypto_offset; ngtcp2_ssize num_reclaimed; + uint32_t version; switch (type) { case NGTCP2_PKT_INITIAL: @@ -1979,28 +2568,41 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, } assert(conn->in_pktns->crypto.tx.ckm); pktns = conn->in_pktns; + version = conn->negotiated_version ? conn->negotiated_version + : conn->client_chosen_version; + if (version == conn->client_chosen_version) { + cc.ckm = pktns->crypto.tx.ckm; + cc.hp_ctx = pktns->crypto.tx.hp_ctx; + } else { + assert(conn->vneg.version == version); + + cc.ckm = conn->vneg.tx.ckm; + cc.hp_ctx = conn->vneg.tx.hp_ctx; + } break; case NGTCP2_PKT_HANDSHAKE: if (!conn->hs_pktns || !conn->hs_pktns->crypto.tx.ckm) { return 0; } pktns = conn->hs_pktns; + version = conn->negotiated_version; + cc.ckm = pktns->crypto.tx.ckm; + cc.hp_ctx = pktns->crypto.tx.hp_ctx; break; default: assert(0); + abort(); } cc.aead = pktns->crypto.ctx.aead; cc.hp = pktns->crypto.ctx.hp; - cc.ckm = pktns->crypto.tx.ckm; - cc.hp_ctx = pktns->crypto.tx.hp_ctx; cc.encrypt = conn->callbacks.encrypt; cc.hp_mask = conn->callbacks.hp_mask; - ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, type, + ngtcp2_pkt_hd_init(&hd, conn_pkt_flags_long(conn), type, &conn->dcid.current.cid, &conn->oscid, pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns), - conn->version, 0); + version, 0); if (!conn->server && type == NGTCP2_PKT_INITIAL && conn->local.settings.token.len) { @@ -2023,7 +2625,7 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, /* ack_delay = */ 0, NGTCP2_DEFAULT_ACK_DELAY_EXPONENT); if (rv != 0) { - ngtcp2_frame_chain_list_del(frq, conn->mem); + ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, conn->mem); return rv; } @@ -2038,10 +2640,10 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, } } - /* Server requires at least NGTCP2_DEFAULT_MAX_PKTLEN bytes in order - to send ack-eliciting Initial packet. */ + /* Server requires at least NGTCP2_MAX_UDP_PAYLOAD_SIZE bytes in + order to send ack-eliciting Initial packet. */ if (!conn->server || type != NGTCP2_PKT_INITIAL || - destlen >= NGTCP2_DEFAULT_MAX_PKTLEN) { + destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE) { build_pkt: for (; ngtcp2_ksl_len(&pktns->crypto.tx.frq);) { left = ngtcp2_ppe_left(&ppe); @@ -2060,7 +2662,8 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, rv = conn_cryptofrq_pop(conn, &nfrc, pktns, left); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_list_del(frq, conn->mem); + ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, + conn->mem); return rv; } @@ -2078,15 +2681,16 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; } if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) && pktns->rtb.num_retransmittable && pktns->rtb.probe_pkt_left) { - num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, - pktns->rtb.probe_pkt_left + 1); + num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, 1); if (num_reclaimed < 0) { - ngtcp2_frame_chain_list_del(frq, conn->mem); + ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, + conn->mem); return rv; } if (num_reclaimed) { @@ -2098,21 +2702,19 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, send any probe packet. Client needs to send probe packets until it knows that server has completed address validation or handshake has been confirmed. */ - if (pktns->rtb.num_retransmittable == 0 && + if (pktns->rtb.num_pto_eliciting == 0 && (conn->server || (conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED | NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) { pktns->rtb.probe_pkt_left = 0; ngtcp2_conn_set_loss_detection_timer(conn, ts); + /* TODO If packet is empty, we should return now if cwnd is + zero. */ } } - /* Don't send any PING frame if client Initial has not been - acknowledged yet. */ if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) && - pktns->rtb.probe_pkt_left && - (type != NGTCP2_PKT_INITIAL || - ngtcp2_strm_is_all_tx_data_acked(&pktns->crypto.strm))) { + pktns->rtb.probe_pkt_left) { lfr.type = NGTCP2_FRAME_PING; rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr); @@ -2129,7 +2731,7 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { /* The intention of smaller limit is get more chance to measure RTT samples in early phase. */ - if (pktns->rtb.probe_pkt_left || pktns->tx.num_non_ack_pkt >= 1) { + if (pktns->tx.num_non_ack_pkt >= 1) { lfr.type = NGTCP2_FRAME_PING; rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr); @@ -2154,10 +2756,10 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, /* If we cannot write another packet, then we need to add padding to Initial here. */ - if (require_padding || - conn_should_pad_pkt( - conn, type, ngtcp2_ppe_left(&ppe), early_datalen, - (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) != 0)) { + if (conn_should_pad_pkt( + conn, type, ngtcp2_ppe_left(&ppe), write_datalen, + (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) != 0, + require_padding)) { lfr.type = NGTCP2_FRAME_PADDING; lfr.padding.len = ngtcp2_ppe_padding(&ppe); } else { @@ -2174,7 +2776,7 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, spktlen = ngtcp2_ppe_final(&ppe, NULL); if (spktlen < 0) { assert(ngtcp2_err_is_fatal((int)spktlen)); - ngtcp2_frame_chain_list_del(frq, conn->mem); + ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, conn->mem); return spktlen; } @@ -2185,17 +2787,19 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, &hd, ts); } - rv = ngtcp2_rtb_entry_new(&rtbent, &hd, frq, ts, (size_t)spktlen, - rtb_entry_flags, conn->mem); + rv = ngtcp2_rtb_entry_objalloc_new(&rtbent, &hd, frq, ts, (size_t)spktlen, + rtb_entry_flags, + &conn->rtb_entry_objalloc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_list_del(frq, conn->mem); + ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, conn->mem); return rv; } rv = conn_on_pkt_sent(conn, &pktns->rtb, rtbent); if (rv != 0) { - ngtcp2_rtb_entry_del(rtbent, conn->mem); + ngtcp2_rtb_entry_objalloc_del(rtbent, &conn->rtb_entry_objalloc, + &conn->frc_objalloc, conn->mem); return rv; } @@ -2212,8 +2816,12 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, --pktns->rtb.probe_pkt_left; } + conn_update_keep_alive_last_ts(conn, ts); + conn->dcid.current.bytes_sent += (uint64_t)spktlen; + conn->tx.pacing.pktlen += (size_t)spktlen; + ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); ++pktns->tx.last_pkt_num; @@ -2258,13 +2866,14 @@ static ngtcp2_ssize conn_write_ack_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, ack_delay = 0; ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; break; - case NGTCP2_PKT_SHORT: + case NGTCP2_PKT_1RTT: pktns = &conn->pktns; ack_delay = conn_compute_ack_delay(conn); ack_delay_exponent = conn->local.transport_params.ack_delay_exponent; break; default: assert(0); + abort(); } if (!pktns->crypto.tx.ckm) { @@ -2283,8 +2892,8 @@ static ngtcp2_ssize conn_write_ack_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } spktlen = ngtcp2_conn_write_single_frame_pkt( - conn, pi, dest, destlen, type, &conn->dcid.current.cid, ackfr, - NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); + conn, pi, dest, destlen, type, NGTCP2_WRITE_PKT_FLAG_NONE, + &conn->dcid.current.cid, ackfr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); if (spktlen <= 0) { return spktlen; @@ -2333,6 +2942,11 @@ static void conn_discard_initial_state(ngtcp2_conn *conn, ngtcp2_tstamp ts) { "discarding Initial packet number space"); conn_discard_pktns(conn, &conn->in_pktns, ts); + + conn_vneg_crypto_free(conn); + + memset(&conn->vneg.rx, 0, sizeof(conn->vneg.rx)); + memset(&conn->vneg.tx, 0, sizeof(conn->vneg.tx)); } /* @@ -2429,15 +3043,13 @@ static ngtcp2_ssize conn_write_handshake_ack_pkts(ngtcp2_conn *conn, static ngtcp2_ssize conn_write_client_initial(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, - size_t early_datalen, + uint64_t early_datalen, ngtcp2_tstamp ts) { int rv; - assert(conn->callbacks.client_initial); - - rv = conn->callbacks.client_initial(conn, conn->user_data); + rv = conn_call_client_initial(conn); if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; + return rv; } return conn_write_handshake_pkt(conn, pi, dest, destlen, NGTCP2_PKT_INITIAL, @@ -2445,6 +3057,39 @@ static ngtcp2_ssize conn_write_client_initial(ngtcp2_conn *conn, ts); } +/* + * dcid_tx_left returns the maximum number of bytes that server is + * allowed to send to an unvalidated path associated to |dcid|. + */ +static uint64_t dcid_tx_left(ngtcp2_dcid *dcid) { + if (dcid->flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) { + return SIZE_MAX; + } + /* From QUIC spec: Prior to validating the client address, servers + MUST NOT send more than three times as many bytes as the number + of bytes they have received. */ + assert(dcid->bytes_recv * 3 >= dcid->bytes_sent); + + return dcid->bytes_recv * 3 - dcid->bytes_sent; +} + +/* + * conn_server_tx_left returns the maximum number of bytes that server + * is allowed to send to an unvalidated path. + */ +static uint64_t conn_server_tx_left(ngtcp2_conn *conn, ngtcp2_dcid *dcid) { + assert(conn->server); + + /* If pv->dcid has the current path, use conn->dcid.current. This + is because conn->dcid.current gets update for bytes_recv and + bytes_sent. */ + if (ngtcp2_path_eq(&dcid->ps.path, &conn->dcid.current.ps.path)) { + return dcid_tx_left(&conn->dcid.current); + } + + return dcid_tx_left(dcid); +} + /* * conn_write_handshake_pkts writes Initial and Handshake packets in * the buffer pointed by |dest| whose length is |destlen|. @@ -2460,13 +3105,13 @@ static ngtcp2_ssize conn_write_client_initial(ngtcp2_conn *conn, static ngtcp2_ssize conn_write_handshake_pkts(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, - size_t early_datalen, + uint64_t write_datalen, ngtcp2_tstamp ts) { ngtcp2_ssize nwrite; ngtcp2_ssize res = 0; - int64_t prev_pkt_num = -1; ngtcp2_rtb_entry *rtbent; uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE; + ngtcp2_conn_stat *cstat = &conn->cstat; ngtcp2_ksl_it it; /* As a client, we would like to discard Initial packet number space @@ -2491,17 +3136,9 @@ static ngtcp2_ssize conn_write_handshake_pkts(ngtcp2_conn *conn, padded. */ conn_discard_initial_state(conn, ts); } else if (conn->in_pktns) { - if (conn->server) { - it = ngtcp2_rtb_head(&conn->in_pktns->rtb); - if (!ngtcp2_ksl_it_end(&it)) { - rtbent = ngtcp2_ksl_it_get(&it); - prev_pkt_num = rtbent->hd.pkt_num; - } - } - nwrite = conn_write_handshake_pkt(conn, pi, dest, destlen, NGTCP2_PKT_INITIAL, - NGTCP2_WRITE_PKT_FLAG_NONE, early_datalen, ts); + NGTCP2_WRITE_PKT_FLAG_NONE, write_datalen, ts); if (nwrite < 0) { assert(nwrite != NGTCP2_ERR_NOBUF); return nwrite; @@ -2510,6 +3147,15 @@ static ngtcp2_ssize conn_write_handshake_pkts(ngtcp2_conn *conn, if (nwrite == 0) { if (conn->server && (conn->in_pktns->rtb.probe_pkt_left || ngtcp2_ksl_len(&conn->in_pktns->crypto.tx.frq))) { + if (cstat->loss_detection_timer != UINT64_MAX && + conn_server_tx_left(conn, &conn->dcid.current) < + NGTCP2_MAX_UDP_PAYLOAD_SIZE) { + ngtcp2_log_info( + &conn->log, NGTCP2_LOG_EVENT_RCV, + "loss detection timer canceled due to amplification limit"); + cstat->loss_detection_timer = UINT64_MAX; + } + return 0; } } else { @@ -2517,24 +3163,27 @@ static ngtcp2_ssize conn_write_handshake_pkts(ngtcp2_conn *conn, dest += nwrite; destlen -= (size_t)nwrite; - if (conn->server && destlen) { - it = ngtcp2_rtb_head(&conn->in_pktns->rtb); - if (!ngtcp2_ksl_it_end(&it)) { - rtbent = ngtcp2_ksl_it_get(&it); - if (rtbent->hd.pkt_num != prev_pkt_num && - (rtbent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { - /* We might have already added padding to Initial, but in - that case, we should have destlen == 0 and no Handshake - packet will be written. */ - wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; + if (destlen) { + /* We might have already added padding to Initial, but in that + case, we should have destlen == 0 and no Handshake packet + will be written. */ + if (conn->server) { + it = ngtcp2_rtb_head(&conn->in_pktns->rtb); + if (!ngtcp2_ksl_it_end(&it)) { + rtbent = ngtcp2_ksl_it_get(&it); + if (rtbent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { + wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; + } } + } else { + wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; } } } } - nwrite = conn_write_handshake_pkt(conn, pi, dest, destlen, - NGTCP2_PKT_HANDSHAKE, wflags, 0, ts); + nwrite = conn_write_handshake_pkt( + conn, pi, dest, destlen, NGTCP2_PKT_HANDSHAKE, wflags, write_datalen, ts); if (nwrite < 0) { assert(nwrite != NGTCP2_ERR_NOBUF); return nwrite; @@ -2608,12 +3257,13 @@ static size_t conn_required_num_new_connection_id(ngtcp2_conn *conn) { return 0; } - assert(conn->remote.transport_params.active_connection_id_limit); + assert(conn->remote.transport_params); + assert(conn->remote.transport_params->active_connection_id_limit); /* len includes retired CID. We don't provide extra CID if doing so exceeds NGTCP2_MAX_SCID_POOL_SIZE. */ - n = conn->remote.transport_params.active_connection_id_limit + + n = conn->remote.transport_params->active_connection_id_limit + conn->scid.num_retired; return (size_t)ngtcp2_min(NGTCP2_MAX_SCID_POOL_SIZE, n) - len; @@ -2667,7 +3317,7 @@ static int conn_enqueue_new_connection_id(ngtcp2_conn *conn) { return NGTCP2_ERR_NOMEM; } - ngtcp2_scid_init(scid, seq, &cid, token); + ngtcp2_scid_init(scid, seq, &cid); rv = ngtcp2_ksl_insert(&conn->scid.set, NULL, &scid->cid, scid); if (rv != 0) { @@ -2675,7 +3325,7 @@ static int conn_enqueue_new_connection_id(ngtcp2_conn *conn) { return rv; } - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); if (rv != 0) { return rv; } @@ -2718,7 +3368,7 @@ static int conn_remove_retired_connection_id(ngtcp2_conn *conn, for (; !ngtcp2_pq_empty(&conn->scid.used);) { scid = ngtcp2_struct_of(ngtcp2_pq_top(&conn->scid.used), ngtcp2_scid, pe); - if (scid->ts_retired == UINT64_MAX || scid->ts_retired + timeout >= ts) { + if (scid->retired_ts == UINT64_MAX || scid->retired_ts + timeout >= ts) { break; } @@ -2737,9 +3387,9 @@ static int conn_remove_retired_connection_id(ngtcp2_conn *conn, --conn->scid.num_retired; } - for (; ngtcp2_ringbuf_len(&conn->dcid.retired);) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, 0); - if (dcid->ts_retired + timeout >= ts) { + for (; ngtcp2_ringbuf_len(&conn->dcid.retired.rb);) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, 0); + if (dcid->retired_ts + timeout >= ts) { break; } @@ -2748,7 +3398,7 @@ static int conn_remove_retired_connection_id(ngtcp2_conn *conn, return rv; } - ngtcp2_ringbuf_pop_front(&conn->dcid.retired); + ngtcp2_ringbuf_pop_front(&conn->dcid.retired.rb); } return 0; @@ -2762,10 +3412,33 @@ static size_t conn_min_short_pktlen(ngtcp2_conn *conn) { return conn->dcid.current.cid.datalen + NGTCP2_MIN_PKT_EXPANDLEN; } +/* + * conn_handle_unconfirmed_key_update_from_remote deals with key + * update which has not been confirmed yet and initiated by the remote + * endpoint. + * + * If key update was initiated by the remote endpoint, acknowledging a + * packet encrypted with the new key completes key update procedure. + */ +static void conn_handle_unconfirmed_key_update_from_remote(ngtcp2_conn *conn, + int64_t largest_ack, + ngtcp2_tstamp ts) { + if (!(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) || + (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR) || + largest_ack < conn->pktns.crypto.rx.ckm->pkt_num) { + return; + } + + conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED; + conn->crypto.key_update.confirmed_ts = ts; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CRY, "key update confirmed"); +} + /* * conn_write_pkt writes a protected packet in the buffer pointed by * |dest| whose length if |destlen|. |type| specifies the type of - * packet. It can be NGTCP2_PKT_SHORT or NGTCP2_PKT_0RTT. + * packet. It can be NGTCP2_PKT_1RTT or NGTCP2_PKT_0RTT. * * This function can send new stream data. In order to send stream * data, specify the underlying stream and parameters to @@ -2804,25 +3477,24 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, ngtcp2_rtb_entry *ent; ngtcp2_strm *strm; int pkt_empty = 1; - size_t ndatalen = 0; + uint64_t ndatalen = 0; int send_stream = 0; int stream_blocked = 0; int send_datagram = 0; ngtcp2_pktns *pktns = &conn->pktns; size_t left; - size_t datalen = 0; + uint64_t datalen = 0; ngtcp2_vec data[NGTCP2_MAX_STREAM_DATACNT]; size_t datacnt; - uint8_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE; + uint16_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE; int hd_logged = 0; ngtcp2_path_challenge_entry *pcent; - uint8_t hd_flags; + uint8_t hd_flags = NGTCP2_PKT_FLAG_NONE; int require_padding = (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) != 0; int write_more = (flags & NGTCP2_WRITE_PKT_FLAG_MORE) != 0; int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; size_t min_pktlen = conn_min_short_pktlen(conn); int padded = 0; - int credit_expanded = 0; ngtcp2_cc_pkt cc_pkt; uint64_t crypto_offset; uint64_t stream_offset; @@ -2831,6 +3503,9 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint64_t target_max_data; ngtcp2_conn_stat *cstat = &conn->cstat; uint64_t delta; + const ngtcp2_cid *scid = NULL; + int keep_alive_expired = 0; + uint32_t version = 0; /* Return 0 if destlen is less than minimum packet length which can trigger Stateless Reset */ @@ -2861,21 +3536,22 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (!ppe_pending) { switch (type) { - case NGTCP2_PKT_SHORT: - hd_flags = - (pktns->crypto.tx.ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) - ? NGTCP2_PKT_FLAG_KEY_PHASE - : NGTCP2_PKT_FLAG_NONE; + case NGTCP2_PKT_1RTT: + hd_flags = conn_pkt_flags_short(conn); + scid = NULL; cc->aead = pktns->crypto.ctx.aead; cc->hp = pktns->crypto.ctx.hp; cc->ckm = pktns->crypto.tx.ckm; cc->hp_ctx = pktns->crypto.tx.hp_ctx; + assert(conn->negotiated_version); + + version = conn->negotiated_version; + /* transport parameter is only valid after handshake completion which means we don't know how many connection ID that remote peer can accept before handshake completion. */ - if (conn->oscid.datalen && - (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + if (conn->oscid.datalen && conn_is_handshake_completed(conn)) { rv = conn_enqueue_new_connection_id(conn); if (rv != 0) { return rv; @@ -2888,11 +3564,13 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (!conn->early.ckm) { return 0; } - hd_flags = NGTCP2_PKT_FLAG_LONG_FORM; + hd_flags = conn_pkt_flags_long(conn); + scid = &conn->oscid; cc->aead = conn->early.ctx.aead; cc->hp = conn->early.ctx.hp; cc->ckm = conn->early.ckm; cc->hp_ctx = conn->early.hp_ctx; + version = conn->client_chosen_version; break; default: /* Unreachable */ @@ -2903,7 +3581,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, cc->hp_mask = conn->callbacks.hp_mask; if (conn_should_send_max_data(conn)) { - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); if (rv != 0) { return rv; } @@ -2937,12 +3615,11 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn->rx.max_offset = conn->rx.unsent_max_offset = nfrc->fr.max_data.max_data; - credit_expanded = 1; } - ngtcp2_pkt_hd_init(hd, hd_flags, type, &conn->dcid.current.cid, - &conn->oscid, pktns->tx.last_pkt_num + 1, - pktns_select_pkt_numlen(pktns), conn->version, 0); + ngtcp2_pkt_hd_init(hd, hd_flags, type, &conn->dcid.current.cid, scid, + pktns->tx.last_pkt_num + 1, + pktns_select_pkt_numlen(pktns), version, 0); ngtcp2_ppe_init(ppe, dest, destlen, cc); @@ -2956,8 +3633,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, return 0; } - if (ngtcp2_ringbuf_len(&conn->rx.path_challenge)) { - pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge, 0); + if (ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb)) { + pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge.rb, 0); /* PATH_RESPONSE is bound to the path that the corresponding PATH_CHALLENGE is received. */ @@ -2970,11 +3647,12 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (rv != 0) { assert(NGTCP2_ERR_NOBUF == rv); } else { - ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge); + ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge.rb); pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING; - require_padding = !conn->server || destlen >= 1200; + require_padding = + !conn->server || destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE; /* We don't retransmit PATH_RESPONSE. */ } } @@ -2996,6 +3674,10 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, ngtcp2_acktr_commit_ack(&pktns->acktr); ngtcp2_acktr_add_ack(&pktns->acktr, hd->pkt_num, ackfr->ack.largest_ack); + if (type == NGTCP2_PKT_1RTT) { + conn_handle_unconfirmed_key_update_from_remote( + conn, ackfr->ack.largest_ack, ts); + } pkt_empty = 0; } } @@ -3006,7 +3688,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, ((*pfrc)->binder->flags & NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK)) { frc = *pfrc; *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); continue; } @@ -3017,9 +3699,22 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (strm == NULL || (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD)) { frc = *pfrc; *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); continue; } + + if (!(strm->flags & NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED)) { + strm->flags |= NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED; + + rv = conn_call_stream_stop_sending( + conn, (*pfrc)->fr.stop_sending.stream_id, + (*pfrc)->fr.stop_sending.app_error_code, strm->stream_user_data); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + } + break; case NGTCP2_FRAME_STREAM: assert(0); @@ -3029,7 +3724,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn->remote.bidi.max_streams) { frc = *pfrc; *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); continue; } break; @@ -3038,7 +3733,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn->remote.uni.max_streams) { frc = *pfrc; *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); continue; } break; @@ -3049,7 +3744,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, (*pfrc)->fr.max_stream_data.max_stream_data < strm->rx.max_offset) { frc = *pfrc; *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); continue; } break; @@ -3057,13 +3752,10 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if ((*pfrc)->fr.max_data.max_data < conn->rx.max_offset) { frc = *pfrc; *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); continue; } break; - case NGTCP2_FRAME_RETIRE_CONNECTION_ID: - ++conn->dcid.num_retire_queued; - break; case NGTCP2_FRAME_CRYPTO: assert(0); break; @@ -3077,6 +3769,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; pfrc = &(*pfrc)->next; } @@ -3117,6 +3810,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; } } @@ -3132,7 +3826,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, return rv; } - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); return rv; @@ -3149,6 +3843,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } else { pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; pfrc = &(*pfrc)->next; } @@ -3163,7 +3858,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, return rv; } - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); return rv; @@ -3181,6 +3876,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } else { pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; pfrc = &(*pfrc)->next; } @@ -3193,7 +3889,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (!(strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && conn_should_send_max_stream_data(conn, strm)) { - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); return rv; @@ -3236,8 +3932,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } pkt_empty = 0; - credit_expanded = 1; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; pfrc = &(*pfrc)->next; strm->rx.max_offset = strm->rx.unsent_max_offset = @@ -3253,6 +3949,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (stream_offset == (uint64_t)-1) { ngtcp2_strm_streamfrq_clear(strm); ngtcp2_conn_tx_strmq_pop(conn); + assert(conn->tx.strmq_nretrans); + --conn->tx.strmq_nretrans; continue; } @@ -3286,10 +3984,13 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; if (ngtcp2_strm_streamfrq_empty(strm)) { ngtcp2_conn_tx_strmq_pop(conn); + assert(conn->tx.strmq_nretrans); + --conn->tx.strmq_nretrans; continue; } @@ -3303,35 +4004,11 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } } - /* Add ACK if MAX_DATA or MAX_STREAM_DATA frame is encoded to - decrease packet count. */ - if (ackfr == NULL && credit_expanded) { - rv = conn_create_ack_frame( - conn, &ackfr, pktns, type, ts, /* ack_delay = */ 0, - conn->local.transport_params.ack_delay_exponent); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - return rv; - } - - if (ackfr) { - rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, ackfr); - if (rv != 0) { - assert(NGTCP2_ERR_NOBUF == rv); - } else { - ngtcp2_acktr_commit_ack(&pktns->acktr); - ngtcp2_acktr_add_ack(&pktns->acktr, hd->pkt_num, - ackfr->ack.largest_ack); - } - } - } - if (rv != NGTCP2_ERR_NOBUF && !send_stream && !send_datagram && !(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) && pktns->rtb.num_retransmittable && pktns->tx.frq == NULL && pktns->rtb.probe_pkt_left) { - num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, - pktns->rtb.probe_pkt_left + 1); + num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, 1); if (num_reclaimed < 0) { return rv; } @@ -3343,9 +4020,11 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, those packets have been acknowledged (i.e., retransmission in another packet). In this case, we don't have to send any probe packet. */ - if (pktns->rtb.num_retransmittable == 0) { + if (pktns->rtb.num_pto_eliciting == 0) { pktns->rtb.probe_pkt_left = 0; ngtcp2_conn_set_loss_detection_timer(conn, ts); + /* TODO If packet is empty, we should return now if cwnd is + zero. */ } } } else { @@ -3362,11 +4041,15 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, vmsg->stream.strm->stream_id, vmsg->stream.strm->tx.offset, ndatalen, left)) != (size_t)-1 && (ndatalen || datalen == 0)) { - datacnt = ngtcp2_vec_copy_at_most( - data, &ndatalen, NGTCP2_MAX_STREAM_DATACNT, vmsg->stream.data, - vmsg->stream.datacnt, ndatalen); + datacnt = ngtcp2_vec_copy_at_most(data, NGTCP2_MAX_STREAM_DATACNT, + vmsg->stream.data, vmsg->stream.datacnt, + (size_t)ndatalen); + ndatalen = ngtcp2_vec_len(data, datacnt); + + assert((datacnt == 0 && datalen == 0) || (datacnt && datalen)); - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, datacnt, conn->mem); + rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( + &nfrc, datacnt, &conn->frc_objalloc, conn->mem); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); return rv; @@ -3393,6 +4076,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; vmsg->stream.strm->tx.offset += ndatalen; @@ -3410,22 +4094,49 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } if (rv != NGTCP2_ERR_NOBUF && send_datagram && - left >= ngtcp2_pkt_datagram_framelen(datalen)) { - lfr.datagram.type = NGTCP2_FRAME_DATAGRAM_LEN; - lfr.datagram.datacnt = vmsg->datagram.datacnt; - lfr.datagram.data = (ngtcp2_vec *)vmsg->datagram.data; + left >= ngtcp2_pkt_datagram_framelen((size_t)datalen)) { + if (conn->callbacks.ack_datagram || conn->callbacks.lost_datagram) { + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } - rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr); - assert(rv == 0); + nfrc->fr.datagram.type = NGTCP2_FRAME_DATAGRAM_LEN; + nfrc->fr.datagram.dgram_id = vmsg->datagram.dgram_id; + nfrc->fr.datagram.datacnt = vmsg->datagram.datacnt; + nfrc->fr.datagram.data = (ngtcp2_vec *)vmsg->datagram.data; - pkt_empty = 0; - rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING; + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr); + assert(rv == 0); - if (vmsg->datagram.paccepted) { - *vmsg->datagram.paccepted = 1; - } - } else { - send_datagram = 0; + /* Because DATAGRAM will not be retransmitted, we do not use + data anymore. Just nullify it. The only reason to keep + track a frame is keep dgram_id to pass it to + ngtcp2_ack_datagram or ngtcp2_lost_datagram callbacks. */ + nfrc->fr.datagram.datacnt = 0; + nfrc->fr.datagram.data = NULL; + + *pfrc = nfrc; + pfrc = &(*pfrc)->next; + } else { + lfr.datagram.type = NGTCP2_FRAME_DATAGRAM_LEN; + lfr.datagram.datacnt = vmsg->datagram.datacnt; + lfr.datagram.data = (ngtcp2_vec *)vmsg->datagram.data; + + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr); + assert(rv == 0); + } + + pkt_empty = 0; + rtb_entry_flags |= + NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | NGTCP2_RTB_ENTRY_FLAG_DATAGRAM; + + if (vmsg->datagram.paccepted) { + *vmsg->datagram.paccepted = 1; + } + } else { + send_datagram = 0; } if (pkt_empty) { @@ -3434,7 +4145,9 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, return NGTCP2_ERR_STREAM_DATA_BLOCKED; } - if (conn->pktns.rtb.probe_pkt_left == 0) { + keep_alive_expired = conn_keep_alive_expired(conn, ts); + + if (conn->pktns.rtb.probe_pkt_left == 0 && !keep_alive_expired) { return 0; } } else if (write_more) { @@ -3470,7 +4183,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { - if (pktns->tx.num_non_ack_pkt >= NGTCP2_MAX_NON_ACK_TX_PKT) { + if (pktns->tx.num_non_ack_pkt >= NGTCP2_MAX_NON_ACK_TX_PKT || + keep_alive_expired || conn->pktns.rtb.probe_pkt_left) { lfr.type = NGTCP2_FRAME_PING; rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr); @@ -3480,6 +4194,9 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, packet is still empty. */ } else { rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING; + if (conn->pktns.rtb.probe_pkt_left) { + rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_PROBE; + } pktns->tx.num_non_ack_pkt = 0; } } else { @@ -3491,18 +4208,16 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, /* TODO Push STREAM frame back to ngtcp2_strm if there is an error before ngtcp2_rtb_entry is safely created and added. */ - lfr.type = NGTCP2_FRAME_PADDING; - if ((require_padding || - /* Making full sized packet will help GSO a bit */ - ngtcp2_ppe_left(ppe) < 10 || - (type == NGTCP2_PKT_0RTT && conn->state == NGTCP2_CS_CLIENT_INITIAL)) && - ngtcp2_ppe_left(ppe)) { + if (require_padding || + /* Making full sized packet will help GSO a bit */ + ngtcp2_ppe_left(ppe) < 10) { lfr.padding.len = ngtcp2_ppe_padding(ppe); } else { lfr.padding.len = ngtcp2_ppe_padding_size(ppe, min_pktlen); } if (lfr.padding.len) { + lfr.type = NGTCP2_FRAME_PADDING; padded = 1; ngtcp2_log_tx_fr(&conn->log, hd, &lfr); ngtcp2_qlog_write_frame(&conn->qlog, &lfr); @@ -3525,8 +4240,9 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, hd, ts); } - rv = ngtcp2_rtb_entry_new(&ent, hd, NULL, ts, (size_t)nwrite, - rtb_entry_flags, conn->mem); + rv = ngtcp2_rtb_entry_objalloc_new(&ent, hd, NULL, ts, (size_t)nwrite, + rtb_entry_flags, + &conn->rtb_entry_objalloc); if (rv != 0) { assert(ngtcp2_err_is_fatal((int)nwrite)); return rv; @@ -3547,7 +4263,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, rv = conn_on_pkt_sent(conn, &pktns->rtb, ent); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_rtb_entry_del(ent, conn->mem); + ngtcp2_rtb_entry_objalloc_del(ent, &conn->rtb_entry_objalloc, + &conn->frc_objalloc, conn->mem); return rv; } @@ -3556,7 +4273,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn->cc.on_pkt_sent( &conn->cc, &conn->cstat, ngtcp2_cc_pkt_init(&cc_pkt, hd->pkt_num, (size_t)nwrite, - NGTCP2_PKTNS_ID_APPLICATION, ts)); + NGTCP2_PKTNS_ID_APPLICATION, ts, ent->rst.lost, + ent->rst.tx_in_flight, ent->rst.is_app_limited)); } if (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE) { @@ -3567,7 +4285,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn_handle_tx_ecn(conn, pi, NULL, pktns, hd, ts); } - conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_PPE_PENDING; + conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_PPE_PENDING; if (pktns->rtb.probe_pkt_left && (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { @@ -3577,8 +4295,12 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, nwrite); } + conn_update_keep_alive_last_ts(conn, ts); + conn->dcid.current.bytes_sent += (uint64_t)nwrite; + conn->tx.pacing.pktlen += (size_t)nwrite; + ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); ++pktns->tx.last_pkt_num; @@ -3588,8 +4310,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, - uint8_t type, const ngtcp2_cid *dcid, ngtcp2_frame *fr, uint8_t rtb_flags, - const ngtcp2_path *path, ngtcp2_tstamp ts) { + uint8_t type, uint8_t flags, const ngtcp2_cid *dcid, ngtcp2_frame *fr, + uint16_t rtb_entry_flags, const ngtcp2_path *path, ngtcp2_tstamp ts) { int rv; ngtcp2_ppe ppe; ngtcp2_pkt_hd hd; @@ -3597,41 +4319,59 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( ngtcp2_ssize nwrite; ngtcp2_crypto_cc cc; ngtcp2_pktns *pktns; - uint8_t flags; + uint8_t hd_flags; ngtcp2_rtb_entry *rtbent; int padded = 0; + const ngtcp2_cid *scid; + uint32_t version; switch (type) { case NGTCP2_PKT_INITIAL: pktns = conn->in_pktns; - flags = NGTCP2_PKT_FLAG_LONG_FORM; + hd_flags = conn_pkt_flags_long(conn); + scid = &conn->oscid; + version = conn->negotiated_version ? conn->negotiated_version + : conn->client_chosen_version; + if (version == conn->client_chosen_version) { + cc.ckm = pktns->crypto.tx.ckm; + cc.hp_ctx = pktns->crypto.tx.hp_ctx; + } else { + assert(version == conn->vneg.version); + + cc.ckm = conn->vneg.tx.ckm; + cc.hp_ctx = conn->vneg.tx.hp_ctx; + } break; case NGTCP2_PKT_HANDSHAKE: pktns = conn->hs_pktns; - flags = NGTCP2_PKT_FLAG_LONG_FORM; + hd_flags = conn_pkt_flags_long(conn); + scid = &conn->oscid; + version = conn->negotiated_version; + cc.ckm = pktns->crypto.tx.ckm; + cc.hp_ctx = pktns->crypto.tx.hp_ctx; break; - case NGTCP2_PKT_SHORT: - /* 0 means Short packet. */ + case NGTCP2_PKT_1RTT: pktns = &conn->pktns; - flags = (pktns->crypto.tx.ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) - ? NGTCP2_PKT_FLAG_KEY_PHASE - : NGTCP2_PKT_FLAG_NONE; + hd_flags = conn_pkt_flags_short(conn); + scid = NULL; + version = conn->negotiated_version; + cc.ckm = pktns->crypto.tx.ckm; + cc.hp_ctx = pktns->crypto.tx.hp_ctx; break; default: /* We don't support 0-RTT packet in this function. */ assert(0); + abort(); } cc.aead = pktns->crypto.ctx.aead; cc.hp = pktns->crypto.ctx.hp; - cc.ckm = pktns->crypto.tx.ckm; - cc.hp_ctx = pktns->crypto.tx.hp_ctx; cc.encrypt = conn->callbacks.encrypt; cc.hp_mask = conn->callbacks.hp_mask; - ngtcp2_pkt_hd_init(&hd, flags, type, dcid, &conn->oscid, + ngtcp2_pkt_hd_init(&hd, hd_flags, type, dcid, scid, pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns), - conn->version, 0); + version, 0); ngtcp2_ppe_init(&ppe, dest, destlen, &cc); @@ -3655,21 +4395,25 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( } lfr.type = NGTCP2_FRAME_PADDING; - switch (fr->type) { - case NGTCP2_FRAME_PATH_CHALLENGE: - case NGTCP2_FRAME_PATH_RESPONSE: - if (!conn->server || destlen >= 1200) { - lfr.padding.len = ngtcp2_ppe_padding(&ppe); - } else { - lfr.padding.len = 0; - } - break; - default: - if (type == NGTCP2_PKT_SHORT) { - lfr.padding.len = - ngtcp2_ppe_padding_size(&ppe, conn_min_short_pktlen(conn)); - } else { - lfr.padding.len = ngtcp2_ppe_padding_hp_sample(&ppe); + if (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) { + lfr.padding.len = ngtcp2_ppe_padding(&ppe); + } else { + switch (fr->type) { + case NGTCP2_FRAME_PATH_CHALLENGE: + case NGTCP2_FRAME_PATH_RESPONSE: + if (!conn->server || destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE) { + lfr.padding.len = ngtcp2_ppe_padding(&ppe); + } else { + lfr.padding.len = 0; + } + break; + default: + if (type == NGTCP2_PKT_1RTT) { + lfr.padding.len = + ngtcp2_ppe_padding_size(&ppe, conn_min_short_pktlen(conn)); + } else { + lfr.padding.len = ngtcp2_ppe_padding_hp_sample(&ppe); + } } } if (lfr.padding.len) { @@ -3683,7 +4427,7 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( return nwrite; } - if (type == NGTCP2_PKT_SHORT) { + if (type == NGTCP2_PKT_1RTT) { ++cc.ckm->use_count; } @@ -3695,35 +4439,57 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( case NGTCP2_FRAME_ACK_ECN: ngtcp2_acktr_commit_ack(&pktns->acktr); ngtcp2_acktr_add_ack(&pktns->acktr, hd.pkt_num, fr->ack.largest_ack); + if (type == NGTCP2_PKT_1RTT) { + conn_handle_unconfirmed_key_update_from_remote(conn, fr->ack.largest_ack, + ts); + } break; } - if (((rtb_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) || padded) && + if (((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) || padded) && (!path || ngtcp2_path_eq(&conn->dcid.current.ps.path, path))) { - if (pi) { - conn_handle_tx_ecn(conn, pi, &rtb_flags, pktns, &hd, ts); + if (pi && !(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE)) { + conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, &hd, ts); } - rv = ngtcp2_rtb_entry_new(&rtbent, &hd, NULL, ts, (size_t)nwrite, rtb_flags, - conn->mem); + rv = ngtcp2_rtb_entry_objalloc_new(&rtbent, &hd, NULL, ts, (size_t)nwrite, + rtb_entry_flags, + &conn->rtb_entry_objalloc); if (rv != 0) { return rv; } rv = conn_on_pkt_sent(conn, &pktns->rtb, rtbent); if (rv != 0) { - ngtcp2_rtb_entry_del(rtbent, conn->mem); + ngtcp2_rtb_entry_objalloc_del(rtbent, &conn->rtb_entry_objalloc, + &conn->frc_objalloc, conn->mem); return rv; } - if ((rtb_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) && - (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE)) { - conn_restart_timer_on_write(conn, ts); + if (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { + if (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE) { + conn_restart_timer_on_write(conn, ts); + } + + if (pktns->rtb.probe_pkt_left && path && + ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { + --pktns->rtb.probe_pkt_left; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "probe pkt size=%td", + nwrite); + } } - } else if (pi && conn->tx.ecn.state == NGTCP2_ECN_STATE_CAPABLE) { + } else if (pi && !(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) && + conn->tx.ecn.state == NGTCP2_ECN_STATE_CAPABLE) { conn_handle_tx_ecn(conn, pi, NULL, pktns, &hd, ts); } + if (path && ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { + conn_update_keep_alive_last_ts(conn, ts); + } + + conn->tx.pacing.pktlen += (size_t)nwrite; + ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); ++pktns->tx.last_pkt_num; @@ -3732,7 +4498,7 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( } /* - * conn_process_early_rtb makes any pending 0RTT packet Short packet. + * conn_process_early_rtb makes any pending 0RTT packet 1RTT packet. */ static void conn_process_early_rtb(ngtcp2_conn *conn) { ngtcp2_rtb_entry *ent; @@ -3748,9 +4514,9 @@ static void conn_process_early_rtb(ngtcp2_conn *conn) { continue; } - /* 0-RTT packet is retransmitted as a Short packet. */ + /* 0-RTT packet is retransmitted as a 1RTT packet. */ ent->hd.flags &= (uint8_t)~NGTCP2_PKT_FLAG_LONG_FORM; - ent->hd.type = NGTCP2_PKT_SHORT; + ent->hd.type = NGTCP2_PKT_1RTT; } } @@ -3763,10 +4529,10 @@ static int conn_handshake_remnants_left(ngtcp2_conn *conn) { ngtcp2_pktns *in_pktns = conn->in_pktns; ngtcp2_pktns *hs_pktns = conn->hs_pktns; - return !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) || - (in_pktns && (in_pktns->rtb.num_retransmittable || + return !conn_is_handshake_completed(conn) || + (in_pktns && (in_pktns->rtb.num_pto_eliciting || ngtcp2_ksl_len(&in_pktns->crypto.tx.frq))) || - (hs_pktns && (hs_pktns->rtb.num_retransmittable || + (hs_pktns && (hs_pktns->rtb.num_pto_eliciting || ngtcp2_ksl_len(&hs_pktns->crypto.tx.frq))); } @@ -3779,13 +4545,20 @@ static int conn_handshake_remnants_left(ngtcp2_conn *conn) { * * NGTCP2_ERR_NOMEM * Out of memory. + * NGTCP2_ERR_CONNECTION_ID_LIMIT + * The number of unacknowledged retirement exceeds the limit. */ static int conn_retire_dcid_seq(ngtcp2_conn *conn, uint64_t seq) { ngtcp2_pktns *pktns = &conn->pktns; ngtcp2_frame_chain *nfrc; int rv; - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + rv = ngtcp2_conn_track_retired_dcid_seq(conn, seq); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); if (rv != 0) { return rv; } @@ -3809,7 +4582,7 @@ static int conn_retire_dcid_seq(ngtcp2_conn *conn, uint64_t seq) { */ static int conn_retire_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, ngtcp2_tstamp ts) { - ngtcp2_ringbuf *rb = &conn->dcid.retired; + ngtcp2_ringbuf *rb = &conn->dcid.retired.rb; ngtcp2_dcid *dest, *stale_dcid; int rv; @@ -3827,7 +4600,7 @@ static int conn_retire_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, dest = ngtcp2_ringbuf_push_back(rb); ngtcp2_dcid_copy(dest, dcid); - dest->ts_retired = ts; + dest->retired_ts = ts; return conn_retire_dcid_seq(conn, dcid->seq); } @@ -3848,20 +4621,19 @@ static int conn_retire_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, */ static int conn_bind_dcid(ngtcp2_conn *conn, ngtcp2_dcid **pdcid, const ngtcp2_path *path, ngtcp2_tstamp ts) { - ngtcp2_pv *pv = conn->pv; ngtcp2_dcid *dcid, *ndcid; ngtcp2_cid cid; size_t i, len; int rv; assert(!ngtcp2_path_eq(&conn->dcid.current.ps.path, path)); - assert(!pv || !ngtcp2_path_eq(&pv->dcid.ps.path, path)); - assert(!pv || !(pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) || - !ngtcp2_path_eq(&pv->fallback_dcid.ps.path, path)); + assert(!conn->pv || !ngtcp2_path_eq(&conn->pv->dcid.ps.path, path)); + assert(!conn->pv || !(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) || + !ngtcp2_path_eq(&conn->pv->fallback_dcid.ps.path, path)); - len = ngtcp2_ringbuf_len(&conn->dcid.bound); + len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i); + dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); if (ngtcp2_path_eq(&dcid->ps.path, path)) { *pdcid = dcid; @@ -3870,41 +4642,128 @@ static int conn_bind_dcid(ngtcp2_conn *conn, ngtcp2_dcid **pdcid, } if (conn->dcid.current.cid.datalen == 0) { - ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound); + ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound.rb); ngtcp2_cid_zero(&cid); ngtcp2_dcid_init(ndcid, ++conn->dcid.zerolen_seq, &cid, NULL); - ngtcp2_path_copy(&ndcid->ps.path, path); + ngtcp2_dcid_set_path(ndcid, path); *pdcid = ndcid; return 0; } - if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { return NGTCP2_ERR_CONN_ID_BLOCKED; } - if (ngtcp2_ringbuf_full(&conn->dcid.bound)) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, 0); + if (ngtcp2_ringbuf_full(&conn->dcid.bound.rb)) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, 0); rv = conn_retire_dcid(conn, dcid, ts); if (rv != 0) { return rv; } } - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); - ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound); + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); + ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound.rb); ngtcp2_dcid_copy(ndcid, dcid); - ngtcp2_path_copy(&ndcid->ps.path, path); + ndcid->bound_ts = ts; + ngtcp2_dcid_set_path(ndcid, path); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); *pdcid = ndcid; return 0; } +static int conn_start_pmtud(ngtcp2_conn *conn) { + int rv; + size_t hard_max_udp_payload_size; + + assert(!conn->local.settings.no_pmtud); + assert(!conn->pmtud); + assert(conn_is_handshake_completed(conn)); + assert(conn->remote.transport_params); + assert(conn->remote.transport_params->max_udp_payload_size >= + NGTCP2_MAX_UDP_PAYLOAD_SIZE); + + hard_max_udp_payload_size = + (size_t)ngtcp2_min(conn->remote.transport_params->max_udp_payload_size, + (uint64_t)conn->local.settings.max_udp_payload_size); + + rv = ngtcp2_pmtud_new(&conn->pmtud, conn->dcid.current.max_udp_payload_size, + hard_max_udp_payload_size, + conn->pktns.tx.last_pkt_num + 1, conn->mem); + if (rv != 0) { + return rv; + } + + if (ngtcp2_pmtud_finished(conn->pmtud)) { + ngtcp2_conn_stop_pmtud(conn); + } + + return 0; +} + +int ngtcp2_conn_start_pmtud(ngtcp2_conn *conn) { + return conn_start_pmtud(conn); +} + +void ngtcp2_conn_stop_pmtud(ngtcp2_conn *conn) { + if (!conn->pmtud) { + return; + } + + ngtcp2_pmtud_del(conn->pmtud); + + conn->pmtud = NULL; +} + +static ngtcp2_ssize conn_write_pmtud_probe(ngtcp2_conn *conn, + ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, ngtcp2_tstamp ts) { + size_t probelen; + ngtcp2_ssize nwrite; + ngtcp2_frame lfr; + + assert(conn->pmtud); + assert(!ngtcp2_pmtud_finished(conn->pmtud)); + + if (!ngtcp2_pmtud_require_probe(conn->pmtud)) { + return 0; + } + + probelen = ngtcp2_pmtud_probelen(conn->pmtud); + if (probelen > destlen) { + return 0; + } + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "sending PMTUD probe packet len=%zu", probelen); + + lfr.type = NGTCP2_FRAME_PING; + + nwrite = ngtcp2_conn_write_single_frame_pkt( + conn, pi, dest, probelen, NGTCP2_PKT_1RTT, + NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING, &conn->dcid.current.cid, &lfr, + NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE, + NULL, ts); + if (nwrite < 0) { + return nwrite; + } + + assert(nwrite); + + ngtcp2_pmtud_probe_sent(conn->pmtud, conn_compute_pto(conn, &conn->pktns), + ts); + + return nwrite; +} + /* * conn_stop_pv stops the path validation which is currently running. * This function does nothing if no path validation is currently being @@ -3948,7 +4807,59 @@ static int conn_stop_pv(ngtcp2_conn *conn, ngtcp2_tstamp ts) { return rv; } -static void conn_reset_congestion_state(ngtcp2_conn *conn); +/* + * conn_abort_pv aborts the current path validation and frees + * resources allocated for it. This function assumes that conn->pv is + * not NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static int conn_abort_pv(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + ngtcp2_pv *pv = conn->pv; + int rv; + + assert(pv); + + if (!(pv->flags & NGTCP2_PV_FLAG_DONT_CARE)) { + rv = conn_call_path_validation(conn, pv, + NGTCP2_PATH_VALIDATION_RESULT_ABORTED); + if (rv != 0) { + return rv; + } + } + + return conn_stop_pv(conn, ts); +} + +static size_t conn_shape_udp_payload(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, + size_t payloadlen) { + if (conn->remote.transport_params && + conn->remote.transport_params->max_udp_payload_size) { + assert(conn->remote.transport_params->max_udp_payload_size >= + NGTCP2_MAX_UDP_PAYLOAD_SIZE); + + payloadlen = + (size_t)ngtcp2_min((uint64_t)payloadlen, + conn->remote.transport_params->max_udp_payload_size); + } + + payloadlen = + ngtcp2_min(payloadlen, conn->local.settings.max_udp_payload_size); + + if (conn->local.settings.no_udp_payload_size_shaping) { + return payloadlen; + } + + return ngtcp2_min(payloadlen, dcid->max_udp_payload_size); +} + +static void conn_reset_congestion_state(ngtcp2_conn *conn, ngtcp2_tstamp ts); /* * conn_on_path_validation_failed is called when path validation @@ -3966,10 +4877,12 @@ static int conn_on_path_validation_failed(ngtcp2_conn *conn, ngtcp2_pv *pv, ngtcp2_tstamp ts) { int rv; - rv = conn_call_path_validation(conn, &pv->dcid.ps.path, - NGTCP2_PATH_VALIDATION_RESULT_FAILURE); - if (rv != 0) { - return rv; + if (!(pv->flags & NGTCP2_PV_FLAG_DONT_CARE)) { + rv = conn_call_path_validation(conn, pv, + NGTCP2_PATH_VALIDATION_RESULT_FAILURE); + if (rv != 0) { + return rv; + } } if (pv->flags & NGTCP2_PV_FLAG_MTU_PROBE) { @@ -3978,45 +4891,12 @@ static int conn_on_path_validation_failed(ngtcp2_conn *conn, ngtcp2_pv *pv, if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) { ngtcp2_dcid_copy(&conn->dcid.current, &pv->fallback_dcid); - conn_reset_congestion_state(conn); + conn_reset_congestion_state(conn, ts); } return conn_stop_pv(conn, ts); } -/* - * dcid_tx_left returns the maximum number of bytes that server is - * allowed to send to an unvalidated path associated to |dcid|. - */ -static size_t dcid_tx_left(ngtcp2_dcid *dcid) { - if (dcid->flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) { - return SIZE_MAX; - } - /* From QUIC spec: Prior to validating the client address, servers - MUST NOT send more than three times as many bytes as the number - of bytes they have received. */ - assert(dcid->bytes_recv * 3 >= dcid->bytes_sent); - - return dcid->bytes_recv * 3 - dcid->bytes_sent; -} - -/* - * conn_server_tx_left returns the maximum number of bytes that server - * is allowed to send to an unvalidated path. - */ -static size_t conn_server_tx_left(ngtcp2_conn *conn, ngtcp2_dcid *dcid) { - assert(conn->server); - - /* If pv->dcid has the current path, use conn->dcid.current. This - is because conn->dcid.current gets update for bytes_recv and - bytes_sent. */ - if (ngtcp2_path_eq(&dcid->ps.path, &conn->dcid.current.ps.path)) { - return dcid_tx_left(&conn->dcid.current); - } - - return dcid_tx_left(dcid); -} - /* * conn_write_path_challenge writes a packet which includes * PATH_CHALLENGE frame into |dest| of length |destlen|. @@ -4034,19 +4914,30 @@ static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_tstamp ts) { - int rv; ngtcp2_ssize nwrite; ngtcp2_tstamp expiry; ngtcp2_pv *pv = conn->pv; ngtcp2_frame lfr; ngtcp2_duration timeout; uint8_t flags; - size_t tx_left; + uint64_t tx_left; + int rv; if (ngtcp2_pv_validation_timed_out(pv, ts)) { ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PTV, "path validation was timed out"); - return conn_on_path_validation_failed(conn, pv, ts); + rv = conn_on_path_validation_failed(conn, pv, ts); + if (rv != 0) { + return rv; + } + + /* We might set path to the one which we just failed validate. + Set it to the current path here. */ + if (path) { + ngtcp2_path_copy(path, &conn->dcid.current.ps.path); + } + + return 0; } ngtcp2_pv_handle_entry_expiry(pv, ts); @@ -4055,12 +4946,9 @@ static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn, return 0; } - assert(conn->callbacks.rand); - rv = conn->callbacks.rand( - lfr.path_challenge.data, sizeof(lfr.path_challenge.data), - &conn->local.settings.rand_ctx, NGTCP2_RAND_USAGE_PATH_CHALLENGE); + rv = conn_call_get_path_challenge_data(conn, lfr.path_challenge.data); if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; + return rv; } lfr.type = NGTCP2_FRAME_PATH_CHALLENGE; @@ -4069,16 +4957,18 @@ static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn, timeout = ngtcp2_max(timeout, 3 * conn->cstat.initial_rtt); expiry = ts + timeout * (1ULL << pv->round); + destlen = ngtcp2_min(destlen, NGTCP2_MAX_UDP_PAYLOAD_SIZE); + if (conn->server) { if (!(pv->dcid.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) { tx_left = conn_server_tx_left(conn, &pv->dcid); - destlen = ngtcp2_min(destlen, tx_left); + destlen = (size_t)ngtcp2_min((uint64_t)destlen, tx_left); if (destlen == 0) { return 0; } } - if (destlen < 1200) { + if (destlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE) { flags = NGTCP2_PV_ENTRY_FLAG_UNDERSIZED; } else { flags = NGTCP2_PV_ENTRY_FLAG_NONE; @@ -4090,8 +4980,10 @@ static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn, ngtcp2_pv_add_entry(pv, lfr.path_challenge.data, expiry, flags, ts); nwrite = ngtcp2_conn_write_single_frame_pkt( - conn, pi, dest, destlen, NGTCP2_PKT_SHORT, &pv->dcid.cid, &lfr, - NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING, &pv->dcid.ps.path, ts); + conn, pi, dest, destlen, NGTCP2_PKT_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE, + &pv->dcid.cid, &lfr, + NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING, + &pv->dcid.ps.path, ts); if (nwrite <= 0) { return nwrite; } @@ -4131,10 +5023,10 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn, ngtcp2_frame lfr; ngtcp2_ssize nwrite; int rv; - size_t tx_left; + uint64_t tx_left; - for (; ngtcp2_ringbuf_len(&conn->rx.path_challenge);) { - pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge, 0); + for (; ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb);) { + pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge.rb, 0); if (ngtcp2_path_eq(&conn->dcid.current.ps.path, &pcent->ps.path)) { /* Send PATH_RESPONSE from conn_write_pkt. */ @@ -4159,7 +5051,7 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn, /* Client does not expect to respond to path validation against unknown path */ - ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge); + ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge.rb); pcent = NULL; } @@ -4181,9 +5073,11 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn, } } + destlen = ngtcp2_min(destlen, NGTCP2_MAX_UDP_PAYLOAD_SIZE); + if (conn->server && !(dcid->flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) { tx_left = conn_server_tx_left(conn, dcid); - destlen = ngtcp2_min(destlen, tx_left); + destlen = (size_t)ngtcp2_min((uint64_t)destlen, tx_left); if (destlen == 0) { return 0; } @@ -4193,8 +5087,9 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn, memcpy(lfr.path_response.data, pcent->data, sizeof(lfr.path_response.data)); nwrite = ngtcp2_conn_write_single_frame_pkt( - conn, pi, dest, destlen, NGTCP2_PKT_SHORT, &dcid->cid, &lfr, - NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING, &pcent->ps.path, ts); + conn, pi, dest, destlen, NGTCP2_PKT_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE, + &dcid->cid, &lfr, NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING, &pcent->ps.path, + ts); if (nwrite <= 0) { return nwrite; } @@ -4203,21 +5098,23 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn, ngtcp2_path_copy(path, &pcent->ps.path); } - ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge); + ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge.rb); dcid->bytes_sent += (uint64_t)nwrite; return nwrite; } -ngtcp2_ssize ngtcp2_conn_write_pkt(ngtcp2_conn *conn, ngtcp2_path *path, - ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, ngtcp2_tstamp ts) { - return ngtcp2_conn_writev_stream(conn, path, pi, dest, destlen, - /* pdatalen = */ NULL, - NGTCP2_WRITE_STREAM_FLAG_NONE, - /* stream_id = */ -1, - /* datav = */ NULL, /* datavcnt = */ 0, ts); +ngtcp2_ssize ngtcp2_conn_write_pkt_versioned(ngtcp2_conn *conn, + ngtcp2_path *path, + int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, ngtcp2_tstamp ts) { + return ngtcp2_conn_writev_stream_versioned( + conn, path, pkt_info_version, pi, dest, destlen, + /* pdatalen = */ NULL, NGTCP2_WRITE_STREAM_FLAG_NONE, + /* stream_id = */ -1, + /* datav = */ NULL, /* datavcnt = */ 0, ts); } /* @@ -4250,6 +5147,12 @@ static int conn_on_version_negotiation(ngtcp2_conn *conn, return NGTCP2_ERR_INVALID_ARGUMENT; } + /* Version Negotiation packet is ignored if client has reacted upon + Version Negotiation packet. */ + if (conn->local.settings.original_version != conn->client_chosen_version) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + if (payloadlen > sizeof(sv)) { p = ngtcp2_mem_malloc(conn->mem, payloadlen); if (p == NULL) { @@ -4263,29 +5166,24 @@ static int conn_on_version_negotiation(ngtcp2_conn *conn, ngtcp2_log_rx_vn(&conn->log, hd, p, nsv); - for (i = 0; i < nsv; ++i) { - if (p[i] == conn->version) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "ignore Version Negotiation because it contains version " - "selected by client"); + ngtcp2_qlog_version_negotiation_pkt_received(&conn->qlog, hd, p, nsv); - rv = NGTCP2_ERR_INVALID_ARGUMENT; - goto fin; - } - } + if (!ngtcp2_is_reserved_version(conn->local.settings.original_version)) { + for (i = 0; i < nsv; ++i) { + if (p[i] == conn->local.settings.original_version) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "ignore Version Negotiation because it contains the " + "original version"); - if (conn->callbacks.recv_version_negotiation) { - rv = conn->callbacks.recv_version_negotiation(conn, hd, p, nsv, - conn->user_data); - if (rv != 0) { - rv = NGTCP2_ERR_CALLBACK_FAILURE; + rv = NGTCP2_ERR_INVALID_ARGUMENT; + goto fin; + } } } - if (rv == 0) { - /* TODO Just move to the terminal state for now in order not to - send CONNECTION_CLOSE frame. */ - conn->state = NGTCP2_CS_DRAINING; + rv = conn_call_recv_version_negotiation(conn, hd, p, nsv); + if (rv != 0) { + goto fin; } fin: @@ -4325,6 +5223,7 @@ int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_stream *sfr; ngtcp2_strm *strm; int rv; + int streamfrq_empty; if (*pfrc == NULL) { return 0; @@ -4341,12 +5240,13 @@ int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns, strm = ngtcp2_conn_find_stream(conn, sfr->stream_id); if (!strm) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); break; } + streamfrq_empty = ngtcp2_strm_streamfrq_empty(strm); rv = ngtcp2_strm_streamfrq_push(strm, frc); if (rv != 0) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } if (!ngtcp2_strm_is_tx_queued(strm)) { @@ -4356,6 +5256,9 @@ int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns, return rv; } } + if (streamfrq_empty) { + ++conn->tx.strmq_nretrans; + } break; case NGTCP2_FRAME_CRYPTO: frc = *pfrc; @@ -4367,7 +5270,7 @@ int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns, &frc->fr.crypto.offset, frc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } break; @@ -4401,7 +5304,8 @@ int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns, * ODCID does not match; or Token is empty. */ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, - size_t hdpktlen, const uint8_t *pkt, size_t pktlen) { + size_t hdpktlen, const uint8_t *pkt, size_t pktlen, + ngtcp2_tstamp ts) { int rv; ngtcp2_pkt_retry retry; ngtcp2_pktns *in_pktns = conn->in_pktns; @@ -4424,7 +5328,7 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, retry.odcid = conn->dcid.current.cid; rv = ngtcp2_pkt_verify_retry_tag( - conn->version, &retry, pkt, pktlen, conn->callbacks.encrypt, + conn->client_chosen_version, &retry, pkt, pktlen, conn->callbacks.encrypt, &conn->crypto.retry_aead, &conn->crypto.retry_aead_ctx); if (rv != 0) { ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, @@ -4444,7 +5348,7 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, return 0; } - ngtcp2_qlog_retry_pkt_received(&conn->qlog, hd); + ngtcp2_qlog_retry_pkt_received(&conn->qlog, hd, &retry); /* DCID must be updated before invoking callback because client generates new initial keys there. */ @@ -4453,11 +5357,9 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, conn->flags |= NGTCP2_CONN_FLAG_RECV_RETRY; - assert(conn->callbacks.recv_retry); - - rv = conn->callbacks.recv_retry(conn, hd, conn->user_data); + rv = conn_call_recv_retry(conn, hd); if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; + return rv; } conn->state = NGTCP2_CS_CLIENT_INITIAL; @@ -4489,7 +5391,7 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, ngtcp2_cpymem(token->base, retry.token.base, retry.token.len); reset_conn_stat_recovery(&conn->cstat); - conn_reset_congestion_state(conn); + conn_reset_congestion_state(conn, ts); conn_reset_ecn_validation_state(conn); return 0; @@ -4497,13 +5399,7 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, int ngtcp2_conn_detect_lost_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) { - ngtcp2_duration pto = conn_compute_pto(conn, pktns); - int rv = ngtcp2_rtb_detect_lost_pkt(&pktns->rtb, conn, pktns, cstat, pto, ts); - if (rv != 0) { - return rv; - } - - return 0; + return ngtcp2_rtb_detect_lost_pkt(&pktns->rtb, conn, pktns, cstat, ts); } /* @@ -4547,7 +5443,7 @@ static int conn_recv_ack(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_ack *fr, if (num_acked < 0) { /* TODO assert this */ assert(ngtcp2_err_is_fatal((int)num_acked)); - ngtcp2_frame_chain_list_del(frc, conn->mem); + ngtcp2_frame_chain_list_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return (int)num_acked; } @@ -4555,11 +5451,6 @@ static int conn_recv_ack(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_ack *fr, return 0; } - rv = ngtcp2_conn_detect_lost_pkt(conn, pktns, &conn->cstat, ts); - if (rv != 0) { - return rv; - } - pktns->rtb.probe_pkt_left = 0; if (cstat->pto_count && @@ -4644,13 +5535,18 @@ static int conn_recv_max_stream_data(ngtcp2_conn *conn, return 0; } - strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc); if (strm == NULL) { return NGTCP2_ERR_NOMEM; } rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL); if (rv != 0) { - ngtcp2_mem_free(conn->mem, strm); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); + return rv; + } + + rv = conn_call_stream_open(conn, strm); + if (rv != 0) { return rv; } } @@ -4769,22 +5665,22 @@ static int conn_ensure_decrypt_buffer(ngtcp2_conn *conn, size_t n) { /* * decrypt_pkt decrypts the data pointed by |payload| whose length is * |payloadlen|, and writes plaintext data to the buffer pointed by - * |dest|. The buffer pointed by |ad| is the Additional Data, and its - * length is |adlen|. |pkt_num| is used to create a nonce. |ckm| is - * the cryptographic key, and iv to use. |decrypt| is a callback - * function which actually decrypts a packet. + * |dest|. The buffer pointed by |aad| is the Additional + * Authenticated Data, and its length is |aadlen|. |pkt_num| is used + * to create a nonce. |ckm| is the cryptographic key, and iv to use. + * |decrypt| is a callback function which actually decrypts a packet. * * This function returns the number of bytes written in |dest| if it * succeeds, or one of the following negative error codes: * * NGTCP2_ERR_CALLBACK_FAILURE * User callback failed. - * NGTCP2_ERR_TLS_DECRYPT - * TLS backend failed to decrypt data. + * NGTCP2_ERR_DECRYPT + * Failed to decrypt a packet. */ static ngtcp2_ssize decrypt_pkt(uint8_t *dest, const ngtcp2_crypto_aead *aead, const uint8_t *payload, size_t payloadlen, - const uint8_t *ad, size_t adlen, + const uint8_t *aad, size_t aadlen, int64_t pkt_num, ngtcp2_crypto_km *ckm, ngtcp2_decrypt decrypt) { /* TODO nonce is limited to 64 bytes. */ @@ -4796,10 +5692,10 @@ static ngtcp2_ssize decrypt_pkt(uint8_t *dest, const ngtcp2_crypto_aead *aead, ngtcp2_crypto_create_nonce(nonce, ckm->iv.base, ckm->iv.len, pkt_num); rv = decrypt(dest, aead, &ckm->aead_ctx, payload, payloadlen, nonce, - ckm->iv.len, ad, adlen); + ckm->iv.len, aad, aadlen); if (rv != 0) { - if (rv == NGTCP2_ERR_TLS_DECRYPT) { + if (rv == NGTCP2_ERR_DECRYPT) { return rv; } return NGTCP2_ERR_CALLBACK_FAILURE; @@ -4825,20 +5721,17 @@ static ngtcp2_ssize decrypt_pkt(uint8_t *dest, const ngtcp2_crypto_aead *aead, * User-defined callback function failed; or it does not return * expected result. */ -static ngtcp2_ssize decrypt_hp(ngtcp2_pkt_hd *hd, uint8_t *dest, - const ngtcp2_crypto_cipher *hp, - const uint8_t *pkt, size_t pktlen, - size_t pkt_num_offset, ngtcp2_crypto_km *ckm, - const ngtcp2_crypto_cipher_ctx *hp_ctx, - ngtcp2_hp_mask hp_mask) { +static ngtcp2_ssize +decrypt_hp(ngtcp2_pkt_hd *hd, uint8_t *dest, const ngtcp2_crypto_cipher *hp, + const uint8_t *pkt, size_t pktlen, size_t pkt_num_offset, + const ngtcp2_crypto_cipher_ctx *hp_ctx, ngtcp2_hp_mask hp_mask) { size_t sample_offset; uint8_t *p = dest; - uint8_t mask[NGTCP2_HP_MASKLEN]; + uint8_t mask[NGTCP2_HP_SAMPLELEN]; size_t i; int rv; assert(hp_mask); - assert(ckm); if (pkt_num_offset + 4 + NGTCP2_HP_SAMPLELEN > pktlen) { return NGTCP2_ERR_PROTO; @@ -4921,40 +5814,65 @@ static int conn_emit_pending_crypto_data(ngtcp2_conn *conn, * conn_recv_connection_close is called when CONNECTION_CLOSE or * APPLICATION_CLOSE frame is received. */ -static void conn_recv_connection_close(ngtcp2_conn *conn, - ngtcp2_connection_close *fr) { +static int conn_recv_connection_close(ngtcp2_conn *conn, + ngtcp2_connection_close *fr) { + ngtcp2_connection_close_error *ccerr = &conn->rx.ccerr; + conn->state = NGTCP2_CS_DRAINING; if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) { - conn->rx.ccec.type = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT; + ccerr->type = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT; } else { - conn->rx.ccec.type = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION; + ccerr->type = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION; + } + ccerr->error_code = fr->error_code; + ccerr->frame_type = fr->frame_type; + + if (!fr->reasonlen) { + ccerr->reasonlen = 0; + + return 0; + } + + if (ccerr->reason == NULL) { + ccerr->reason = ngtcp2_mem_malloc( + conn->mem, NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN); + if (ccerr->reason == NULL) { + return NGTCP2_ERR_NOMEM; + } } - conn->rx.ccec.error_code = fr->error_code; + + ccerr->reasonlen = + ngtcp2_min(fr->reasonlen, NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN); + ngtcp2_cpymem(ccerr->reason, fr->reason, ccerr->reasonlen); + + return 0; } static void conn_recv_path_challenge(ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_path_challenge *fr) { ngtcp2_path_challenge_entry *ent; - /* client only responds to PATH_CHALLENGE from the current path. */ - if (!conn->server && !ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { - ngtcp2_log_info( - &conn->log, NGTCP2_LOG_EVENT_CON, - "discard PATH_CHALLENGE from the path which is not current"); + /* client only responds to PATH_CHALLENGE from the current path or + path which client is migrating to. */ + if (!conn->server && !ngtcp2_path_eq(&conn->dcid.current.ps.path, path) && + (!conn->pv || !ngtcp2_path_eq(&conn->pv->dcid.ps.path, path))) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "discard PATH_CHALLENGE from the path which is not current " + "or endpoint is migrating to"); return; } - ent = ngtcp2_ringbuf_push_front(&conn->rx.path_challenge); + ent = ngtcp2_ringbuf_push_front(&conn->rx.path_challenge.rb); ngtcp2_path_challenge_entry_init(ent, path, fr->data); } /* * conn_reset_congestion_state resets congestion state. */ -static void conn_reset_congestion_state(ngtcp2_conn *conn) { +static void conn_reset_congestion_state(ngtcp2_conn *conn, ngtcp2_tstamp ts) { conn_reset_conn_stat_cc(conn, &conn->cstat); - conn->cc.reset(&conn->cc); + conn->cc.reset(&conn->cc, &conn->cstat, ts); if (conn->hs_pktns) { ngtcp2_rtb_reset_cc_state(&conn->hs_pktns->rtb, @@ -4962,6 +5880,8 @@ static void conn_reset_congestion_state(ngtcp2_conn *conn) { } ngtcp2_rtb_reset_cc_state(&conn->pktns.rtb, conn->pktns.tx.last_pkt_num + 1); ngtcp2_rst_init(&conn->rst); + + conn->tx.pacing.next_ts = UINT64_MAX; } static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, @@ -4977,9 +5897,8 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, rv = ngtcp2_pv_validate(pv, &ent_flags, fr->data); if (rv != 0) { - if (rv == NGTCP2_ERR_PATH_VALIDATION_FAILED) { - return conn_on_path_validation_failed(conn, pv, ts); - } + assert(!ngtcp2_err_is_fatal(rv)); + return 0; } @@ -4990,11 +5909,11 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, rv = conn_retire_dcid(conn, &conn->dcid.current, ts); if (rv != 0) { - goto fail; + return rv; } ngtcp2_dcid_copy(&conn->dcid.current, &pv->dcid); } - conn_reset_congestion_state(conn); + conn_reset_congestion_state(conn, ts); conn_reset_ecn_validation_state(conn); } @@ -5002,10 +5921,21 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, conn->dcid.current.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED; } - rv = conn_call_path_validation(conn, &pv->dcid.ps.path, + rv = conn_call_path_validation(conn, pv, NGTCP2_PATH_VALIDATION_RESULT_SUCCESS); if (rv != 0) { - goto fail; + return rv; + } + + if (!conn->local.settings.no_pmtud) { + ngtcp2_conn_stop_pmtud(conn); + + if (!(pv->flags & NGTCP2_PV_ENTRY_FLAG_UNDERSIZED)) { + rv = conn_start_pmtud(conn); + if (rv != 0) { + return rv; + } + } } } @@ -5022,7 +5952,7 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, NGTCP2_PV_FLAG_MTU_PROBE, &conn->log, conn->mem); if (rv != 0) { - goto fail; + return rv; } npv->dcid.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED; @@ -5032,7 +5962,7 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, rv = ngtcp2_pv_new(&npv, &pv->fallback_dcid, timeout, NGTCP2_PV_FLAG_DONT_CARE, &conn->log, conn->mem); if (rv != 0) { - goto fail; + return rv; } } @@ -5051,7 +5981,6 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, return 0; } -fail: return conn_stop_pv(conn, ts); } @@ -5141,6 +6070,33 @@ static void pktns_increase_ecn_counts(ngtcp2_pktns *pktns, } } +/* + * vneg_other_versions_includes returns nonzero if |other_versions| of + * length |other_versionslen| includes |version|. |other_versions| is + * the wire image of other_versions field of version_information + * transport parameter, and each version is encoded in network byte + * order. + */ +static int vneg_other_versions_includes(const uint8_t *other_versions, + size_t other_versionslen, + uint32_t version) { + size_t i; + + assert(!(other_versionslen & 0x3)); + + if (other_versionslen == 0) { + return 0; + } + + for (i = 0; i < other_versionslen; i += sizeof(uint32_t)) { + if (version == ngtcp2_get_uint32(&other_versions[i])) { + return 1; + } + } + + return 0; +} + static int conn_recv_crypto(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, ngtcp2_strm *strm, const ngtcp2_crypto *fr); @@ -5217,13 +6173,17 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, if (!(pkt[0] & NGTCP2_HEADER_FORM_BIT)) { if (conn->state == NGTCP2_CS_SERVER_INITIAL) { - /* Ignore Short packet unless server's first Handshake packet - has been transmitted. */ + /* Ignore 1RTT packet unless server's first Handshake packet has + been transmitted. */ return (ngtcp2_ssize)pktlen; } + if (conn->pktns.crypto.rx.ckm) { + return 0; + } + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, - "buffering Short packet len=%zu", pktlen); + "buffering 1RTT packet len=%zu", pktlen); rv = conn_buffer_pkt(conn, &conn->pktns, path, pi, pkt, pktlen, dgramlen, ts); @@ -5239,8 +6199,7 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return NGTCP2_ERR_DISCARD_PKT; } - switch (hd.type) { - case NGTCP2_PKT_VERSION_NEGOTIATION: + if (hd.type == NGTCP2_PKT_VERSION_NEGOTIATION) { hdpktlen = (size_t)nread; ngtcp2_log_rx_pkt_hd(&conn->log, &hd); @@ -5276,7 +6235,7 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return NGTCP2_ERR_DISCARD_PKT; } return NGTCP2_ERR_RECV_VERSION_NEGOTIATION; - case NGTCP2_PKT_RETRY: + } else if (hd.type == NGTCP2_PKT_RETRY) { hdpktlen = (size_t)nread; ngtcp2_log_rx_pkt_hd(&conn->log, &hd); @@ -5291,7 +6250,11 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return NGTCP2_ERR_DISCARD_PKT; } - rv = conn_on_retry(conn, &hd, hdpktlen, pkt, pktlen); + if (conn->client_chosen_version != hd.version) { + return NGTCP2_ERR_DISCARD_PKT; + } + + rv = conn_on_retry(conn, &hd, hdpktlen, pkt, pktlen, ts); if (rv != 0) { if (ngtcp2_err_is_fatal(rv)) { return rv; @@ -5307,7 +6270,18 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, pktlen = (size_t)nread + hd.len; - if (conn->version != hd.version) { + if (!ngtcp2_is_supported_version(hd.version)) { + return NGTCP2_ERR_DISCARD_PKT; + } + + if (conn->server) { + if (hd.version != conn->client_chosen_version && + (!conn->negotiated_version || hd.version != conn->negotiated_version)) { + return NGTCP2_ERR_DISCARD_PKT; + } + } else if (hd.version != conn->client_chosen_version && + conn->negotiated_version && + hd.version != conn->negotiated_version) { return NGTCP2_ERR_DISCARD_PKT; } @@ -5326,6 +6300,11 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, if (!conn->server) { return NGTCP2_ERR_DISCARD_PKT; } + + if (hd.version != conn->client_chosen_version) { + return NGTCP2_ERR_DISCARD_PKT; + } + if (conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) { if (conn->early.ckm) { ngtcp2_ssize nread2; @@ -5364,6 +6343,14 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, assert(conn->in_pktns); if (conn->server) { + if (dgramlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE) { + ngtcp2_log_info( + &conn->log, NGTCP2_LOG_EVENT_PKT, + "Initial packet was ignored because it is included in UDP datagram " + "less than %zu bytes: %zu bytes", + NGTCP2_MAX_UDP_PAYLOAD_SIZE, dgramlen); + return NGTCP2_ERR_DISCARD_PKT; + } if (conn->local.settings.token.len) { rv = verify_token(&conn->local.settings.token, &hd); if (rv != 0) { @@ -5384,18 +6371,55 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return rv; } } - } else if (hd.token.len != 0) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "packet was ignored because token is not empty"); - return NGTCP2_ERR_DISCARD_PKT; + } else { + if (hd.token.len != 0) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because token is not empty"); + return NGTCP2_ERR_DISCARD_PKT; + } + + if (hd.version != conn->client_chosen_version && + !conn->negotiated_version && conn->vneg.version != hd.version) { + if (!vneg_other_versions_includes(conn->vneg.other_versions, + conn->vneg.other_versionslen, + hd.version)) { + return NGTCP2_ERR_DISCARD_PKT; + } + + /* Install new Initial keys using QUIC version = hd.version */ + rv = conn_call_version_negotiation( + conn, hd.version, + (conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) + ? &conn->dcid.current.cid + : &conn->rcid); + if (rv != 0) { + return rv; + } + + assert(conn->vneg.version == hd.version); + } } pktns = conn->in_pktns; crypto = &pktns->crypto.strm; crypto_level = NGTCP2_CRYPTO_LEVEL_INITIAL; + if (hd.version == conn->client_chosen_version) { + ckm = pktns->crypto.rx.ckm; + hp_ctx = &pktns->crypto.rx.hp_ctx; + } else { + assert(conn->vneg.version == hd.version); + + ckm = conn->vneg.rx.ckm; + hp_ctx = &conn->vneg.rx.hp_ctx; + } + break; case NGTCP2_PKT_HANDSHAKE: + if (hd.version != conn->negotiated_version) { + return NGTCP2_ERR_DISCARD_PKT; + } + if (!conn->hs_pktns->crypto.rx.ckm) { if (conn->server) { ngtcp2_log_info( @@ -5418,6 +6442,8 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, pktns = conn->hs_pktns; crypto = &pktns->crypto.strm; crypto_level = NGTCP2_CRYPTO_LEVEL_HANDSHAKE; + ckm = pktns->crypto.rx.ckm; + hp_ctx = &pktns->crypto.rx.hp_ctx; break; default: @@ -5431,8 +6457,6 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, decrypt = conn->callbacks.decrypt; aead = &pktns->crypto.ctx.aead; hp = &pktns->crypto.ctx.hp; - ckm = pktns->crypto.rx.ckm; - hp_ctx = &pktns->crypto.rx.hp_ctx; assert(ckm); assert(hp_mask); @@ -5444,7 +6468,7 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, } nwrite = decrypt_hp(&hd, conn->crypto.decrypt_hp_buf.base, hp, pkt, pktlen, - (size_t)nread, ckm, hp_ctx, hp_mask); + (size_t)nread, hp_ctx, hp_mask); if (nwrite < 0) { if (ngtcp2_err_is_fatal((int)nwrite)) { return nwrite; @@ -5505,6 +6529,15 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return NGTCP2_ERR_PROTO; } + if (!conn->server && hd.version != conn->client_chosen_version && + !conn->negotiated_version) { + conn->negotiated_version = hd.version; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "the negotiated version is 0x%08x", + conn->negotiated_version); + } + payload = conn->crypto.decrypt_buf.base; payloadlen = (size_t)nwrite; @@ -5589,6 +6622,15 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, case NGTCP2_FRAME_PADDING: break; case NGTCP2_FRAME_CRYPTO: + if (!conn->server && !conn->negotiated_version && + ngtcp2_vec_len(fr->crypto.data, fr->crypto.datacnt)) { + conn->negotiated_version = hd.version; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "the negotiated version is 0x%08x", + conn->negotiated_version); + } + rv = conn_recv_crypto(conn, crypto_level, crypto, &fr->crypto); if (rv != 0) { return rv; @@ -5596,7 +6638,10 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, require_ack = 1; break; case NGTCP2_FRAME_CONNECTION_CLOSE: - conn_recv_connection_close(conn, &fr->connection_close); + rv = conn_recv_connection_close(conn, &fr->connection_close); + if (rv != 0) { + return rv; + } break; case NGTCP2_FRAME_PING: require_ack = 1; @@ -5645,21 +6690,37 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, : (ngtcp2_ssize)pktlen; } +static int is_unrecoverable_error(int liberr) { + switch (liberr) { + case NGTCP2_ERR_CRYPTO: + case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM: + case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM: + case NGTCP2_ERR_TRANSPORT_PARAM: + case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE: + return 1; + } + + return 0; +} + /* * conn_recv_handshake_cpkt processes compound packet during * handshake. The buffer pointed by |pkt| might contain multiple - * packets. The Short packet must be the last one because it does not + * packets. The 1RTT packet must be the last one because it does not * have payload length field. * * This function returns the same error code returned by * conn_recv_handshake_pkt. */ -static int conn_recv_handshake_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path, - const ngtcp2_pkt_info *pi, - const uint8_t *pkt, size_t pktlen, - ngtcp2_tstamp ts) { +static ngtcp2_ssize conn_recv_handshake_cpkt(ngtcp2_conn *conn, + const ngtcp2_path *path, + const ngtcp2_pkt_info *pi, + const uint8_t *pkt, size_t pktlen, + ngtcp2_tstamp ts) { ngtcp2_ssize nread; size_t dgramlen = pktlen; + const uint8_t *origpkt = pkt; + uint32_t version; if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { conn->dcid.current.bytes_recv += dgramlen; @@ -5670,53 +6731,55 @@ static int conn_recv_handshake_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path, conn_recv_handshake_pkt(conn, path, pi, pkt, pktlen, dgramlen, ts, ts); if (nread < 0) { if (ngtcp2_err_is_fatal((int)nread)) { - return (int)nread; + return nread; } if (nread == NGTCP2_ERR_DRAINING) { return NGTCP2_ERR_DRAINING; } - if ((pkt[0] & NGTCP2_HEADER_FORM_BIT) && - /* Not a Version Negotiation packet */ - pktlen > 4 && ngtcp2_get_uint32(&pkt[1]) > 0 && - ngtcp2_pkt_get_type_long(pkt[0]) == NGTCP2_PKT_INITIAL) { - if (conn->server) { - /* If server discards first Initial, then drop connection - state. This is because SCID in packet might be corrupted - and the current connection state might wrongly discard - valid packet and prevent the handshake from - completing. */ - if (conn->in_pktns && conn->in_pktns->rx.max_pkt_num == -1) { - /* If this is crypto related error, then return normally - in order to send CONNECTION_CLOSE with TLS alert (e.g., - no_application_protocol). */ - switch (nread) { - case NGTCP2_ERR_CRYPTO: - case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM: - case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM: - case NGTCP2_ERR_TRANSPORT_PARAM: - return (int)nread; + if ((pkt[0] & NGTCP2_HEADER_FORM_BIT) && pktlen > 4) { + /* Not a Version Negotiation packet */ + version = ngtcp2_get_uint32(&pkt[1]); + if (ngtcp2_pkt_get_type_long(version, pkt[0]) == NGTCP2_PKT_INITIAL) { + if (conn->server) { + if (is_unrecoverable_error((int)nread)) { + /* If server gets crypto error from TLS stack, it is + unrecoverable, therefore drop connection. */ + return nread; + } + + /* If server discards first Initial, then drop connection + state. This is because SCID in packet might be corrupted + and the current connection state might wrongly discard + valid packet and prevent the handshake from + completing. */ + if (conn->in_pktns && conn->in_pktns->rx.max_pkt_num == -1) { + return NGTCP2_ERR_DROP_CONN; } - return NGTCP2_ERR_DROP_CONN; + return (ngtcp2_ssize)dgramlen; } - return 0; - } - /* client */ - if (nread == NGTCP2_ERR_CRYPTO) { - /* If client gets crypto error from TLS stack, it is - unrecoverable, therefore drop connection. */ - return (int)nread; + /* client */ + if (is_unrecoverable_error((int)nread)) { + /* If client gets crypto error from TLS stack, it is + unrecoverable, therefore drop connection. */ + return nread; + } + return (ngtcp2_ssize)dgramlen; } - return 0; } if (nread == NGTCP2_ERR_DISCARD_PKT) { - return 0; + return (ngtcp2_ssize)dgramlen; } - return (int)nread; + return nread; + } + + if (nread == 0) { + assert(!(pkt[0] & NGTCP2_HEADER_FORM_BIT)); + return pkt - origpkt; } assert(pktlen >= (size_t)nread); @@ -5727,7 +6790,7 @@ static int conn_recv_handshake_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path, "read packet %td left %zu", nread, pktlen); } - return 0; + return (ngtcp2_ssize)dgramlen; } int ngtcp2_conn_init_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, @@ -5737,45 +6800,39 @@ int ngtcp2_conn_init_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, uint64_t max_tx_offset; int local_stream = conn_local_stream(conn, stream_id); + assert(conn->remote.transport_params); + if (bidi_stream(stream_id)) { if (local_stream) { max_rx_offset = conn->local.transport_params.initial_max_stream_data_bidi_local; max_tx_offset = - conn->remote.transport_params.initial_max_stream_data_bidi_remote; + conn->remote.transport_params->initial_max_stream_data_bidi_remote; } else { max_rx_offset = conn->local.transport_params.initial_max_stream_data_bidi_remote; max_tx_offset = - conn->remote.transport_params.initial_max_stream_data_bidi_local; + conn->remote.transport_params->initial_max_stream_data_bidi_local; } } else if (local_stream) { max_rx_offset = 0; - max_tx_offset = conn->remote.transport_params.initial_max_stream_data_uni; + max_tx_offset = conn->remote.transport_params->initial_max_stream_data_uni; } else { max_rx_offset = conn->local.transport_params.initial_max_stream_data_uni; max_tx_offset = 0; } - rv = ngtcp2_strm_init(strm, stream_id, NGTCP2_STRM_FLAG_NONE, max_rx_offset, - max_tx_offset, stream_user_data, conn->mem); - if (rv != 0) { - return rv; - } + ngtcp2_strm_init(strm, stream_id, NGTCP2_STRM_FLAG_NONE, max_rx_offset, + max_tx_offset, stream_user_data, &conn->frc_objalloc, + conn->mem); - rv = ngtcp2_map_insert(&conn->strms, &strm->me); + rv = ngtcp2_map_insert(&conn->strms, (ngtcp2_map_key_type)strm->stream_id, + strm); if (rv != 0) { assert(rv != NGTCP2_ERR_INVALID_ARGUMENT); goto fail; } - if (!conn_local_stream(conn, stream_id)) { - rv = conn_call_stream_open(conn, strm); - if (rv != 0) { - goto fail; - } - } - return 0; fail: @@ -5806,7 +6863,7 @@ static int conn_emit_pending_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm, int rv; uint64_t offset; uint32_t sdflags; - int handshake_completed = conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED; + int handshake_completed = conn_is_handshake_completed(conn); if (!strm->rx.rob) { return 0; @@ -5816,7 +6873,7 @@ static int conn_emit_pending_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm, /* Stop calling callback if application has called ngtcp2_conn_shutdown_stream_read() inside the callback. Because it doubly counts connection window. */ - if (strm->flags & (NGTCP2_STRM_FLAG_STOP_SENDING)) { + if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) { return 0; } @@ -5987,7 +7044,7 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { uint64_t rx_offset, fr_end_offset; int local_stream; int bidi; - size_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt); + uint64_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt); uint32_t sdflags = NGTCP2_STREAM_DATA_FLAG_NONE; local_stream = conn_local_stream(conn, fr->stream_id); @@ -6038,16 +7095,22 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { return 0; } - strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc); if (strm == NULL) { return NGTCP2_ERR_NOMEM; } /* TODO Perhaps, call new_stream callback? */ rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL); if (rv != 0) { - ngtcp2_mem_free(conn->mem, strm); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); + return rv; + } + + rv = conn_call_stream_open(conn, strm); + if (rv != 0) { return rv; } + if (!bidi) { ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_WR); } @@ -6081,8 +7144,7 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { return NGTCP2_ERR_FINAL_SIZE; } - if (strm->flags & - (NGTCP2_STRM_FLAG_STOP_SENDING | NGTCP2_STRM_FLAG_RECV_RST)) { + if (strm->flags & NGTCP2_STRM_FLAG_RECV_RST) { return 0; } @@ -6095,11 +7157,6 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { strm->rx.last_offset = fr_end_offset; ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_RD); - - if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) { - return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, - strm->app_error_code); - } } } else { if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && @@ -6113,8 +7170,7 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { return 0; } - if (strm->flags & - (NGTCP2_STRM_FLAG_STOP_SENDING | NGTCP2_STRM_FLAG_RECV_RST)) { + if (strm->flags & NGTCP2_STRM_FLAG_RECV_RST) { return 0; } } @@ -6139,6 +7195,10 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { datalen = 0; } + if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) { + return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm); + } + fin = (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && rx_offset == strm->rx.last_offset; @@ -6146,11 +7206,11 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { if (fin) { sdflags |= NGTCP2_STREAM_DATA_FLAG_FIN; } - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + if (!conn_is_handshake_completed(conn)) { sdflags |= NGTCP2_STREAM_DATA_FLAG_EARLY; } rv = conn_call_recv_stream_data(conn, strm, sdflags, offset, data, - datalen); + (size_t)datalen); if (rv != 0) { return rv; } @@ -6167,7 +7227,7 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { return rv; } } - return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, NGTCP2_NO_ERROR); + return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm); } /* @@ -6186,7 +7246,7 @@ static int conn_reset_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, ngtcp2_frame_chain *frc; ngtcp2_pktns *pktns = &conn->pktns; - rv = ngtcp2_frame_chain_new(&frc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&frc, &conn->frc_objalloc); if (rv != 0) { return rv; } @@ -6219,7 +7279,7 @@ static int conn_stop_sending(ngtcp2_conn *conn, ngtcp2_strm *strm, ngtcp2_frame_chain *frc; ngtcp2_pktns *pktns = &conn->pktns; - rv = ngtcp2_frame_chain_new(&frc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&frc, &conn->frc_objalloc); if (rv != 0) { return rv; } @@ -6367,26 +7427,31 @@ static int conn_recv_reset_stream(ngtcp2_conn *conn, return NGTCP2_ERR_FINAL_SIZE; } + if (strm->flags & NGTCP2_STRM_FLAG_RECV_RST) { + return 0; + } + + if (strm->rx.max_offset < fr->final_size) { + return NGTCP2_ERR_FLOW_CONTROL; + } + datalen = fr->final_size - strm->rx.last_offset; - if (strm->rx.max_offset < fr->final_size || - conn_max_data_violated(conn, datalen)) { + if (conn_max_data_violated(conn, datalen)) { return NGTCP2_ERR_FLOW_CONTROL; } - if (!(strm->flags & NGTCP2_STRM_FLAG_RECV_RST)) { - rv = conn_call_stream_reset(conn, fr->stream_id, fr->final_size, - fr->app_error_code, strm->stream_user_data); - if (rv != 0) { - return rv; - } + rv = conn_call_stream_reset(conn, fr->stream_id, fr->final_size, + fr->app_error_code, strm->stream_user_data); + if (rv != 0) { + return rv; + } - /* Extend connection flow control window for the amount of data - which are not passed to application. */ - if (!(strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING)) { - ngtcp2_conn_extend_max_offset(conn, strm->rx.last_offset - - ngtcp2_strm_rx_offset(strm)); - } + /* Extend connection flow control window for the amount of data + which are not passed to application. */ + if (!(strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING)) { + ngtcp2_conn_extend_max_offset(conn, strm->rx.last_offset - + ngtcp2_strm_rx_offset(strm)); } conn->rx.offset += datalen; @@ -6395,7 +7460,9 @@ static int conn_recv_reset_stream(ngtcp2_conn *conn, strm->rx.last_offset = fr->final_size; strm->flags |= NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_RECV_RST; - return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, fr->app_error_code); + ngtcp2_strm_set_app_error_code(strm, fr->app_error_code); + + return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm); } /* @@ -6459,34 +7526,44 @@ static int conn_recv_stop_sending(ngtcp2_conn *conn, /* Frame is received reset before we create ngtcp2_strm object. */ - strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc); if (strm == NULL) { return NGTCP2_ERR_NOMEM; } rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL); if (rv != 0) { - ngtcp2_mem_free(conn->mem, strm); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); + return rv; + } + + rv = conn_call_stream_open(conn, strm); + if (rv != 0) { return rv; } } + ngtcp2_strm_set_app_error_code(strm, fr->app_error_code); + /* No RESET_STREAM is required if we have sent FIN and all data have been acknowledged. */ - if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) && - ngtcp2_strm_is_all_tx_data_acked(strm)) { - return 0; - } - - rv = conn_reset_stream(conn, strm, fr->app_error_code); - if (rv != 0) { - return rv; + if (!ngtcp2_strm_is_all_tx_data_fin_acked(strm) && + !(strm->flags & NGTCP2_STRM_FLAG_SENT_RST)) { + rv = conn_reset_stream(conn, strm, fr->app_error_code); + if (rv != 0) { + return rv; + } } strm->flags |= NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_SENT_RST; + if (ngtcp2_strm_is_tx_queued(strm) && !ngtcp2_strm_streamfrq_empty(strm)) { + assert(conn->tx.strmq_nretrans); + --conn->tx.strmq_nretrans; + } + ngtcp2_strm_streamfrq_clear(strm); - return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, fr->app_error_code); + return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm); } /* @@ -6497,8 +7574,8 @@ static int check_stateless_reset(const ngtcp2_dcid *dcid, const ngtcp2_path *path, const ngtcp2_pkt_stateless_reset *sr) { return ngtcp2_path_eq(&dcid->ps.path, path) && - ngtcp2_verify_stateless_reset_token(dcid->token, - sr->stateless_reset_token) == 0; + ngtcp2_dcid_verify_stateless_reset_token( + dcid, sr->stateless_reset_token) == 0; } /* @@ -6535,18 +7612,18 @@ static int conn_on_stateless_reset(ngtcp2_conn *conn, const ngtcp2_path *path, (!pv || (!check_stateless_reset(&pv->dcid, path, &sr) && (!(pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) || !check_stateless_reset(&pv->fallback_dcid, path, &sr))))) { - len = ngtcp2_ringbuf_len(&conn->dcid.retired); + len = ngtcp2_ringbuf_len(&conn->dcid.retired.rb); for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i); + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, i); if (check_stateless_reset(dcid, path, &sr)) { break; } } if (i == len) { - len = ngtcp2_ringbuf_len(&conn->dcid.bound); + len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i); + dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); if (check_stateless_reset(dcid, path, &sr)) { break; } @@ -6562,16 +7639,9 @@ static int conn_on_stateless_reset(ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_log_rx_sr(&conn->log, &sr); - if (!conn->callbacks.recv_stateless_reset) { - return 0; - } - - rv = conn->callbacks.recv_stateless_reset(conn, &sr, conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } + ngtcp2_qlog_stateless_reset_pkt_received(&conn->qlog, &sr); - return 0; + return conn_call_recv_stateless_reset(conn, &sr); } /* @@ -6628,16 +7698,20 @@ static int conn_retire_dcid_prior_to(ngtcp2_conn *conn, ngtcp2_ringbuf *rb, if (rv != 0) { return rv; } + if (i == 0) { ngtcp2_ringbuf_pop_front(rb); - } else if (i == ngtcp2_ringbuf_len(rb) - 1) { + continue; + } + + if (i == ngtcp2_ringbuf_len(rb) - 1) { ngtcp2_ringbuf_pop_back(rb); break; - } else { - last = ngtcp2_ringbuf_get(rb, ngtcp2_ringbuf_len(rb) - 1); - ngtcp2_dcid_copy(dcid, last); - ngtcp2_ringbuf_pop_back(rb); } + + last = ngtcp2_ringbuf_get(rb, ngtcp2_ringbuf_len(rb) - 1); + ngtcp2_dcid_copy(dcid, last); + ngtcp2_ringbuf_pop_back(rb); } return 0; @@ -6691,10 +7765,10 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, } } - len = ngtcp2_ringbuf_len(&conn->dcid.bound); + len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i); + dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); rv = ngtcp2_dcid_verify_uniqueness(dcid, fr->seq, &fr->cid, fr->stateless_reset_token); if (rv != 0) { @@ -6705,10 +7779,10 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, } } - len = ngtcp2_ringbuf_len(&conn->dcid.unused); + len = ngtcp2_ringbuf_len(&conn->dcid.unused.rb); for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, i); + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, i); rv = ngtcp2_dcid_verify_uniqueness(dcid, fr->seq, &fr->cid, fr->stateless_reset_token); if (rv != 0) { @@ -6722,13 +7796,13 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, if (conn->dcid.retire_prior_to < fr->retire_prior_to) { conn->dcid.retire_prior_to = fr->retire_prior_to; - rv = - conn_retire_dcid_prior_to(conn, &conn->dcid.bound, fr->retire_prior_to); + rv = conn_retire_dcid_prior_to(conn, &conn->dcid.bound.rb, + fr->retire_prior_to); if (rv != 0) { return rv; } - rv = conn_retire_dcid_prior_to(conn, &conn->dcid.unused, + rv = conn_retire_dcid_prior_to(conn, &conn->dcid.unused.rb, conn->dcid.retire_prior_to); if (rv != 0) { return rv; @@ -6742,13 +7816,7 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, For example, a peer might send seq = 50000 and retire_prior_to = 50000. Then send NEW_CONNECTION_ID frames with seq < 50000. */ - /* TODO we might queue lots of RETIRE_CONNECTION_ID frame here - because conn->dcid.num_retire_queued is incremented when the - frame is serialized. */ - if (conn->dcid.num_retire_queued < NGTCP2_MAX_DCID_POOL_SIZE * 2) { - return conn_retire_dcid_seq(conn, fr->seq); - } - return 0; + return conn_retire_dcid_seq(conn, fr->seq); } if (found) { @@ -6768,7 +7836,7 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, ngtcp2_gaptr_drop_first_gap(&conn->dcid.seqgap); } - len = ngtcp2_ringbuf_len(&conn->dcid.unused); + len = ngtcp2_ringbuf_len(&conn->dcid.unused.rb); if (conn->dcid.current.seq >= conn->dcid.retire_prior_to) { ++extra_dcid; @@ -6795,7 +7863,7 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, return 0; } - dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused); + dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused.rb); ngtcp2_dcid_init(dcid, fr->seq, &fr->cid, fr->stateless_reset_token); return 0; @@ -6820,7 +7888,7 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, int rv; if (conn->dcid.current.seq < conn->dcid.retire_prior_to) { - if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { return 0; } @@ -6829,7 +7897,7 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, return rv; } - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); if (pv) { if (conn->dcid.current.seq == pv->dcid.seq) { ngtcp2_dcid_copy_cid_token(&pv->dcid, dcid); @@ -6841,7 +7909,7 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, } ngtcp2_dcid_copy_cid_token(&conn->dcid.current, dcid); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); rv = conn_call_activate_dcid(conn, &conn->dcid.current); if (rv != 0) { @@ -6851,13 +7919,13 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, if (pv) { if (pv->dcid.seq < conn->dcid.retire_prior_to) { - if (ngtcp2_ringbuf_len(&conn->dcid.unused)) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb)) { rv = conn_retire_dcid(conn, &pv->dcid, ts); if (rv != 0) { return rv; } - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && pv->dcid.seq == pv->fallback_dcid.seq) { @@ -6865,7 +7933,7 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, } ngtcp2_dcid_copy_cid_token(&pv->dcid, dcid); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); rv = conn_call_activate_dcid(conn, &pv->dcid); if (rv != 0) { @@ -6876,20 +7944,20 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, "path migration is aborted because connection ID is" "retired and no unused connection ID is available"); - return conn_stop_pv(conn, ts); + return conn_abort_pv(conn, ts); } } if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && pv->fallback_dcid.seq < conn->dcid.retire_prior_to) { - if (ngtcp2_ringbuf_len(&conn->dcid.unused)) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb)) { rv = conn_retire_dcid(conn, &pv->fallback_dcid, ts); if (rv != 0) { return rv; } - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); ngtcp2_dcid_copy_cid_token(&pv->fallback_dcid, dcid); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); rv = conn_call_activate_dcid(conn, &pv->fallback_dcid); if (rv != 0) { @@ -6897,7 +7965,7 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, } } else { /* Now we have no fallback dcid. */ - return conn_stop_pv(conn, ts); + return conn_abort_pv(conn, ts); } } } @@ -6949,7 +8017,7 @@ static int conn_recv_retire_connection_id(ngtcp2_conn *conn, scid->pe.index = NGTCP2_PQ_BAD_INDEX; } - scid->ts_retired = ts; + scid->retired_ts = ts; return ngtcp2_pq_push(&conn->scid.used, &scid->pe); } @@ -6970,8 +8038,6 @@ static int conn_recv_retire_connection_id(ngtcp2_conn *conn, * Server received NEW_TOKEN. */ static int conn_recv_new_token(ngtcp2_conn *conn, const ngtcp2_new_token *fr) { - int rv; - if (conn->server) { return NGTCP2_ERR_PROTO; } @@ -6980,14 +8046,7 @@ static int conn_recv_new_token(ngtcp2_conn *conn, const ngtcp2_new_token *fr) { return NGTCP2_ERR_FRAME_ENCODING; } - if (conn->callbacks.recv_new_token) { - rv = conn->callbacks.recv_new_token(conn, &fr->token, conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - } - - return 0; + return conn_call_recv_new_token(conn, &fr->token); } /* @@ -7042,49 +8101,48 @@ static int conn_recv_streams_blocked_uni(ngtcp2_conn *conn, * User-defined callback function failed. */ static int conn_select_preferred_addr(ngtcp2_conn *conn) { - struct sockaddr_storage buf; - ngtcp2_addr addr; + ngtcp2_path_storage ps; int rv; ngtcp2_duration pto, initial_pto, timeout; ngtcp2_pv *pv; ngtcp2_dcid *dcid; - ngtcp2_addr_init(&addr, (struct sockaddr *)&buf, 0, NULL); - - if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { return 0; } - rv = conn_call_select_preferred_addr(conn, &addr); + ngtcp2_path_storage_zero(&ps); + ngtcp2_addr_copy(&ps.path.local, &conn->dcid.current.ps.path.local); + + rv = conn_call_select_preferred_addr(conn, &ps.path); if (rv != 0) { return rv; } - if (addr.addrlen == 0 || - ngtcp2_addr_eq(&conn->dcid.current.ps.path.remote, &addr)) { + if (ps.path.remote.addrlen == 0 || + ngtcp2_addr_eq(&conn->dcid.current.ps.path.remote, &ps.path.remote)) { return 0; } assert(conn->pv == NULL); - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); + ngtcp2_dcid_set_path(dcid, &ps.path); + pto = conn_compute_pto(conn, &conn->pktns); initial_pto = conn_compute_initial_pto(conn, &conn->pktns); timeout = 3 * ngtcp2_max(pto, initial_pto); - rv = ngtcp2_pv_new(&pv, dcid, timeout, NGTCP2_PV_FLAG_NONE, &conn->log, - conn->mem); + rv = ngtcp2_pv_new(&pv, dcid, timeout, NGTCP2_PV_FLAG_PREFERRED_ADDR, + &conn->log, conn->mem); if (rv != 0) { /* TODO Call ngtcp2_dcid_free here if it is introduced */ return rv; } - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); conn->pv = pv; - ngtcp2_addr_copy(&pv->dcid.ps.path.local, &conn->dcid.current.ps.path.local); - ngtcp2_addr_copy(&pv->dcid.ps.path.remote, &addr); - return conn_call_activate_dcid(conn, &pv->dcid); } @@ -7120,18 +8178,18 @@ static int conn_recv_handshake_done(ngtcp2_conn *conn, ngtcp2_tstamp ts) { conn_discard_handshake_state(conn, ts); - if (conn->remote.transport_params.preferred_address_present) { + assert(conn->remote.transport_params); + + if (conn->remote.transport_params->preferred_address_present) { rv = conn_select_preferred_addr(conn); if (rv != 0) { return rv; } } - if (conn->callbacks.handshake_confirmed) { - rv = conn->callbacks.handshake_confirmed(conn, conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } + rv = conn_call_handshake_confirmed(conn); + if (rv != 0) { + return rv; } /* Re-arm loss detection timer after handshake has been @@ -7151,38 +8209,9 @@ static int conn_recv_handshake_done(ngtcp2_conn *conn, ngtcp2_tstamp ts) { * User-defined callback function failed. */ static int conn_recv_datagram(ngtcp2_conn *conn, ngtcp2_datagram *fr) { - const uint8_t *data; - size_t datalen; - int rv; - uint32_t flags = NGTCP2_DATAGRAM_FLAG_NONE; - assert(conn->local.transport_params.max_datagram_frame_size); - if (!conn->callbacks.recv_datagram) { - return 0; - } - - if (fr->datacnt) { - assert(fr->datacnt == 1); - - data = fr->data->base; - datalen = fr->data->len; - } else { - data = NULL; - datalen = 0; - } - - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { - flags |= NGTCP2_DATAGRAM_FLAG_EARLY; - } - - rv = conn->callbacks.recv_datagram(conn, flags, data, datalen, - conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; + return conn_call_recv_datagram(conn, fr); } /* @@ -7246,14 +8275,12 @@ static int conn_prepare_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) { new_rx_ckm = conn->crypto.key_update.new_rx_ckm; new_tx_ckm = conn->crypto.key_update.new_tx_ckm; - assert(conn->callbacks.update_key); - - rv = conn->callbacks.update_key( + rv = conn_call_update_key( conn, new_rx_ckm->secret.base, new_tx_ckm->secret.base, &rx_aead_ctx, new_rx_ckm->iv.base, &tx_aead_ctx, new_tx_ckm->iv.base, - rx_ckm->secret.base, tx_ckm->secret.base, secretlen, conn->user_data); + rx_ckm->secret.base, tx_ckm->secret.base, secretlen); if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; + return rv; } new_rx_ckm->aead_ctx = rx_aead_ctx; @@ -7276,9 +8303,11 @@ static int conn_prepare_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) { /* * conn_rotate_keys rotates keys. The current key moves to old key, - * and new key moves to the current key. + * and new key moves to the current key. If the local endpoint + * initiated this key update, pass nonzero as |initiator|. */ -static void conn_rotate_keys(ngtcp2_conn *conn, int64_t pkt_num) { +static void conn_rotate_keys(ngtcp2_conn *conn, int64_t pkt_num, + int initiator) { ngtcp2_pktns *pktns = &conn->pktns; assert(conn->crypto.key_update.new_rx_ckm); @@ -7302,6 +8331,9 @@ static void conn_rotate_keys(ngtcp2_conn *conn, int64_t pkt_num) { pktns->crypto.tx.ckm->pkt_num = pktns->tx.last_pkt_num + 1; conn->flags |= NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED; + if (initiator) { + conn->flags |= NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR; + } } /* @@ -7353,14 +8385,19 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PTV, "path is migrated back to the original path"); ngtcp2_dcid_copy(&conn->dcid.current, &conn->pv->fallback_dcid); - conn_reset_congestion_state(conn); + conn_reset_congestion_state(conn, ts); conn->dcid.current.bytes_recv += dgramlen; conn_reset_ecn_validation_state(conn); - rv = conn_stop_pv(conn, ts); + + rv = conn_abort_pv(conn, ts); if (rv != 0) { return rv; } - return 0; + + /* Run PMTUD just in case if it is prematurely aborted */ + assert(!conn->pmtud); + + return conn_start_pmtud(conn); } remote_addr_cmp = @@ -7368,14 +8405,21 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, local_addr_eq = ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, &path->local); - /* The transport specification draft-27 says: + /* + * When to change DCID? RFC 9002 section 9.5 says: * - * An endpoint MUST use a new connection ID if it initiates - * connection migration as described in Section 9.2 or probes a new - * network path as described in Section 9.1. An endpoint MUST use a - * new connection ID in response to a change in the address of a - * peer if the packet with the new peer address uses an active - * connection ID that has not been previously used by the peer. + * An endpoint MUST NOT reuse a connection ID when sending from more + * than one local address -- for example, when initiating connection + * migration as described in Section 9.2 or when probing a new + * network path as described in Section 9.1. + * + * Similarly, an endpoint MUST NOT reuse a connection ID when + * sending to more than one destination address. Due to network + * changes outside the control of its peer, an endpoint might + * receive packets from a new source address with the same + * Destination Connection ID field value, in which case it MAY + * continue to use the current connection ID with the new remote + * address while still sending from the same local address. */ require_new_cid = conn->dcid.current.cid.datalen && ((new_cid_used && remote_addr_cmp) || !local_addr_eq); @@ -7387,10 +8431,10 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, initial_pto = conn_compute_initial_pto(conn, &conn->pktns); timeout = 3 * ngtcp2_max(pto, initial_pto); - len = ngtcp2_ringbuf_len(&conn->dcid.bound); + len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); for (i = 0; i < len; ++i) { - bound_dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i); + bound_dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); if (ngtcp2_path_eq(&bound_dcid->ps.path, path)) { ngtcp2_log_info( &conn->log, NGTCP2_LOG_EVENT_CON, @@ -7398,13 +8442,13 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, ngtcp2_dcid_copy(&dcid, bound_dcid); if (i == 0) { - ngtcp2_ringbuf_pop_front(&conn->dcid.bound); - } else if (i == ngtcp2_ringbuf_len(&conn->dcid.bound) - 1) { - ngtcp2_ringbuf_pop_back(&conn->dcid.bound); + ngtcp2_ringbuf_pop_front(&conn->dcid.bound.rb); + } else if (i == ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1) { + ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb); } else { - last = ngtcp2_ringbuf_get(&conn->dcid.bound, len - 1); + last = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, len - 1); ngtcp2_dcid_copy(bound_dcid, last); - ngtcp2_ringbuf_pop_back(&conn->dcid.bound); + ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb); } require_new_cid = 0; @@ -7420,11 +8464,11 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, if (i == len) { if (require_new_cid) { - if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { return NGTCP2_ERR_CONN_ID_BLOCKED; } - ngtcp2_dcid_copy(&dcid, ngtcp2_ringbuf_get(&conn->dcid.unused, 0)); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + ngtcp2_dcid_copy(&dcid, ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0)); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); rv = conn_call_activate_dcid(conn, &dcid); if (rv != 0) { @@ -7440,7 +8484,7 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, } } - ngtcp2_path_copy(&dcid.ps.path, path); + ngtcp2_dcid_set_path(&dcid, path); dcid.bytes_recv += dgramlen; rv = ngtcp2_pv_new(&pv, &dcid, timeout, NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE, @@ -7452,24 +8496,35 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, if (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE)) { ngtcp2_dcid_copy(&pv->fallback_dcid, &conn->pv->fallback_dcid); pv->fallback_pto = conn->pv->fallback_pto; + /* Unset the flag bit so that conn_stop_pv does not retire + DCID. */ + conn->pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE; } else { ngtcp2_dcid_copy(&pv->fallback_dcid, &conn->dcid.current); pv->fallback_pto = pto; } - ngtcp2_dcid_copy(&conn->dcid.current, &dcid); - if (!local_addr_eq || (remote_addr_cmp & (NGTCP2_ADDR_COMPARE_FLAG_ADDR | NGTCP2_ADDR_COMPARE_FLAG_FAMILY))) { - conn_reset_congestion_state(conn); + conn_reset_congestion_state(conn, ts); + } else { + /* For NAT rebinding, keep max_udp_payload_size since client most + likely does not send a padded PATH_CHALLENGE. */ + dcid.max_udp_payload_size = ngtcp2_max( + dcid.max_udp_payload_size, conn->dcid.current.max_udp_payload_size); } + + ngtcp2_dcid_copy(&conn->dcid.current, &dcid); + conn_reset_ecn_validation_state(conn); + ngtcp2_conn_stop_pmtud(conn); + if (conn->pv) { ngtcp2_log_info( &conn->log, NGTCP2_LOG_EVENT_PTV, "path migration is aborted because new migration has started"); - rv = conn_stop_pv(conn, ts); + rv = conn_abort_pv(conn, ts); if (rv != 0) { return rv; } @@ -7481,7 +8536,7 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, } /* - * conn_recv_pkt_from_new_path is called when a Short packet is + * conn_recv_pkt_from_new_path is called when a 1RTT packet is * received from new path (not current path). This packet would be a * packet which only contains probing frame, or reordered packet, or a * path is being validated. @@ -7524,7 +8579,7 @@ static int conn_recv_pkt_from_new_path(ngtcp2_conn *conn, return rv; } - ngtcp2_path_copy(&bound_dcid->ps.path, path); + ngtcp2_dcid_set_path(bound_dcid, path); bound_dcid->bytes_recv += dgramlen; return 0; @@ -7607,7 +8662,10 @@ conn_recv_delayed_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_pkt_info *pi, case NGTCP2_FRAME_PADDING: break; case NGTCP2_FRAME_CONNECTION_CLOSE: - conn_recv_connection_close(conn, &fr->connection_close); + rv = conn_recv_connection_close(conn, &fr->connection_close); + if (rv != 0) { + return rv; + } break; case NGTCP2_FRAME_CRYPTO: case NGTCP2_FRAME_PING: @@ -7719,7 +8777,14 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return NGTCP2_ERR_DISCARD_PKT; } - if (pktlen < (size_t)nread + hd.len || conn->version != hd.version) { + if (pktlen < (size_t)nread + hd.len) { + return NGTCP2_ERR_DISCARD_PKT; + } + + assert(conn->negotiated_version); + + if (hd.version != conn->client_chosen_version && + hd.version != conn->negotiated_version) { return NGTCP2_ERR_DISCARD_PKT; } @@ -7740,6 +8805,10 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, "delayed Initial packet was discarded"); return (ngtcp2_ssize)pktlen; case NGTCP2_PKT_HANDSHAKE: + if (hd.version != conn->negotiated_version) { + return NGTCP2_ERR_DISCARD_PKT; + } + if (!conn->hs_pktns) { ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, "delayed Handshake packet was discarded"); @@ -7755,7 +8824,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, decrypt = conn->callbacks.decrypt; break; case NGTCP2_PKT_0RTT: - if (!conn->server) { + if (!conn->server || hd.version != conn->client_chosen_version) { return NGTCP2_ERR_DISCARD_PKT; } @@ -7800,7 +8869,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, } nwrite = decrypt_hp(&hd, conn->crypto.decrypt_hp_buf.base, hp, pkt, pktlen, - (size_t)nread, ckm, hp_ctx, hp_mask); + (size_t)nread, hp_ctx, hp_mask); if (nwrite < 0) { if (ngtcp2_err_is_fatal((int)nwrite)) { return nwrite; @@ -7824,7 +8893,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_log_rx_pkt_hd(&conn->log, &hd); - if (hd.type == NGTCP2_PKT_SHORT) { + if (hd.type == NGTCP2_PKT_1RTT) { key_phase_bit_changed = conn_key_phase_changed(conn, &hd); } @@ -7834,7 +8903,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, } if (key_phase_bit_changed) { - assert(hd.type == NGTCP2_PKT_SHORT); + assert(hd.type == NGTCP2_PKT_1RTT); ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, "unexpected KEY_PHASE"); @@ -7867,7 +8936,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, ckm, decrypt); if (force_decrypt_failure) { - nwrite = NGTCP2_ERR_TLS_DECRYPT; + nwrite = NGTCP2_ERR_DECRYPT; } if (nwrite < 0) { @@ -7875,9 +8944,9 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return nwrite; } - assert(NGTCP2_ERR_TLS_DECRYPT == nwrite); + assert(NGTCP2_ERR_DECRYPT == nwrite); - if (hd.type == NGTCP2_PKT_SHORT && + if (hd.type == NGTCP2_PKT_1RTT && ++conn->crypto.decryption_failure_count >= pktns->crypto.ctx.max_decryption_failure) { return NGTCP2_ERR_AEAD_LIMIT_REACHED; @@ -7963,8 +9032,6 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, "packet was ignored because of mismatched DCID"); return NGTCP2_ERR_DISCARD_PKT; } - - conn->flags |= NGTCP2_CONN_FLAG_RECV_PROTECTED_PKT; } ngtcp2_qlog_pkt_received_start(&conn->qlog); @@ -7985,8 +9052,9 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, hd.type == NGTCP2_PKT_0RTT) { return NGTCP2_ERR_PROTO; } + assert(conn->remote.transport_params); assign_recved_ack_delay_unscaled( - &fr->ack, conn->remote.transport_params.ack_delay_exponent); + &fr->ack, conn->remote.transport_params->ack_delay_exponent); break; } @@ -8092,8 +9160,11 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, break; case NGTCP2_FRAME_CONNECTION_CLOSE: case NGTCP2_FRAME_CONNECTION_CLOSE_APP: - conn_recv_connection_close(conn, &fr->connection_close); - break; + rv = conn_recv_connection_close(conn, &fr->connection_close); + if (rv != 0) { + return rv; + } + break; case NGTCP2_FRAME_PING: non_probing_pkt = 1; break; @@ -8180,7 +9251,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, } } - if (conn->server && hd.type == NGTCP2_PKT_SHORT && + if (conn->server && hd.type == NGTCP2_PKT_1RTT && !ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { if (non_probing_pkt && pktns->rx.max_pkt_num < hd.pkt_num && !conn_path_validation_in_progress(conn, path)) { @@ -8208,10 +9279,10 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, } } - if (hd.type == NGTCP2_PKT_SHORT) { + if (hd.type == NGTCP2_PKT_1RTT) { if (ckm == conn->crypto.key_update.new_rx_ckm) { ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "rotate keys"); - conn_rotate_keys(conn, hd.pkt_num); + conn_rotate_keys(conn, hd.pkt_num, /* initiator = */ 0); } else if (ckm->pkt_num > hd.pkt_num) { ckm->pkt_num = hd.pkt_num; } @@ -8220,6 +9291,10 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, conn->early.discard_started_ts == UINT64_MAX) { conn->early.discard_started_ts = ts; } + + if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { + conn_update_keep_alive_last_ts(conn, ts); + } } rv = pktns_commit_recv_pkt_num(pktns, hd.pkt_num, require_ack, pkt_ts); @@ -8250,8 +9325,8 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, } /* - * conn_process_buffered_protected_pkt processes buffered 0RTT or - * Short packets. + * conn_process_buffered_protected_pkt processes buffered 0RTT or 1RTT + * packets. * * This function returns 0 if it succeeds, or the same negative error * codes from conn_recv_pkt. @@ -8331,12 +9406,55 @@ static int conn_process_buffered_handshake_pkt(ngtcp2_conn *conn, } static void conn_sync_stream_id_limit(ngtcp2_conn *conn) { - ngtcp2_transport_params *params = &conn->remote.transport_params; + ngtcp2_transport_params *params = conn->remote.transport_params; + + assert(params); conn->local.bidi.max_streams = params->initial_max_streams_bidi; conn->local.uni.max_streams = params->initial_max_streams_uni; } +static int strm_set_max_offset(void *data, void *ptr) { + ngtcp2_conn *conn = ptr; + ngtcp2_transport_params *params = conn->remote.transport_params; + ngtcp2_strm *strm = data; + uint64_t max_offset; + int rv; + + assert(params); + + if (!conn_local_stream(conn, strm->stream_id)) { + return 0; + } + + if (bidi_stream(strm->stream_id)) { + max_offset = params->initial_max_stream_data_bidi_remote; + } else { + max_offset = params->initial_max_stream_data_uni; + } + + if (strm->tx.max_offset < max_offset) { + strm->tx.max_offset = max_offset; + + /* Don't call callback if stream is half-closed local */ + if (strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) { + return 0; + } + + rv = conn_call_extend_max_stream_data(conn, strm, strm->stream_id, + strm->tx.max_offset); + if (rv != 0) { + return rv; + } + } + + return 0; +} + +static int conn_sync_stream_data_limit(ngtcp2_conn *conn) { + return ngtcp2_map_each(&conn->strms, strm_set_max_offset, conn); +} + /* * conn_handshake_completed is called once cryptographic handshake has * completed. @@ -8377,7 +9495,7 @@ static int conn_handshake_completed(ngtcp2_conn *conn) { /* * conn_recv_cpkt processes compound packet after handshake. The - * buffer pointed by |pkt| might contain multiple packets. The Short + * buffer pointed by |pkt| might contain multiple packets. The 1RTT * packet must be the last one because it does not have payload length * field. * @@ -8435,11 +9553,11 @@ static int conn_recv_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path, * retired path list. */ static int conn_is_retired_path(ngtcp2_conn *conn, const ngtcp2_path *path) { - size_t i, len = ngtcp2_ringbuf_len(&conn->dcid.retired); + size_t i, len = ngtcp2_ringbuf_len(&conn->dcid.retired.rb); ngtcp2_dcid *dcid; for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i); + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, i); if (ngtcp2_path_eq(&dcid->ps.path, path)) { return 1; } @@ -8459,7 +9577,7 @@ static int conn_enqueue_handshake_done(ngtcp2_conn *conn) { assert(conn->server); - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); if (rv != 0) { return rv; } @@ -8478,27 +9596,36 @@ static int conn_enqueue_handshake_done(ngtcp2_conn *conn) { * reading given data. |pkt| points to the buffer to read and * |pktlen| is the length of the buffer. |path| is the network path. * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: (TBD). + * This function returns the number of bytes processed. Unless the + * last packet is 1RTT packet and an application decryption key has + * been installed, it returns |pktlen| if it succeeds. If it finds + * 1RTT packet and an application decryption key has been installed, + * it returns the number of bytes just before 1RTT packet begins. + * + * This function returns the number of bytes processed if it succeeds, + * or one of the following negative error codes: (TBD). */ -static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, - const ngtcp2_pkt_info *pi, const uint8_t *pkt, - size_t pktlen, ngtcp2_tstamp ts) { +static ngtcp2_ssize conn_read_handshake(ngtcp2_conn *conn, + const ngtcp2_path *path, + const ngtcp2_pkt_info *pi, + const uint8_t *pkt, size_t pktlen, + ngtcp2_tstamp ts) { int rv; + ngtcp2_ssize nread; switch (conn->state) { case NGTCP2_CS_CLIENT_INITIAL: /* TODO Better to log something when we ignore input */ - return 0; + return (ngtcp2_ssize)pktlen; case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: - rv = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts); - if (rv < 0) { - return rv; + nread = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts); + if (nread < 0) { + return nread; } if (conn->state == NGTCP2_CS_CLIENT_INITIAL) { /* Retry packet was received */ - return 0; + return (ngtcp2_ssize)pktlen; } assert(conn->hs_pktns); @@ -8510,20 +9637,24 @@ static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, } } - if ((conn->flags & (NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED | - NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) == - NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) { + if (conn_is_handshake_completed(conn) && + !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) { rv = conn_handshake_completed(conn); if (rv != 0) { return rv; } + + rv = conn_process_buffered_protected_pkt(conn, &conn->pktns, ts); + if (rv != 0) { + return rv; + } } - return 0; + return nread; case NGTCP2_CS_SERVER_INITIAL: - rv = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts); - if (rv < 0) { - return rv; + nread = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts); + if (nread < 0) { + return nread; } /* @@ -8538,7 +9669,7 @@ static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_rob_data_buffered(conn->in_pktns->crypto.strm.rx.rob)) { /* Address has been validated with token */ if (conn->local.settings.token.len) { - return 0; + return nread; } return NGTCP2_ERR_RETRY; } @@ -8562,11 +9693,11 @@ static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, } } - return 0; + return nread; case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: - rv = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts); - if (rv < 0) { - return rv; + nread = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts); + if (nread < 0) { + return nread; } if (conn->hs_pktns->crypto.rx.ckm) { @@ -8580,7 +9711,7 @@ static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, conn_discard_initial_state(conn, ts); } - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + if (!conn_is_handshake_completed(conn)) { /* If server hits amplification limit, it cancels loss detection timer. If server receives a packet from client, the limit is increased and server can send more. If server has @@ -8603,7 +9734,24 @@ static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, } } - return 0; + if ((size_t)nread < pktlen) { + /* We have 1RTT packet and application rx key, but the + handshake has not completed yet. */ + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "buffering 1RTT packet len=%zu", + pktlen - (size_t)nread); + + rv = conn_buffer_pkt(conn, &conn->pktns, path, pi, pkt + nread, + pktlen - (size_t)nread, pktlen, ts); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + + return (ngtcp2_ssize)pktlen; + } + + return nread; } if (!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED)) { @@ -8633,26 +9781,38 @@ static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, return rv; } + if (!conn->local.settings.no_pmtud) { + rv = conn_start_pmtud(conn); + if (rv != 0) { + return rv; + } + } + conn->pktns.rtb.persistent_congestion_start_ts = ts; /* Re-arm loss detection timer here after handshake has been confirmed. */ ngtcp2_conn_set_loss_detection_timer(conn, ts); - return 0; + return nread; case NGTCP2_CS_CLOSING: return NGTCP2_ERR_CLOSING; case NGTCP2_CS_DRAINING: return NGTCP2_ERR_DRAINING; default: - return 0; + return (ngtcp2_ssize)pktlen; } } -int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, - const ngtcp2_pkt_info *pi, const uint8_t *pkt, - size_t pktlen, ngtcp2_tstamp ts) { +int ngtcp2_conn_read_pkt_versioned(ngtcp2_conn *conn, const ngtcp2_path *path, + int pkt_info_version, + const ngtcp2_pkt_info *pi, + const uint8_t *pkt, size_t pktlen, + ngtcp2_tstamp ts) { int rv = 0; + ngtcp2_ssize nread = 0; + const ngtcp2_pkt_info zero_pi = {0}; + (void)pkt_info_version; conn->log.last_ts = ts; conn->qlog.last_ts = ts; @@ -8673,11 +9833,29 @@ int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return 0; } + if (!pi) { + pi = &zero_pi; + } + switch (conn->state) { case NGTCP2_CS_CLIENT_INITIAL: case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: case NGTCP2_CS_CLIENT_TLS_HANDSHAKE_FAILED: - return conn_read_handshake(conn, path, pi, pkt, pktlen, ts); + nread = conn_read_handshake(conn, path, pi, pkt, pktlen, ts); + if (nread < 0) { + return (int)nread; + } + + if ((size_t)nread == pktlen) { + return 0; + } + + assert(conn->pktns.crypto.rx.ckm); + + pkt += nread; + pktlen -= (size_t)nread; + + break; case NGTCP2_CS_SERVER_INITIAL: case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: case NGTCP2_CS_SERVER_TLS_HANDSHAKE_FAILED: @@ -8694,7 +9872,22 @@ int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return 0; } - return conn_read_handshake(conn, path, pi, pkt, pktlen, ts); + + nread = conn_read_handshake(conn, path, pi, pkt, pktlen, ts); + if (nread < 0) { + return (int)nread; + } + + if ((size_t)nread == pktlen) { + return 0; + } + + assert(conn->pktns.crypto.rx.ckm); + + pkt += nread; + pktlen -= (size_t)nread; + + break; case NGTCP2_CS_CLOSING: return NGTCP2_ERR_CLOSING; case NGTCP2_CS_DRAINING: @@ -8704,11 +9897,13 @@ int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, if (rv != 0) { return rv; } - return conn_recv_cpkt(conn, path, pi, pkt, pktlen, ts); + break; default: assert(0); abort(); } + + return conn_recv_cpkt(conn, path, pi, pkt, pktlen, ts); } /* @@ -8731,9 +9926,10 @@ static int conn_check_pkt_num_exhausted(ngtcp2_conn *conn) { static ngtcp2_ssize conn_retransmit_retry_early(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, + uint8_t flags, ngtcp2_tstamp ts) { - return conn_write_pkt(conn, pi, dest, destlen, NULL, NGTCP2_PKT_0RTT, - NGTCP2_WRITE_PKT_FLAG_NONE, ts); + return conn_write_pkt(conn, pi, dest, destlen, NULL, NGTCP2_PKT_0RTT, flags, + ts); } /* @@ -8746,11 +9942,45 @@ static int conn_handshake_probe_left(ngtcp2_conn *conn) { conn->hs_pktns->rtb.probe_pkt_left; } +/* + * conn_validate_early_transport_params_limits validates that the + * limits in transport parameters remembered by client for early data + * are not reduced. This function is only used by client and should + * only be called when early data is accepted by server. + */ +static int conn_validate_early_transport_params_limits(ngtcp2_conn *conn) { + const ngtcp2_transport_params *params = conn->remote.transport_params; + + assert(!conn->server); + assert(params); + + if (conn->early.transport_params.active_connection_id_limit > + params->active_connection_id_limit || + conn->early.transport_params.initial_max_data > + params->initial_max_data || + conn->early.transport_params.initial_max_stream_data_bidi_local > + params->initial_max_stream_data_bidi_local || + conn->early.transport_params.initial_max_stream_data_bidi_remote > + params->initial_max_stream_data_bidi_remote || + conn->early.transport_params.initial_max_stream_data_uni > + params->initial_max_stream_data_uni || + conn->early.transport_params.initial_max_streams_bidi > + params->initial_max_streams_bidi || + conn->early.transport_params.initial_max_streams_uni > + params->initial_max_streams_uni || + conn->early.transport_params.max_datagram_frame_size > + params->max_datagram_frame_size) { + return NGTCP2_ERR_PROTO; + } + + return 0; +} + /* * conn_write_handshake writes QUIC handshake packets to the buffer - * pointed by |dest| of length |destlen|. |early_datalen| specifies - * the expected length of early data to send. Specify 0 to - * |early_datalen| if there is no early data. + * pointed by |dest| of length |destlen|. |write_datalen| specifies + * the expected length of 0RTT or 1RTT packet payload. Specify 0 to + * |write_datalen| if there is no such data. * * This function returns the number of bytes written to the buffer, or * one of the following negative error codes: @@ -8771,14 +10001,12 @@ static int conn_handshake_probe_left(ngtcp2_conn *conn) { */ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, - size_t early_datalen, + uint64_t write_datalen, ngtcp2_tstamp ts) { int rv; ngtcp2_ssize res = 0, nwrite = 0, early_spktlen = 0; size_t origlen = destlen; - size_t server_tx_left; - ngtcp2_conn_stat *cstat = &conn->cstat; - size_t pending_early_datalen; + uint64_t pending_early_datalen; ngtcp2_dcid *dcid; ngtcp2_preferred_addr *paddr; @@ -8786,27 +10014,30 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, case NGTCP2_CS_CLIENT_INITIAL: pending_early_datalen = conn_retry_early_payloadlen(conn); if (pending_early_datalen) { - early_datalen = pending_early_datalen; + write_datalen = pending_early_datalen; } if (!(conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY)) { nwrite = - conn_write_client_initial(conn, pi, dest, destlen, early_datalen, ts); + conn_write_client_initial(conn, pi, dest, destlen, write_datalen, ts); if (nwrite <= 0) { return nwrite; } } else { nwrite = conn_write_handshake_pkt( conn, pi, dest, destlen, NGTCP2_PKT_INITIAL, - NGTCP2_WRITE_PKT_FLAG_NONE, early_datalen, ts); + NGTCP2_WRITE_PKT_FLAG_NONE, write_datalen, ts); if (nwrite < 0) { return nwrite; } } if (pending_early_datalen) { - early_spktlen = conn_retransmit_retry_early(conn, pi, dest + nwrite, - destlen - (size_t)nwrite, ts); + early_spktlen = conn_retransmit_retry_early( + conn, pi, dest + nwrite, destlen - (size_t)nwrite, + nwrite ? NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING + : NGTCP2_WRITE_PKT_FLAG_NONE, + ts); if (early_spktlen < 0) { assert(ngtcp2_err_is_fatal((int)early_spktlen)); @@ -8826,12 +10057,12 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) { pending_early_datalen = conn_retry_early_payloadlen(conn); if (pending_early_datalen) { - early_datalen = pending_early_datalen; + write_datalen = pending_early_datalen; } } nwrite = - conn_write_handshake_pkts(conn, pi, dest, destlen, early_datalen, ts); + conn_write_handshake_pkts(conn, pi, dest, destlen, write_datalen, ts); if (nwrite < 0) { return nwrite; } @@ -8841,9 +10072,10 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, destlen -= (size_t)nwrite; } - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + if (!conn_is_handshake_completed(conn)) { if (!(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) { - nwrite = conn_retransmit_retry_early(conn, pi, dest, destlen, ts); + nwrite = conn_retransmit_retry_early(conn, pi, dest, destlen, + NGTCP2_WRITE_PKT_FLAG_NONE, ts); if (nwrite < 0) { return nwrite; } @@ -8866,13 +10098,30 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM; } + if ((conn->flags & NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED) && + !(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) { + rv = conn_validate_early_transport_params_limits(conn); + if (rv != 0) { + return rv; + } + } + + /* Server might increase stream data limits. Extend it if we have + streams created for early data. */ + rv = conn_sync_stream_data_limit(conn); + if (rv != 0) { + return rv; + } + conn->state = NGTCP2_CS_POST_HANDSHAKE; - if (conn->remote.transport_params.preferred_address_present) { - assert(!ngtcp2_ringbuf_full(&conn->dcid.unused)); + assert(conn->remote.transport_params); - paddr = &conn->remote.transport_params.preferred_address; - dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused); + if (conn->remote.transport_params->preferred_address_present) { + assert(!ngtcp2_ringbuf_full(&conn->dcid.unused.rb)); + + paddr = &conn->remote.transport_params->preferred_address; + dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused.rb); ngtcp2_dcid_init(dcid, 1, &paddr->cid, paddr->stateless_reset_token); rv = ngtcp2_gaptr_push(&conn->dcid.seqgap, 1, 1); @@ -8881,11 +10130,12 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } } - if (conn->remote.transport_params.stateless_reset_token_present) { + if (conn->remote.transport_params->stateless_reset_token_present) { assert(conn->dcid.current.seq == 0); - memcpy(conn->dcid.current.token, - conn->remote.transport_params.stateless_reset_token, - sizeof(conn->dcid.current.token)); + assert(!(conn->dcid.current.flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT)); + ngtcp2_dcid_set_token( + &conn->dcid.current, + conn->remote.transport_params->stateless_reset_token); } rv = conn_call_activate_dcid(conn, &conn->dcid.current); @@ -8895,15 +10145,17 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn_process_early_rtb(conn); - rv = conn_process_buffered_protected_pkt(conn, &conn->pktns, ts); - if (rv != 0) { - return (ngtcp2_ssize)rv; + if (!conn->local.settings.no_pmtud) { + rv = conn_start_pmtud(conn); + if (rv != 0) { + return rv; + } } return res; case NGTCP2_CS_SERVER_INITIAL: - nwrite = conn_write_handshake_pkts(conn, pi, dest, destlen, - /* early_datalen = */ 0, ts); + nwrite = + conn_write_handshake_pkts(conn, pi, dest, destlen, write_datalen, ts); if (nwrite < 0) { return nwrite; } @@ -8914,47 +10166,30 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, return nwrite; case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { - if (!(conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) { - server_tx_left = conn_server_tx_left(conn, &conn->dcid.current); - if (server_tx_left == 0) { - if (cstat->loss_detection_timer != UINT64_MAX) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, - "loss detection timer canceled"); - cstat->loss_detection_timer = UINT64_MAX; - cstat->pto_count = 0; - } - return 0; - } - } - - if (conn_handshake_probe_left(conn) || !conn_cwnd_is_zero(conn)) { - nwrite = conn_write_handshake_pkts(conn, pi, dest, destlen, - /* early_datalen = */ 0, ts); - if (nwrite < 0) { - return nwrite; - } - - res += nwrite; - dest += nwrite; - destlen -= (size_t)nwrite; + if (conn_handshake_probe_left(conn) || !conn_cwnd_is_zero(conn)) { + nwrite = + conn_write_handshake_pkts(conn, pi, dest, destlen, write_datalen, ts); + if (nwrite < 0) { + return nwrite; } - if (res == 0) { - nwrite = conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts); - if (nwrite < 0) { - return nwrite; - } + res += nwrite; + dest += nwrite; + destlen -= (size_t)nwrite; + } - res += nwrite; - dest += nwrite; - origlen -= (size_t)nwrite; + if (res == 0) { + nwrite = conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts); + if (nwrite < 0) { + return nwrite; } - return res; + res += nwrite; + dest += nwrite; + origlen -= (size_t)nwrite; } - return 0; + return res; case NGTCP2_CS_CLOSING: return NGTCP2_ERR_CLOSING; case NGTCP2_CS_DRAINING: @@ -8996,17 +10231,17 @@ static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn, int send_stream = 0; int send_datagram = 0; ngtcp2_ssize spktlen, early_spktlen; - int was_client_initial; - size_t datalen; - size_t early_datalen = 0; + uint64_t datalen; + uint64_t write_datalen = 0; uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE; int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; + uint32_t version; assert(!conn->server); /* conn->early.ckm might be created in the first call of conn_handshake(). Check it later. */ - if (vmsg && !(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) { + if (vmsg) { switch (vmsg->type) { case NGTCP2_VMSG_TYPE_STREAM: datalen = ngtcp2_vec_len(vmsg->stream.data, vmsg->stream.datacnt); @@ -9018,9 +10253,11 @@ static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn, (vmsg->stream.strm->tx.max_offset - vmsg->stream.strm->tx.offset) && (conn->tx.max_offset - conn->tx.offset))); if (send_stream) { - early_datalen = - conn_enforce_flow_control(conn, vmsg->stream.strm, datalen) + - NGTCP2_STREAM_OVERHEAD; + write_datalen = + conn_enforce_flow_control(conn, vmsg->stream.strm, datalen); + write_datalen = + ngtcp2_min(write_datalen, NGTCP2_MIN_COALESCED_PAYLOADLEN); + write_datalen += NGTCP2_STREAM_OVERHEAD; if (vmsg->stream.flags & NGTCP2_WRITE_STREAM_FLAG_MORE) { wflags |= NGTCP2_WRITE_PKT_FLAG_MORE; @@ -9031,11 +10268,9 @@ static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn, break; case NGTCP2_VMSG_TYPE_DATAGRAM: datalen = ngtcp2_vec_len(vmsg->datagram.data, vmsg->datagram.datacnt); - /* TODO Do we need this? DATAGRAM is independent from STREAM - data and no retransmission */ send_datagram = conn_retry_early_payloadlen(conn) == 0; if (send_datagram) { - early_datalen = datalen + NGTCP2_DATAGRAM_OVERHEAD; + write_datalen = datalen + NGTCP2_DATAGRAM_OVERHEAD; if (vmsg->datagram.flags & NGTCP2_WRITE_DATAGRAM_FLAG_MORE) { wflags |= NGTCP2_WRITE_PKT_FLAG_MORE; @@ -9048,32 +10283,39 @@ static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn, } if (!ppe_pending) { - was_client_initial = conn->state == NGTCP2_CS_CLIENT_INITIAL; - spktlen = conn_write_handshake(conn, pi, dest, destlen, early_datalen, ts); + spktlen = conn_write_handshake(conn, pi, dest, destlen, write_datalen, ts); if (spktlen < 0) { return spktlen; } - if (conn->pktns.crypto.tx.ckm || !conn->early.ckm || - (!send_stream && !send_datagram)) { + if ((conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) || + !conn->early.ckm || (!send_stream && !send_datagram)) { return spktlen; } + + /* If spktlen > 0, we are making a compound packet. If Initial + packet is written, we have to pad bytes to 0-RTT packet. */ + version = conn->negotiated_version ? conn->negotiated_version + : conn->client_chosen_version; + if (spktlen > 0 && + ngtcp2_pkt_get_type_long(version, dest[0]) == NGTCP2_PKT_INITIAL) { + wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; + conn->pkt.require_padding = 1; + } else { + conn->pkt.require_padding = 0; + } } else { + assert(!conn->pktns.crypto.rx.ckm); assert(!conn->pktns.crypto.tx.ckm); assert(conn->early.ckm); - was_client_initial = conn->pkt.was_client_initial; + if (conn->pkt.require_padding) { + wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; + } spktlen = conn->pkt.hs_spktlen; } - /* If spktlen > 0, we are making a compound packet. If Initial - packet is written, we have to pad bytes to 0-RTT packet. */ - - if (spktlen && was_client_initial) { - wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; - } - dest += spktlen; destlen -= (size_t)spktlen; @@ -9089,7 +10331,6 @@ static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn, case NGTCP2_ERR_STREAM_DATA_BLOCKED: return spktlen; case NGTCP2_ERR_WRITE_MORE: - conn->pkt.was_client_initial = was_client_initial; conn->pkt.hs_spktlen = spktlen; break; } @@ -9107,7 +10348,7 @@ void ngtcp2_conn_handshake_completed(ngtcp2_conn *conn) { } int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn) { - return (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) && + return conn_is_handshake_completed(conn) && (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED); } @@ -9136,35 +10377,29 @@ int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt, size_t pktlen) { } if (pktlen == 0 || (pkt[0] & NGTCP2_HEADER_FORM_BIT) == 0) { - return -1; + return NGTCP2_ERR_INVALID_ARGUMENT; } nread = ngtcp2_pkt_decode_hd_long(p, pkt, pktlen); if (nread < 0) { - return -1; + return (int)nread; } switch (p->type) { case NGTCP2_PKT_INITIAL: - if (pktlen < NGTCP2_MIN_INITIAL_PKTLEN) { - return -1; - } - if (p->token.len == 0 && p->dcid.datalen < NGTCP2_MIN_INITIAL_DCIDLEN) { - return -1; - } break; case NGTCP2_PKT_0RTT: /* 0-RTT packet may arrive before Initial packet due to - re-ordering. */ - break; + re-ordering. ngtcp2 does not buffer 0RTT packet unless the + very first Initial packet is received or token is received. */ + return NGTCP2_ERR_RETRY; default: - return -1; + return NGTCP2_ERR_INVALID_ARGUMENT; } - if (p->version != NGTCP2_PROTO_VER_V1 && - (p->version < NGTCP2_PROTO_VER_DRAFT_MIN || - NGTCP2_PROTO_VER_DRAFT_MAX < p->version)) { - return 1; + if (pktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE || + (p->token.len == 0 && p->dcid.datalen < NGTCP2_MIN_INITIAL_DCIDLEN)) { + return NGTCP2_ERR_INVALID_ARGUMENT; } return 0; @@ -9178,6 +10413,7 @@ int ngtcp2_conn_install_initial_key( ngtcp2_pktns *pktns = conn->in_pktns; int rv; + assert(ivlen >= 8); assert(pktns); conn_call_delete_crypto_cipher_ctx(conn, &pktns->crypto.rx.hp_ctx); @@ -9220,12 +10456,64 @@ int ngtcp2_conn_install_initial_key( return 0; } +int ngtcp2_conn_install_vneg_initial_key( + ngtcp2_conn *conn, uint32_t version, + const ngtcp2_crypto_aead_ctx *rx_aead_ctx, const uint8_t *rx_iv, + const ngtcp2_crypto_cipher_ctx *rx_hp_ctx, + const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv, + const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen) { + int rv; + + assert(ivlen >= 8); + + conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.rx.hp_ctx); + conn->vneg.rx.hp_ctx.native_handle = NULL; + + if (conn->vneg.rx.ckm) { + conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.rx.ckm->aead_ctx); + ngtcp2_crypto_km_del(conn->vneg.rx.ckm, conn->mem); + conn->vneg.rx.ckm = NULL; + } + + conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.tx.hp_ctx); + conn->vneg.tx.hp_ctx.native_handle = NULL; + + if (conn->vneg.tx.ckm) { + conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.tx.ckm->aead_ctx); + ngtcp2_crypto_km_del(conn->vneg.tx.ckm, conn->mem); + conn->vneg.tx.ckm = NULL; + } + + rv = ngtcp2_crypto_km_new(&conn->vneg.rx.ckm, NULL, 0, NULL, rx_iv, ivlen, + conn->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_crypto_km_new(&conn->vneg.tx.ckm, NULL, 0, NULL, tx_iv, ivlen, + conn->mem); + if (rv != 0) { + return rv; + } + + /* Take owner ship after we are sure that no failure occurs, so that + caller can delete these contexts on failure. */ + conn->vneg.rx.ckm->aead_ctx = *rx_aead_ctx; + conn->vneg.rx.hp_ctx = *rx_hp_ctx; + conn->vneg.tx.ckm->aead_ctx = *tx_aead_ctx; + conn->vneg.tx.hp_ctx = *tx_hp_ctx; + conn->vneg.version = version; + + return 0; +} + int ngtcp2_conn_install_rx_handshake_key( ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx) { ngtcp2_pktns *pktns = conn->hs_pktns; int rv; + assert(ivlen >= 8); assert(pktns); assert(!pktns->crypto.rx.hp_ctx.native_handle); assert(!pktns->crypto.rx.ckm); @@ -9238,6 +10526,16 @@ int ngtcp2_conn_install_rx_handshake_key( pktns->crypto.rx.hp_ctx = *hp_ctx; + rv = conn_call_recv_rx_key(conn, NGTCP2_CRYPTO_LEVEL_HANDSHAKE); + if (rv != 0) { + ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, conn->mem); + pktns->crypto.rx.ckm = NULL; + + memset(&pktns->crypto.rx.hp_ctx, 0, sizeof(pktns->crypto.rx.hp_ctx)); + + return rv; + } + return 0; } @@ -9247,6 +10545,7 @@ int ngtcp2_conn_install_tx_handshake_key( ngtcp2_pktns *pktns = conn->hs_pktns; int rv; + assert(ivlen >= 8); assert(pktns); assert(!pktns->crypto.tx.hp_ctx.native_handle); assert(!pktns->crypto.tx.ckm); @@ -9260,7 +10559,20 @@ int ngtcp2_conn_install_tx_handshake_key( pktns->crypto.tx.hp_ctx = *hp_ctx; if (conn->server) { - return ngtcp2_conn_commit_local_transport_params(conn); + rv = ngtcp2_conn_commit_local_transport_params(conn); + if (rv != 0) { + return rv; + } + } + + rv = conn_call_recv_tx_key(conn, NGTCP2_CRYPTO_LEVEL_HANDSHAKE); + if (rv != 0) { + ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem); + pktns->crypto.tx.ckm = NULL; + + memset(&pktns->crypto.tx.hp_ctx, 0, sizeof(pktns->crypto.tx.hp_ctx)); + + return rv; } return 0; @@ -9272,6 +10584,7 @@ int ngtcp2_conn_install_early_key(ngtcp2_conn *conn, const ngtcp2_crypto_cipher_ctx *hp_ctx) { int rv; + assert(ivlen >= 8); assert(!conn->early.hp_ctx.native_handle); assert(!conn->early.ckm); @@ -9283,6 +10596,22 @@ int ngtcp2_conn_install_early_key(ngtcp2_conn *conn, conn->early.hp_ctx = *hp_ctx; + conn->flags |= NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED; + + if (conn->server) { + rv = conn_call_recv_rx_key(conn, NGTCP2_CRYPTO_LEVEL_EARLY); + } else { + rv = conn_call_recv_tx_key(conn, NGTCP2_CRYPTO_LEVEL_EARLY); + } + if (rv != 0) { + ngtcp2_crypto_km_del(conn->early.ckm, conn->mem); + conn->early.ckm = NULL; + + memset(&conn->early.hp_ctx, 0, sizeof(conn->early.hp_ctx)); + + return rv; + } + return 0; } @@ -9294,6 +10623,7 @@ int ngtcp2_conn_install_rx_key(ngtcp2_conn *conn, const uint8_t *secret, ngtcp2_pktns *pktns = &conn->pktns; int rv; + assert(ivlen >= 8); assert(!pktns->crypto.rx.hp_ctx.native_handle); assert(!pktns->crypto.rx.ckm); @@ -9306,22 +10636,42 @@ int ngtcp2_conn_install_rx_key(ngtcp2_conn *conn, const uint8_t *secret, pktns->crypto.rx.hp_ctx = *hp_ctx; if (!conn->server) { - conn->remote.transport_params = conn->remote.pending_transport_params; - conn_sync_stream_id_limit(conn); - conn->tx.max_offset = conn->remote.transport_params.initial_max_data; - } + if (conn->remote.pending_transport_params) { + ngtcp2_transport_params_del(conn->remote.transport_params, conn->mem); - return 0; -} + conn->remote.transport_params = conn->remote.pending_transport_params; + conn->remote.pending_transport_params = NULL; + conn_sync_stream_id_limit(conn); + conn->tx.max_offset = conn->remote.transport_params->initial_max_data; + } -int ngtcp2_conn_install_tx_key(ngtcp2_conn *conn, const uint8_t *secret, - size_t secretlen, - const ngtcp2_crypto_aead_ctx *aead_ctx, - const uint8_t *iv, size_t ivlen, - const ngtcp2_crypto_cipher_ctx *hp_ctx) { - ngtcp2_pktns *pktns = &conn->pktns; + if (conn->early.ckm) { + conn_discard_early_key(conn); + } + } + + rv = conn_call_recv_rx_key(conn, NGTCP2_CRYPTO_LEVEL_APPLICATION); + if (rv != 0) { + ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, conn->mem); + pktns->crypto.rx.ckm = NULL; + + memset(&pktns->crypto.rx.hp_ctx, 0, sizeof(pktns->crypto.rx.hp_ctx)); + + return rv; + } + + return 0; +} + +int ngtcp2_conn_install_tx_key(ngtcp2_conn *conn, const uint8_t *secret, + size_t secretlen, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *iv, size_t ivlen, + const ngtcp2_crypto_cipher_ctx *hp_ctx) { + ngtcp2_pktns *pktns = &conn->pktns; int rv; + assert(ivlen >= 8); assert(!pktns->crypto.tx.hp_ctx.native_handle); assert(!pktns->crypto.tx.ckm); @@ -9334,13 +10684,28 @@ int ngtcp2_conn_install_tx_key(ngtcp2_conn *conn, const uint8_t *secret, pktns->crypto.tx.hp_ctx = *hp_ctx; if (conn->server) { - conn->remote.transport_params = conn->remote.pending_transport_params; - conn_sync_stream_id_limit(conn); - conn->tx.max_offset = conn->remote.transport_params.initial_max_data; + if (conn->remote.pending_transport_params) { + ngtcp2_transport_params_del(conn->remote.transport_params, conn->mem); + + conn->remote.transport_params = conn->remote.pending_transport_params; + conn->remote.pending_transport_params = NULL; + conn_sync_stream_id_limit(conn); + conn->tx.max_offset = conn->remote.transport_params->initial_max_data; + } } else if (conn->early.ckm) { conn_discard_early_key(conn); } + rv = conn_call_recv_tx_key(conn, NGTCP2_CRYPTO_LEVEL_APPLICATION); + if (rv != 0) { + ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem); + pktns->crypto.tx.ckm = NULL; + + memset(&pktns->crypto.tx.hp_ctx, 0, sizeof(pktns->crypto.tx.hp_ctx)); + + return rv; + } + return 0; } @@ -9358,7 +10723,59 @@ int ngtcp2_conn_initiate_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) { return NGTCP2_ERR_INVALID_STATE; } - conn_rotate_keys(conn, NGTCP2_MAX_PKT_NUM); + conn_rotate_keys(conn, NGTCP2_MAX_PKT_NUM, /* initiator = */ 1); + + return 0; +} + +/* + * conn_retire_stale_bound_dcid retires stale destination connection + * ID in conn->dcid.bound to keep some unused destination connection + * IDs available. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_retire_stale_bound_dcid(ngtcp2_conn *conn, + ngtcp2_duration timeout, + ngtcp2_tstamp ts) { + size_t i; + ngtcp2_dcid *dcid, *last; + int rv; + + for (i = 0; i < ngtcp2_ringbuf_len(&conn->dcid.bound.rb);) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); + + assert(dcid->cid.datalen); + + if (dcid->bound_ts + timeout > ts) { + ++i; + continue; + } + + rv = conn_retire_dcid_seq(conn, dcid->seq); + if (rv != 0) { + return rv; + } + + if (i == 0) { + ngtcp2_ringbuf_pop_front(&conn->dcid.bound.rb); + continue; + } + + if (i == ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1) { + ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb); + break; + } + + last = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, + ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1); + ngtcp2_dcid_copy(dcid, last); + ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb); + } return 0; } @@ -9372,21 +10789,41 @@ ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn) { ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns); ngtcp2_scid *scid; ngtcp2_dcid *dcid; + size_t i, len; if (conn->pv) { res = ngtcp2_pv_next_expiry(conn->pv); } + if (conn->pmtud) { + res = ngtcp2_min(res, conn->pmtud->expiry); + } + if (!ngtcp2_pq_empty(&conn->scid.used)) { scid = ngtcp2_struct_of(ngtcp2_pq_top(&conn->scid.used), ngtcp2_scid, pe); - if (scid->ts_retired != UINT64_MAX) { - res = ngtcp2_min(res, scid->ts_retired + pto); + if (scid->retired_ts != UINT64_MAX) { + t = scid->retired_ts + pto; + res = ngtcp2_min(res, t); } } - if (ngtcp2_ringbuf_len(&conn->dcid.retired)) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, 0); - res = ngtcp2_min(res, dcid->ts_retired + pto); + if (ngtcp2_ringbuf_len(&conn->dcid.retired.rb)) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, 0); + t = dcid->retired_ts + pto; + res = ngtcp2_min(res, t); + } + + if (conn->dcid.current.cid.datalen) { + len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); + for (i = 0; i < len; ++i) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); + + assert(dcid->cid.datalen); + assert(dcid->bound_ts != UINT64_MAX); + + t = dcid->bound_ts + 3 * pto; + res = ngtcp2_min(res, t); + } } if (conn->server && conn->early.ckm && @@ -9408,28 +10845,60 @@ ngtcp2_tstamp ngtcp2_conn_ack_delay_expiry(ngtcp2_conn *conn) { return UINT64_MAX; } +static ngtcp2_tstamp conn_handshake_expiry(ngtcp2_conn *conn) { + if (conn_is_handshake_completed(conn) || + conn->local.settings.handshake_timeout == UINT64_MAX) { + return UINT64_MAX; + } + + return conn->local.settings.initial_ts + + conn->local.settings.handshake_timeout; +} + ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn) { ngtcp2_tstamp t1 = ngtcp2_conn_loss_detection_expiry(conn); ngtcp2_tstamp t2 = ngtcp2_conn_ack_delay_expiry(conn); ngtcp2_tstamp t3 = ngtcp2_conn_internal_expiry(conn); ngtcp2_tstamp t4 = ngtcp2_conn_lost_pkt_expiry(conn); + ngtcp2_tstamp t5 = conn_keep_alive_expiry(conn); + ngtcp2_tstamp t6 = conn_handshake_expiry(conn); + ngtcp2_tstamp t7 = ngtcp2_conn_get_idle_expiry(conn); ngtcp2_tstamp res = ngtcp2_min(t1, t2); res = ngtcp2_min(res, t3); - return ngtcp2_min(res, t4); + res = ngtcp2_min(res, t4); + res = ngtcp2_min(res, t5); + res = ngtcp2_min(res, t6); + res = ngtcp2_min(res, t7); + return ngtcp2_min(res, conn->tx.pacing.next_ts); } int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) { int rv; ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns); + if (ngtcp2_conn_get_idle_expiry(conn) <= ts) { + return NGTCP2_ERR_IDLE_CLOSE; + } + ngtcp2_conn_cancel_expired_ack_delay_timer(conn, ts); + conn_cancel_expired_keep_alive_timer(conn, ts); + + conn_cancel_expired_pkt_tx_timer(conn, ts); + ngtcp2_conn_remove_lost_pkt(conn, ts); if (conn->pv) { ngtcp2_pv_cancel_expired_timer(conn->pv, ts); } + if (conn->pmtud) { + ngtcp2_pmtud_handle_expiry(conn->pmtud, ts); + if (ngtcp2_pmtud_finished(conn->pmtud)) { + ngtcp2_conn_stop_pmtud(conn); + } + } + if (ngtcp2_conn_loss_detection_expiry(conn) <= ts) { rv = ngtcp2_conn_on_loss_detection_timer(conn, ts); if (rv != 0) { @@ -9437,6 +10906,13 @@ int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) { } } + if (conn->dcid.current.cid.datalen) { + rv = conn_retire_stale_bound_dcid(conn, 3 * pto, ts); + if (rv != 0) { + return rv; + } + } + rv = conn_remove_retired_connection_id(conn, pto, ts); if (rv != 0) { return rv; @@ -9449,26 +10925,38 @@ int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) { } } + if (!conn_is_handshake_completed(conn) && + conn->local.settings.handshake_timeout != UINT64_MAX && + conn->local.settings.initial_ts + + conn->local.settings.handshake_timeout <= + ts) { + return NGTCP2_ERR_HANDSHAKE_TIMEOUT; + } + return 0; } static void acktr_cancel_expired_ack_delay_timer(ngtcp2_acktr *acktr, + ngtcp2_duration max_ack_delay, ngtcp2_tstamp ts) { if (!(acktr->flags & NGTCP2_ACKTR_FLAG_CANCEL_TIMER) && - acktr->first_unacked_ts <= ts) { + acktr->first_unacked_ts != UINT64_MAX && + acktr->first_unacked_ts + max_ack_delay <= ts) { acktr->flags |= NGTCP2_ACKTR_FLAG_CANCEL_TIMER; } } void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + ngtcp2_duration ack_delay = conn_compute_ack_delay(conn); + if (conn->in_pktns) { - acktr_cancel_expired_ack_delay_timer(&conn->in_pktns->acktr, ts); + acktr_cancel_expired_ack_delay_timer(&conn->in_pktns->acktr, 0, ts); } if (conn->hs_pktns) { - acktr_cancel_expired_ack_delay_timer(&conn->hs_pktns->acktr, ts); + acktr_cancel_expired_ack_delay_timer(&conn->hs_pktns->acktr, 0, ts); } - acktr_cancel_expired_ack_delay_timer(&conn->pktns.acktr, ts); + acktr_cancel_expired_ack_delay_timer(&conn->pktns.acktr, ack_delay, ts); } ngtcp2_tstamp ngtcp2_conn_lost_pkt_expiry(ngtcp2_conn *conn) { @@ -9514,6 +11002,42 @@ void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts) { ngtcp2_rtb_remove_expired_lost_pkt(&conn->pktns.rtb, pto, ts); } +/* + * select_preferred_version selects the most preferred version. + * |fallback_version| is chosen if no preference is made, or + * |preferred_versions| does not include any of |chosen_version| or + * |other_versions|. |chosen_version| is treated as an extra other + * version. + */ +static uint32_t select_preferred_version(const uint32_t *preferred_versions, + size_t preferred_versionslen, + uint32_t chosen_version, + const uint8_t *other_versions, + size_t other_versionslen, + uint32_t fallback_version) { + size_t i, j; + + if (!preferred_versionslen || + (!other_versionslen && chosen_version == fallback_version)) { + return fallback_version; + } + + for (i = 0; i < preferred_versionslen; ++i) { + if (preferred_versions[i] == chosen_version) { + return chosen_version; + } + for (j = 0; j < other_versionslen; j += sizeof(uint32_t)) { + if (preferred_versions[i] != ngtcp2_get_uint32(&other_versions[j])) { + continue; + } + + return preferred_versions[i]; + } + } + + return fallback_version; +} + /* * conn_client_validate_transport_params validates |params| as client. * |params| must be sent with Encrypted Extensions. @@ -9521,12 +11045,11 @@ void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts) { * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * NGTCP2_ERR_PROTO - * Validation against either of original_dcid and retry_scid is - * failed. * NGTCP2_ERR_TRANSPORT_PARAM * params contains preferred address but server chose zero-length * connection ID. + * NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE + * Validation against version negotiation parameters failed. */ static int conn_client_validate_transport_params(ngtcp2_conn *conn, @@ -9551,14 +11074,76 @@ conn_client_validate_transport_params(ngtcp2_conn *conn, return NGTCP2_ERR_TRANSPORT_PARAM; } + if (params->version_info_present) { + if (conn->negotiated_version != params->version_info.chosen_version) { + return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; + } + + assert(vneg_other_versions_includes(conn->vneg.other_versions, + conn->vneg.other_versionslen, + conn->negotiated_version)); + } else if (conn->client_chosen_version != conn->negotiated_version || + conn->client_chosen_version != + conn->local.settings.original_version) { + return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; + } + + /* When client reacted upon Version Negotiation */ + if (conn->local.settings.original_version != conn->client_chosen_version) { + assert(params->version_info_present); + + /* Server choose original version after Version Negotiation. + Draft does not say this particular case, but this smells like + misbehaved server because server should accept original_version + in the original connection. */ + if (conn->local.settings.original_version == + params->version_info.chosen_version) { + return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; + } + + /* Check version downgrade on incompatible version negotiation. */ + if (params->version_info.other_versionslen == 0) { + return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; + } + + if (conn->client_chosen_version != + select_preferred_version(conn->vneg.preferred_versions, + conn->vneg.preferred_versionslen, + params->version_info.chosen_version, + params->version_info.other_versions, + params->version_info.other_versionslen, + /* fallback_version = */ 0)) { + return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; + } + } + return 0; } +uint32_t +ngtcp2_conn_server_negotiate_version(ngtcp2_conn *conn, + const ngtcp2_version_info *version_info) { + assert(conn->server); + assert(conn->client_chosen_version == version_info->chosen_version); + + return select_preferred_version( + conn->vneg.preferred_versions, conn->vneg.preferred_versionslen, + version_info->chosen_version, version_info->other_versions, + version_info->other_versionslen, version_info->chosen_version); +} + int ngtcp2_conn_set_remote_transport_params( ngtcp2_conn *conn, const ngtcp2_transport_params *params) { int rv; - assert(!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED)); + /* We expect this function is called once per QUIC connection, but + GnuTLS server seems to call TLS extension callback twice if it + sends HelloRetryRequest. In practice, same QUIC transport + parameters are sent in the 2nd client flight, just returning 0 + would cause no harm. */ + if (conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED) { + return 0; + } /* Assume that ngtcp2_decode_transport_params sets default value if active_connection_id_limit is omitted. */ @@ -9574,11 +11159,36 @@ int ngtcp2_conn_set_remote_transport_params( return NGTCP2_ERR_TRANSPORT_PARAM; } - if (params->max_udp_payload_size < 1200) { + if (params->max_udp_payload_size < NGTCP2_MAX_UDP_PAYLOAD_SIZE) { return NGTCP2_ERR_TRANSPORT_PARAM; } - if (!conn->server) { + if (conn->server) { + if (params->version_info_present) { + if (params->version_info.chosen_version != conn->client_chosen_version) { + return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; + } + + conn->negotiated_version = + ngtcp2_conn_server_negotiate_version(conn, ¶ms->version_info); + if (conn->negotiated_version != conn->client_chosen_version) { + rv = conn_call_version_negotiation(conn, conn->negotiated_version, + &conn->rcid); + if (rv != 0) { + return rv; + } + } + } else { + conn->negotiated_version = conn->client_chosen_version; + } + + conn->local.transport_params.version_info.chosen_version = + conn->negotiated_version; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "the negotiated version is 0x%08x", + conn->negotiated_version); + } else { rv = conn_client_validate_transport_params(conn, params); if (rv != 0) { return rv; @@ -9596,11 +11206,24 @@ int ngtcp2_conn_set_remote_transport_params( if ((conn->server && conn->pktns.crypto.tx.ckm) || (!conn->server && conn->pktns.crypto.rx.ckm)) { - conn->remote.transport_params = *params; + ngtcp2_transport_params_del(conn->remote.transport_params, conn->mem); + conn->remote.transport_params = NULL; + + rv = ngtcp2_transport_params_copy_new(&conn->remote.transport_params, + params, conn->mem); + if (rv != 0) { + return rv; + } conn_sync_stream_id_limit(conn); - conn->tx.max_offset = conn->remote.transport_params.initial_max_data; + conn->tx.max_offset = conn->remote.transport_params->initial_max_data; } else { - conn->remote.pending_transport_params = *params; + assert(!conn->remote.pending_transport_params); + + rv = ngtcp2_transport_params_copy_new( + &conn->remote.pending_transport_params, params, conn->mem); + if (rv != 0) { + return rv; + } } conn->flags |= NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED; @@ -9608,22 +11231,46 @@ int ngtcp2_conn_set_remote_transport_params( return 0; } -void ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn, - ngtcp2_transport_params *params) { - if (conn->pktns.crypto.rx.ckm) { - *params = conn->remote.transport_params; - } else { - *params = conn->remote.pending_transport_params; +int ngtcp2_conn_decode_remote_transport_params(ngtcp2_conn *conn, + const uint8_t *data, + size_t datalen) { + ngtcp2_transport_params params; + int rv; + + rv = ngtcp2_decode_transport_params( + ¶ms, + conn->server ? NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO + : NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, + data, datalen); + if (rv != 0) { + return rv; } + + return ngtcp2_conn_set_remote_transport_params(conn, ¶ms); } -void ngtcp2_conn_set_early_remote_transport_params( - ngtcp2_conn *conn, const ngtcp2_transport_params *params) { - ngtcp2_transport_params *p = &conn->remote.transport_params; +const ngtcp2_transport_params * +ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn) { + if (conn->remote.pending_transport_params) { + return conn->remote.pending_transport_params; + } + + return conn->remote.transport_params; +} + +void ngtcp2_conn_set_early_remote_transport_params_versioned( + ngtcp2_conn *conn, int transport_params_version, + const ngtcp2_transport_params *params) { + ngtcp2_transport_params *p; + (void)transport_params_version; assert(!conn->server); + assert(!conn->remote.transport_params); + + /* Assume that all pointer fields in p are NULL */ + p = ngtcp2_mem_calloc(conn->mem, 1, sizeof(*p)); - memset(p, 0, sizeof(*p)); + conn->remote.transport_params = p; p->initial_max_streams_bidi = params->initial_max_streams_bidi; p->initial_max_streams_uni = params->initial_max_streams_uni; @@ -9633,8 +11280,38 @@ void ngtcp2_conn_set_early_remote_transport_params( params->initial_max_stream_data_bidi_remote; p->initial_max_stream_data_uni = params->initial_max_stream_data_uni; p->initial_max_data = params->initial_max_data; + p->active_connection_id_limit = + ngtcp2_max(NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT, + params->active_connection_id_limit); + p->max_idle_timeout = params->max_idle_timeout; + if (!params->max_udp_payload_size) { + p->max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE; + } else { + p->max_udp_payload_size = + ngtcp2_max(NGTCP2_MAX_UDP_PAYLOAD_SIZE, params->max_udp_payload_size); + } + p->disable_active_migration = params->disable_active_migration; p->max_datagram_frame_size = params->max_datagram_frame_size; + /* These parameters are treated specially. If server accepts early + data, it must not set values for these parameters that are + smaller than these remembered values. */ + conn->early.transport_params.initial_max_streams_bidi = + params->initial_max_streams_bidi; + conn->early.transport_params.initial_max_streams_uni = + params->initial_max_streams_uni; + conn->early.transport_params.initial_max_stream_data_bidi_local = + params->initial_max_stream_data_bidi_local; + conn->early.transport_params.initial_max_stream_data_bidi_remote = + params->initial_max_stream_data_bidi_remote; + conn->early.transport_params.initial_max_stream_data_uni = + params->initial_max_stream_data_uni; + conn->early.transport_params.initial_max_data = params->initial_max_data; + conn->early.transport_params.active_connection_id_limit = + params->active_connection_id_limit; + conn->early.transport_params.max_datagram_frame_size = + params->max_datagram_frame_size; + conn_sync_stream_id_limit(conn); conn->tx.max_offset = p->initial_max_data; @@ -9643,8 +11320,11 @@ void ngtcp2_conn_set_early_remote_transport_params( NGTCP2_QLOG_SIDE_REMOTE); } -int ngtcp2_conn_set_local_transport_params( - ngtcp2_conn *conn, const ngtcp2_transport_params *params) { +int ngtcp2_conn_set_local_transport_params_versioned( + ngtcp2_conn *conn, int transport_params_version, + const ngtcp2_transport_params *params) { + (void)transport_params_version; + assert(conn->server); assert(params->active_connection_id_limit <= NGTCP2_MAX_DCID_POOL_SIZE); @@ -9652,7 +11332,7 @@ int ngtcp2_conn_set_local_transport_params( return NGTCP2_ERR_INVALID_STATE; } - conn->local.transport_params = *params; + conn_set_local_transport_params(conn, params); return 0; } @@ -9661,7 +11341,6 @@ int ngtcp2_conn_commit_local_transport_params(ngtcp2_conn *conn) { const ngtcp2_mem *mem = conn->mem; ngtcp2_transport_params *params = &conn->local.transport_params; ngtcp2_scid *scident; - ngtcp2_ksl_it it; int rv; assert(1 == ngtcp2_ksl_len(&conn->scid.set)); @@ -9677,32 +11356,21 @@ int ngtcp2_conn_commit_local_transport_params(ngtcp2_conn *conn) { params->preferred_address_present = 0; } - if (conn->server) { - if (params->stateless_reset_token_present) { - it = ngtcp2_ksl_begin(&conn->scid.set); - scident = ngtcp2_ksl_it_get(&it); - - memcpy(scident->token, params->stateless_reset_token, - NGTCP2_STATELESS_RESET_TOKENLEN); + if (conn->server && params->preferred_address_present) { + scident = ngtcp2_mem_malloc(mem, sizeof(*scident)); + if (scident == NULL) { + return NGTCP2_ERR_NOMEM; } - if (params->preferred_address_present) { - scident = ngtcp2_mem_malloc(mem, sizeof(*scident)); - if (scident == NULL) { - return NGTCP2_ERR_NOMEM; - } - - ngtcp2_scid_init(scident, 1, ¶ms->preferred_address.cid, - params->preferred_address.stateless_reset_token); + ngtcp2_scid_init(scident, 1, ¶ms->preferred_address.cid); - rv = ngtcp2_ksl_insert(&conn->scid.set, NULL, &scident->cid, scident); - if (rv != 0) { - ngtcp2_mem_free(mem, scident); - return rv; - } - - conn->scid.last_seq = 1; + rv = ngtcp2_ksl_insert(&conn->scid.set, NULL, &scident->cid, scident); + if (rv != 0) { + ngtcp2_mem_free(mem, scident); + return rv; } + + conn->scid.last_seq = 1; } conn->rx.window = conn->rx.unsent_max_offset = conn->rx.max_offset = @@ -9712,15 +11380,27 @@ int ngtcp2_conn_commit_local_transport_params(ngtcp2_conn *conn) { conn->remote.uni.unsent_max_streams = params->initial_max_streams_uni; conn->remote.uni.max_streams = params->initial_max_streams_uni; + conn->flags |= NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED; + ngtcp2_qlog_parameters_set_transport_params(&conn->qlog, params, conn->server, NGTCP2_QLOG_SIDE_LOCAL); return 0; } -void ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn, - ngtcp2_transport_params *params) { - *params = conn->local.transport_params; +const ngtcp2_transport_params * +ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn) { + return &conn->local.transport_params; +} + +ngtcp2_ssize ngtcp2_conn_encode_local_transport_params(ngtcp2_conn *conn, + uint8_t *dest, + size_t destlen) { + return ngtcp2_encode_transport_params( + dest, destlen, + conn->server ? NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS + : NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, + &conn->local.transport_params); } int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, int64_t *pstream_id, @@ -9732,7 +11412,7 @@ int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, int64_t *pstream_id, return NGTCP2_ERR_STREAM_ID_BLOCKED; } - strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc); if (strm == NULL) { return NGTCP2_ERR_NOMEM; } @@ -9740,7 +11420,7 @@ int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, int64_t *pstream_id, rv = ngtcp2_conn_init_stream(conn, strm, conn->local.bidi.next_stream_id, stream_user_data); if (rv != 0) { - ngtcp2_mem_free(conn->mem, strm); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); return rv; } @@ -9759,7 +11439,7 @@ int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, int64_t *pstream_id, return NGTCP2_ERR_STREAM_ID_BLOCKED; } - strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc); if (strm == NULL) { return NGTCP2_ERR_NOMEM; } @@ -9767,7 +11447,7 @@ int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, int64_t *pstream_id, rv = ngtcp2_conn_init_stream(conn, strm, conn->local.uni.next_stream_id, stream_user_data); if (rv != 0) { - ngtcp2_mem_free(conn->mem, strm); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); return rv; } ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_RD); @@ -9779,39 +11459,72 @@ int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, int64_t *pstream_id, } ngtcp2_strm *ngtcp2_conn_find_stream(ngtcp2_conn *conn, int64_t stream_id) { - ngtcp2_map_entry *me; - - me = ngtcp2_map_find(&conn->strms, (uint64_t)stream_id); - if (me == NULL) { - return NULL; - } - - return ngtcp2_struct_of(me, ngtcp2_strm, me); + return ngtcp2_map_find(&conn->strms, (uint64_t)stream_id); } -ngtcp2_ssize ngtcp2_conn_write_stream(ngtcp2_conn *conn, ngtcp2_path *path, - ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, ngtcp2_ssize *pdatalen, - uint32_t flags, int64_t stream_id, - const uint8_t *data, size_t datalen, - ngtcp2_tstamp ts) { +ngtcp2_ssize ngtcp2_conn_write_stream_versioned( + ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen, + uint32_t flags, int64_t stream_id, const uint8_t *data, size_t datalen, + ngtcp2_tstamp ts) { ngtcp2_vec datav; datav.len = datalen; datav.base = (uint8_t *)data; - return ngtcp2_conn_writev_stream(conn, path, pi, dest, destlen, pdatalen, - flags, stream_id, &datav, 1, ts); + return ngtcp2_conn_writev_stream_versioned(conn, path, pkt_info_version, pi, + dest, destlen, pdatalen, flags, + stream_id, &datav, 1, ts); } -ngtcp2_ssize ngtcp2_conn_writev_stream(ngtcp2_conn *conn, ngtcp2_path *path, - ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, ngtcp2_ssize *pdatalen, - uint32_t flags, int64_t stream_id, - const ngtcp2_vec *datav, size_t datavcnt, - ngtcp2_tstamp ts) { +static ngtcp2_ssize conn_write_vmsg_wrapper(ngtcp2_conn *conn, + ngtcp2_path *path, + int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, ngtcp2_vmsg *vmsg, + ngtcp2_tstamp ts) { + ngtcp2_conn_stat *cstat = &conn->cstat; + ngtcp2_ssize nwrite; + int undersized; + + nwrite = ngtcp2_conn_write_vmsg(conn, path, pkt_info_version, pi, dest, + destlen, vmsg, ts); + if (nwrite < 0) { + return nwrite; + } + + if (cstat->bytes_in_flight >= cstat->cwnd) { + conn->rst.is_cwnd_limited = 1; + } + + if (vmsg == NULL && cstat->bytes_in_flight < cstat->cwnd && + conn->tx.strmq_nretrans == 0) { + if (conn->local.settings.no_udp_payload_size_shaping) { + undersized = (size_t)nwrite < conn->local.settings.max_udp_payload_size; + } else { + undersized = (size_t)nwrite < conn->dcid.current.max_udp_payload_size; + } + + if (undersized) { + conn->rst.app_limited = conn->rst.delivered + cstat->bytes_in_flight; + + if (conn->rst.app_limited == 0) { + conn->rst.app_limited = cstat->max_udp_payload_size; + } + } + } + + return nwrite; +} + +ngtcp2_ssize ngtcp2_conn_writev_stream_versioned( + ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen, + uint32_t flags, int64_t stream_id, const ngtcp2_vec *datav, size_t datavcnt, + ngtcp2_tstamp ts) { ngtcp2_vmsg vmsg, *pvmsg; ngtcp2_strm *strm; + int64_t datalen; if (pdatalen) { *pdatalen = -1; @@ -9827,6 +11540,16 @@ ngtcp2_ssize ngtcp2_conn_writev_stream(ngtcp2_conn *conn, ngtcp2_path *path, return NGTCP2_ERR_STREAM_SHUT_WR; } + datalen = ngtcp2_vec_len_varint(datav, datavcnt); + if (datalen == -1) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + if ((uint64_t)datalen > NGTCP2_MAX_VARINT - strm->tx.offset || + (uint64_t)datalen > NGTCP2_MAX_VARINT - conn->tx.offset) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + vmsg.type = NGTCP2_VMSG_TYPE_STREAM; vmsg.stream.strm = strm; vmsg.stream.flags = flags; @@ -9839,50 +11562,67 @@ ngtcp2_ssize ngtcp2_conn_writev_stream(ngtcp2_conn *conn, ngtcp2_path *path, pvmsg = NULL; } - return ngtcp2_conn_write_vmsg(conn, path, pi, dest, destlen, pvmsg, ts); + return conn_write_vmsg_wrapper(conn, path, pkt_info_version, pi, dest, + destlen, pvmsg, ts); } -ngtcp2_ssize ngtcp2_conn_writev_datagram(ngtcp2_conn *conn, ngtcp2_path *path, - ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, int *paccepted, - uint32_t flags, - const ngtcp2_vec *datav, - size_t datavcnt, ngtcp2_tstamp ts) { +ngtcp2_ssize ngtcp2_conn_writev_datagram_versioned( + ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, int *paccepted, + uint32_t flags, uint64_t dgram_id, const ngtcp2_vec *datav, size_t datavcnt, + ngtcp2_tstamp ts) { ngtcp2_vmsg vmsg; + int64_t datalen; if (paccepted) { *paccepted = 0; } - if (conn->remote.transport_params.max_datagram_frame_size == 0) { + if (conn->remote.transport_params == NULL || + conn->remote.transport_params->max_datagram_frame_size == 0) { return NGTCP2_ERR_INVALID_STATE; } - if (conn->remote.transport_params.max_datagram_frame_size < - ngtcp2_pkt_datagram_framelen(ngtcp2_vec_len(datav, datavcnt))) { + + datalen = ngtcp2_vec_len_varint(datav, datavcnt); + if (datalen == -1 || (uint64_t)datalen > SIZE_MAX) { + return NGTCP2_ERR_INVALID_STATE; + } + + if (conn->remote.transport_params->max_datagram_frame_size < + ngtcp2_pkt_datagram_framelen((size_t)datalen)) { return NGTCP2_ERR_INVALID_ARGUMENT; } vmsg.type = NGTCP2_VMSG_TYPE_DATAGRAM; + vmsg.datagram.dgram_id = dgram_id; vmsg.datagram.flags = flags; vmsg.datagram.data = datav; vmsg.datagram.datacnt = datavcnt; vmsg.datagram.paccepted = paccepted; - return ngtcp2_conn_write_vmsg(conn, path, pi, dest, destlen, &vmsg, ts); + return conn_write_vmsg_wrapper(conn, path, pkt_info_version, pi, dest, + destlen, &vmsg, ts); } ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, - ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, ngtcp2_vmsg *vmsg, - ngtcp2_tstamp ts) { + int pkt_info_version, ngtcp2_pkt_info *pi, + uint8_t *dest, size_t destlen, + ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts) { ngtcp2_ssize nwrite; - ngtcp2_pktns *pktns = &conn->pktns; - size_t origlen = destlen; + size_t origlen; + size_t origdestlen = destlen; int rv; uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE; int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; + ngtcp2_conn_stat *cstat = &conn->cstat; ngtcp2_ssize res = 0; - size_t server_tx_left; + uint64_t server_tx_left; + uint64_t datalen; + uint64_t write_datalen = 0; + int64_t prev_in_pkt_num = -1; + ngtcp2_ksl_it it; + ngtcp2_rtb_entry *rtbent; + (void)pkt_info_version; conn->log.last_ts = ts; conn->qlog.last_ts = ts; @@ -9891,25 +11631,47 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_path_copy(path, &conn->dcid.current.ps.path); } + origlen = destlen = + conn_shape_udp_payload(conn, &conn->dcid.current, destlen); + if (!ppe_pending && pi) { pi->ecn = NGTCP2_ECN_NOT_ECT; } + if (!conn_pacing_pkt_tx_allowed(conn, ts)) { + return 0; + } + switch (conn->state) { case NGTCP2_CS_CLIENT_INITIAL: case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: case NGTCP2_CS_CLIENT_TLS_HANDSHAKE_FAILED: nwrite = conn_client_write_handshake(conn, pi, dest, destlen, vmsg, ts); - if (nwrite < 0) { + /* We might be unable to write a packet because of depletion of + congestion window budget, perhaps due to packet loss that + shrinks the window drastically. */ + if (nwrite <= 0) { return nwrite; } if (conn->state != NGTCP2_CS_POST_HANDSHAKE) { return nwrite; } + + assert(nwrite); + assert(dest[0] & NGTCP2_HEADER_FORM_BIT); + assert(conn->negotiated_version); + + if (ngtcp2_pkt_get_type_long(conn->negotiated_version, dest[0]) == + NGTCP2_PKT_INITIAL) { + /* We have added padding already, but in that case, there is no + space left to write 1RTT packet. */ + wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; + } + res = nwrite; dest += nwrite; destlen -= (size_t)nwrite; - /* Break here so that we can coalesces Short packets. */ + /* Break here so that we can coalesces 1RTT packet. */ break; case NGTCP2_CS_SERVER_INITIAL: case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: @@ -9917,23 +11679,74 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, if (!ppe_pending) { if (!(conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) { server_tx_left = conn_server_tx_left(conn, &conn->dcid.current); - destlen = ngtcp2_min(destlen, server_tx_left); + if (server_tx_left == 0) { + if (cstat->loss_detection_timer != UINT64_MAX) { + ngtcp2_log_info( + &conn->log, NGTCP2_LOG_EVENT_RCV, + "loss detection timer canceled due to amplification limit"); + cstat->loss_detection_timer = UINT64_MAX; + } + + return 0; + } + + destlen = (size_t)ngtcp2_min((uint64_t)destlen, server_tx_left); + } + + if (vmsg) { + switch (vmsg->type) { + case NGTCP2_VMSG_TYPE_STREAM: + datalen = ngtcp2_vec_len(vmsg->stream.data, vmsg->stream.datacnt); + if (datalen == 0 || (datalen > 0 && + (vmsg->stream.strm->tx.max_offset - + vmsg->stream.strm->tx.offset) && + (conn->tx.max_offset - conn->tx.offset))) { + write_datalen = + conn_enforce_flow_control(conn, vmsg->stream.strm, datalen); + write_datalen = + ngtcp2_min(write_datalen, NGTCP2_MIN_COALESCED_PAYLOADLEN); + write_datalen += NGTCP2_STREAM_OVERHEAD; + } + break; + case NGTCP2_VMSG_TYPE_DATAGRAM: + write_datalen = + ngtcp2_vec_len(vmsg->datagram.data, vmsg->datagram.datacnt) + + NGTCP2_DATAGRAM_OVERHEAD; + break; + default: + assert(0); + } + + if (conn->in_pktns && write_datalen > 0) { + it = ngtcp2_rtb_head(&conn->in_pktns->rtb); + if (!ngtcp2_ksl_it_end(&it)) { + rtbent = ngtcp2_ksl_it_get(&it); + prev_in_pkt_num = rtbent->hd.pkt_num; + } + } } - nwrite = conn_write_handshake(conn, pi, dest, destlen, 0, ts); + nwrite = conn_write_handshake(conn, pi, dest, destlen, write_datalen, ts); if (nwrite < 0) { return nwrite; } - if (conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) { - destlen = origlen; - } else { - origlen = destlen; - } - res = nwrite; dest += nwrite; destlen -= (size_t)nwrite; + + if (conn->in_pktns && write_datalen > 0) { + it = ngtcp2_rtb_head(&conn->in_pktns->rtb); + if (!ngtcp2_ksl_it_end(&it)) { + rtbent = ngtcp2_ksl_it_get(&it); + if (rtbent->hd.pkt_num != prev_in_pkt_num && + (rtbent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { + /* We have added padding already, but in that case, there + is no space left to write 1RTT packet. */ + wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; + } + } + } } if (conn->state != NGTCP2_CS_POST_HANDSHAKE && conn->pktns.crypto.tx.ckm == NULL) { @@ -9950,7 +11763,7 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, return 0; } - assert(pktns->crypto.tx.ckm); + assert(conn->pktns.crypto.tx.ckm); if (conn_check_pkt_num_exhausted(conn)) { return NGTCP2_ERR_PKT_NUM_EXHAUSTED; @@ -9976,12 +11789,18 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, if (ppe_pending) { res = conn->pkt.hs_spktlen; conn->pkt.hs_spktlen = 0; + if (conn->pkt.require_padding) { + wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; + } /* dest and destlen have already been adjusted in ppe in the first run. They are adjusted for probe packet later. */ - nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_SHORT, + nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_1RTT, wflags, ts); goto fin; } else { + conn->pkt.require_padding = + (wflags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING); + if (conn->state == NGTCP2_CS_POST_HANDSHAKE) { rv = conn_prepare_key_update(conn, ts); if (rv != 0) { @@ -9992,29 +11811,43 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, if (!conn->pktns.rtb.probe_pkt_left && conn_cwnd_is_zero(conn)) { destlen = 0; } else { - nwrite = conn_write_path_response(conn, path, pi, dest, destlen, ts); - if (nwrite) { - goto fin; - } - - if (conn->pv) { - nwrite = conn_write_path_challenge(conn, path, pi, dest, destlen, ts); + if (res == 0) { + nwrite = + conn_write_path_response(conn, path, pi, dest, origdestlen, ts); if (nwrite) { goto fin; } + + if (conn->pv) { + nwrite = + conn_write_path_challenge(conn, path, pi, dest, origdestlen, ts); + if (nwrite) { + goto fin; + } + } + + if (conn->pmtud && + (!conn->hs_pktns || + ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq) == 0)) { + nwrite = conn_write_pmtud_probe(conn, pi, dest, origdestlen, ts); + if (nwrite) { + goto fin; + } + } } if (conn->server && !(conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) { server_tx_left = conn_server_tx_left(conn, &conn->dcid.current); - origlen = ngtcp2_min(origlen, server_tx_left); - destlen = ngtcp2_min(destlen, server_tx_left); - - if (destlen == 0 && conn->cstat.loss_detection_timer != UINT64_MAX) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, - "loss detection timer canceled"); + origlen = (size_t)ngtcp2_min((uint64_t)origlen, server_tx_left); + destlen = (size_t)ngtcp2_min((uint64_t)destlen, server_tx_left); + + if (server_tx_left == 0 && + conn->cstat.loss_detection_timer != UINT64_MAX) { + ngtcp2_log_info( + &conn->log, NGTCP2_LOG_EVENT_RCV, + "loss detection timer canceled due to amplification limit"); conn->cstat.loss_detection_timer = UINT64_MAX; - conn->cstat.pto_count = 0; } } } @@ -10025,7 +11858,8 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, if (conn_handshake_probe_left(conn)) { destlen = origlen; } - nwrite = conn_write_handshake_pkts(conn, pi, dest, destlen, 0, ts); + nwrite = conn_write_handshake_pkts(conn, pi, dest, destlen, + /* write_datalen = */ 0, ts); if (nwrite < 0) { return nwrite; } @@ -10042,13 +11876,13 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, "transmit probe pkt left=%zu", conn->pktns.rtb.probe_pkt_left); - nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_SHORT, + nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_1RTT, wflags, ts); goto fin; } - nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_SHORT, + nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_1RTT, wflags, ts); if (nwrite) { assert(nwrite != NGTCP2_ERR_NOBUF); @@ -10056,7 +11890,7 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, } if (res == 0) { - nwrite = conn_write_ack_pkt(conn, pi, dest, origlen, NGTCP2_PKT_SHORT, ts); + nwrite = conn_write_ack_pkt(conn, pi, dest, origlen, NGTCP2_PKT_1RTT, ts); } fin: @@ -10078,24 +11912,27 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, static ngtcp2_ssize conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, uint8_t pkt_type, - uint64_t error_code, ngtcp2_tstamp ts) { + uint64_t error_code, const uint8_t *reason, + size_t reasonlen, ngtcp2_tstamp ts) { ngtcp2_pktns *in_pktns = conn->in_pktns; ngtcp2_pktns *hs_pktns = conn->hs_pktns; ngtcp2_ssize res = 0, nwrite; ngtcp2_frame fr; + uint8_t flags = NGTCP2_WRITE_PKT_FLAG_NONE; fr.type = NGTCP2_FRAME_CONNECTION_CLOSE; fr.connection_close.error_code = error_code; fr.connection_close.frame_type = 0; - fr.connection_close.reasonlen = 0; - fr.connection_close.reason = NULL; + fr.connection_close.reasonlen = reasonlen; + fr.connection_close.reason = (uint8_t *)reason; if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) && pkt_type != NGTCP2_PKT_INITIAL) { if (in_pktns && conn->server) { nwrite = ngtcp2_conn_write_single_frame_pkt( - conn, pi, dest, destlen, NGTCP2_PKT_INITIAL, &conn->dcid.current.cid, - &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); + conn, pi, dest, destlen, NGTCP2_PKT_INITIAL, + NGTCP2_WRITE_PKT_FLAG_NONE, &conn->dcid.current.cid, &fr, + NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); if (nwrite < 0) { return nwrite; } @@ -10109,7 +11946,8 @@ conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, hs_pktns->crypto.tx.ckm) { nwrite = ngtcp2_conn_write_single_frame_pkt( conn, pi, dest, destlen, NGTCP2_PKT_HANDSHAKE, - &conn->dcid.current.cid, &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); + NGTCP2_WRITE_PKT_FLAG_NONE, &conn->dcid.current.cid, &fr, + NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); if (nwrite < 0) { return nwrite; } @@ -10120,8 +11958,12 @@ conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } } + if (!conn->server && pkt_type == NGTCP2_PKT_INITIAL) { + flags = NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; + } + nwrite = ngtcp2_conn_write_single_frame_pkt( - conn, pi, dest, destlen, pkt_type, &conn->dcid.current.cid, &fr, + conn, pi, dest, destlen, pkt_type, flags, &conn->dcid.current.cid, &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); if (nwrite < 0) { @@ -10137,13 +11979,15 @@ conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, return res; } -ngtcp2_ssize ngtcp2_conn_write_connection_close( +ngtcp2_ssize ngtcp2_conn_write_connection_close_pkt( ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, uint64_t error_code, ngtcp2_tstamp ts) { + size_t destlen, uint64_t error_code, const uint8_t *reason, + size_t reasonlen, ngtcp2_tstamp ts) { ngtcp2_pktns *in_pktns = conn->in_pktns; ngtcp2_pktns *hs_pktns = conn->hs_pktns; uint8_t pkt_type; ngtcp2_ssize nwrite; + uint64_t server_tx_left; conn->log.last_ts = ts; conn->qlog.last_ts = ts; @@ -10154,9 +11998,10 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close( switch (conn->state) { case NGTCP2_CS_CLIENT_INITIAL: + return NGTCP2_ERR_INVALID_STATE; case NGTCP2_CS_CLOSING: case NGTCP2_CS_DRAINING: - return NGTCP2_ERR_INVALID_STATE; + return 0; default: break; } @@ -10165,13 +12010,20 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close( ngtcp2_path_copy(path, &conn->dcid.current.ps.path); } + destlen = conn_shape_udp_payload(conn, &conn->dcid.current, destlen); + if (pi) { pi->ecn = NGTCP2_ECN_NOT_ECT; } + if (conn->server) { + server_tx_left = conn_server_tx_left(conn, &conn->dcid.current); + destlen = (size_t)ngtcp2_min((uint64_t)destlen, server_tx_left); + } + if (conn->state == NGTCP2_CS_POST_HANDSHAKE || (conn->server && conn->pktns.crypto.tx.ckm)) { - pkt_type = NGTCP2_PKT_SHORT; + pkt_type = NGTCP2_PKT_1RTT; } else if (hs_pktns && hs_pktns->crypto.tx.ckm) { pkt_type = NGTCP2_PKT_HANDSHAKE; } else if (in_pktns && in_pktns->crypto.tx.ckm) { @@ -10183,7 +12035,7 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close( } nwrite = conn_write_connection_close(conn, pi, dest, destlen, pkt_type, - error_code, ts); + error_code, reason, reasonlen, ts); if (nwrite < 0) { return nwrite; } @@ -10193,12 +12045,14 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close( return nwrite; } -ngtcp2_ssize ngtcp2_conn_write_application_close( +ngtcp2_ssize ngtcp2_conn_write_application_close_pkt( ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, uint64_t app_error_code, ngtcp2_tstamp ts) { + size_t destlen, uint64_t app_error_code, const uint8_t *reason, + size_t reasonlen, ngtcp2_tstamp ts) { ngtcp2_ssize nwrite; ngtcp2_ssize res = 0; ngtcp2_frame fr; + uint64_t server_tx_left; conn->log.last_ts = ts; conn->qlog.last_ts = ts; @@ -10209,9 +12063,10 @@ ngtcp2_ssize ngtcp2_conn_write_application_close( switch (conn->state) { case NGTCP2_CS_CLIENT_INITIAL: + return NGTCP2_ERR_INVALID_STATE; case NGTCP2_CS_CLOSING: case NGTCP2_CS_DRAINING: - return NGTCP2_ERR_INVALID_STATE; + return 0; default: break; } @@ -10220,16 +12075,23 @@ ngtcp2_ssize ngtcp2_conn_write_application_close( ngtcp2_path_copy(path, &conn->dcid.current.ps.path); } + destlen = conn_shape_udp_payload(conn, &conn->dcid.current, destlen); + if (pi) { pi->ecn = NGTCP2_ECN_NOT_ECT; } + if (conn->server) { + server_tx_left = conn_server_tx_left(conn, &conn->dcid.current); + destlen = (size_t)ngtcp2_min((uint64_t)destlen, server_tx_left); + } + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) { nwrite = conn_write_connection_close(conn, pi, dest, destlen, conn->hs_pktns->crypto.tx.ckm ? NGTCP2_PKT_HANDSHAKE : NGTCP2_PKT_INITIAL, - NGTCP2_APPLICATION_ERROR, ts); + NGTCP2_APPLICATION_ERROR, NULL, 0, ts); if (nwrite < 0) { return nwrite; } @@ -10251,12 +12113,12 @@ ngtcp2_ssize ngtcp2_conn_write_application_close( fr.type = NGTCP2_FRAME_CONNECTION_CLOSE_APP; fr.connection_close.error_code = app_error_code; fr.connection_close.frame_type = 0; - fr.connection_close.reasonlen = 0; - fr.connection_close.reason = NULL; + fr.connection_close.reasonlen = reasonlen; + fr.connection_close.reason = (uint8_t *)reason; nwrite = ngtcp2_conn_write_single_frame_pkt( - conn, pi, dest, destlen, NGTCP2_PKT_SHORT, &conn->dcid.current.cid, &fr, - NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); + conn, pi, dest, destlen, NGTCP2_PKT_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE, + &conn->dcid.current.cid, &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); if (nwrite < 0) { return nwrite; @@ -10273,6 +12135,92 @@ ngtcp2_ssize ngtcp2_conn_write_application_close( return res; } +static void +connection_close_error_init(ngtcp2_connection_close_error *ccerr, + ngtcp2_connection_close_error_code_type type, + uint64_t error_code, const uint8_t *reason, + size_t reasonlen) { + ccerr->type = type; + ccerr->error_code = error_code; + ccerr->frame_type = 0; + ccerr->reason = (uint8_t *)reason; + ccerr->reasonlen = reasonlen; +} + +void ngtcp2_connection_close_error_default( + ngtcp2_connection_close_error *ccerr) { + connection_close_error_init(ccerr, + NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT, + NGTCP2_NO_ERROR, NULL, 0); +} + +void ngtcp2_connection_close_error_set_transport_error( + ngtcp2_connection_close_error *ccerr, uint64_t error_code, + const uint8_t *reason, size_t reasonlen) { + connection_close_error_init(ccerr, + NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT, + error_code, reason, reasonlen); +} + +void ngtcp2_connection_close_error_set_transport_error_liberr( + ngtcp2_connection_close_error *ccerr, int liberr, const uint8_t *reason, + size_t reasonlen) { + switch (liberr) { + case NGTCP2_ERR_RECV_VERSION_NEGOTIATION: + connection_close_error_init( + ccerr, + NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION, + NGTCP2_NO_ERROR, reason, reasonlen); + + return; + case NGTCP2_ERR_IDLE_CLOSE: + connection_close_error_init( + ccerr, NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE, + NGTCP2_NO_ERROR, reason, reasonlen); + + return; + }; + + ngtcp2_connection_close_error_set_transport_error( + ccerr, ngtcp2_err_infer_quic_transport_error_code(liberr), reason, + reasonlen); +} + +void ngtcp2_connection_close_error_set_transport_error_tls_alert( + ngtcp2_connection_close_error *ccerr, uint8_t tls_alert, + const uint8_t *reason, size_t reasonlen) { + ngtcp2_connection_close_error_set_transport_error( + ccerr, NGTCP2_CRYPTO_ERROR | tls_alert, reason, reasonlen); +} + +void ngtcp2_connection_close_error_set_application_error( + ngtcp2_connection_close_error *ccerr, uint64_t error_code, + const uint8_t *reason, size_t reasonlen) { + connection_close_error_init( + ccerr, NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION, error_code, + reason, reasonlen); +} + +ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned( + ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, + const ngtcp2_connection_close_error *ccerr, ngtcp2_tstamp ts) { + (void)pkt_info_version; + + switch (ccerr->type) { + case NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT: + return ngtcp2_conn_write_connection_close_pkt( + conn, path, pi, dest, destlen, ccerr->error_code, ccerr->reason, + ccerr->reasonlen, ts); + case NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION: + return ngtcp2_conn_write_application_close_pkt( + conn, path, pi, dest, destlen, ccerr->error_code, ccerr->reason, + ccerr->reasonlen, ts); + default: + return 0; + } +} + int ngtcp2_conn_is_in_closing_period(ngtcp2_conn *conn) { return conn->state == NGTCP2_CS_CLOSING; } @@ -10281,47 +12229,45 @@ int ngtcp2_conn_is_in_draining_period(ngtcp2_conn *conn) { return conn->state == NGTCP2_CS_DRAINING; } -int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, - uint64_t app_error_code) { +int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm) { int rv; - if (!strm->app_error_code) { - app_error_code = strm->app_error_code; - } - - rv = ngtcp2_map_remove(&conn->strms, strm->me.key); + rv = ngtcp2_map_remove(&conn->strms, (ngtcp2_map_key_type)strm->stream_id); if (rv != 0) { assert(rv != NGTCP2_ERR_INVALID_ARGUMENT); return rv; } - rv = conn_call_stream_close(conn, strm, app_error_code); + rv = conn_call_stream_close(conn, strm); if (rv != 0) { goto fin; } if (ngtcp2_strm_is_tx_queued(strm)) { ngtcp2_pq_remove(&conn->tx.strmq, &strm->pe); + if (!ngtcp2_strm_streamfrq_empty(strm)) { + assert(conn->tx.strmq_nretrans); + --conn->tx.strmq_nretrans; + } } fin: ngtcp2_strm_free(strm); - ngtcp2_mem_free(conn->mem, strm); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); return rv; } -int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm, - uint64_t app_error_code) { +int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, + ngtcp2_strm *strm) { if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RDWR) == NGTCP2_STRM_FLAG_SHUT_RDWR && ((strm->flags & NGTCP2_STRM_FLAG_RECV_RST) || ngtcp2_strm_rx_offset(strm) == strm->rx.last_offset) && (((strm->flags & NGTCP2_STRM_FLAG_SENT_RST) && (strm->flags & NGTCP2_STRM_FLAG_RST_ACKED)) || - (!(strm->flags & NGTCP2_STRM_FLAG_SENT_RST) && - ngtcp2_strm_is_all_tx_data_acked(strm)))) { - return ngtcp2_conn_close_stream(conn, strm, app_error_code); + ngtcp2_strm_is_all_tx_data_fin_acked(strm))) { + return ngtcp2_conn_close_stream(conn, strm); } return 0; } @@ -10338,14 +12284,16 @@ int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm, */ static int conn_shutdown_stream_write(ngtcp2_conn *conn, ngtcp2_strm *strm, uint64_t app_error_code) { - if (strm->flags & NGTCP2_STRM_FLAG_SENT_RST) { + ngtcp2_strm_set_app_error_code(strm, app_error_code); + + if ((strm->flags & NGTCP2_STRM_FLAG_SENT_RST) || + ngtcp2_strm_is_all_tx_data_fin_acked(strm)) { return 0; } /* Set this flag so that we don't accidentally send DATA to this stream. */ strm->flags |= NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_SENT_RST; - strm->app_error_code = app_error_code; ngtcp2_strm_streamfrq_clear(strm); @@ -10381,7 +12329,7 @@ static int conn_shutdown_stream_read(ngtcp2_conn *conn, ngtcp2_strm *strm, } strm->flags |= NGTCP2_STRM_FLAG_STOP_SENDING; - strm->app_error_code = app_error_code; + ngtcp2_strm_set_app_error_code(strm, app_error_code); return conn_stop_sending(conn, strm, app_error_code); } @@ -10393,7 +12341,7 @@ int ngtcp2_conn_shutdown_stream(ngtcp2_conn *conn, int64_t stream_id, strm = ngtcp2_conn_find_stream(conn, stream_id); if (strm == NULL) { - return NGTCP2_ERR_STREAM_NOT_FOUND; + return 0; } rv = conn_shutdown_stream_read(conn, strm, app_error_code); @@ -10415,7 +12363,7 @@ int ngtcp2_conn_shutdown_stream_write(ngtcp2_conn *conn, int64_t stream_id, strm = ngtcp2_conn_find_stream(conn, stream_id); if (strm == NULL) { - return NGTCP2_ERR_STREAM_NOT_FOUND; + return 0; } return conn_shutdown_stream_write(conn, strm, app_error_code); @@ -10427,7 +12375,7 @@ int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, int64_t stream_id, strm = ngtcp2_conn_find_stream(conn, stream_id); if (strm == NULL) { - return NGTCP2_ERR_STREAM_NOT_FOUND; + return 0; } return conn_shutdown_stream_read(conn, strm, app_error_code); @@ -10475,7 +12423,7 @@ int ngtcp2_conn_extend_max_stream_offset(ngtcp2_conn *conn, int64_t stream_id, strm = ngtcp2_conn_find_stream(conn, stream_id); if (strm == NULL) { - return NGTCP2_ERR_STREAM_NOT_FOUND; + return 0; } return conn_extend_max_stream_offset(conn, strm, datalen); @@ -10488,77 +12436,135 @@ void ngtcp2_conn_extend_max_offset(ngtcp2_conn *conn, uint64_t datalen) { return; } - conn->rx.unsent_max_offset += datalen; + conn->rx.unsent_max_offset += datalen; +} + +void ngtcp2_conn_extend_max_streams_bidi(ngtcp2_conn *conn, size_t n) { + handle_max_remote_streams_extension(&conn->remote.bidi.unsent_max_streams, n); +} + +void ngtcp2_conn_extend_max_streams_uni(ngtcp2_conn *conn, size_t n) { + handle_max_remote_streams_extension(&conn->remote.uni.unsent_max_streams, n); +} + +const ngtcp2_cid *ngtcp2_conn_get_dcid(ngtcp2_conn *conn) { + return &conn->dcid.current.cid; +} + +const ngtcp2_cid *ngtcp2_conn_get_client_initial_dcid(ngtcp2_conn *conn) { + return &conn->rcid; +} + +uint32_t ngtcp2_conn_get_client_chosen_version(ngtcp2_conn *conn) { + return conn->client_chosen_version; +} + +uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn) { + return conn->negotiated_version; +} + +static int delete_strms_pq_each(void *data, void *ptr) { + ngtcp2_conn *conn = ptr; + ngtcp2_strm *s = data; + + if (ngtcp2_strm_is_tx_queued(s)) { + ngtcp2_pq_remove(&conn->tx.strmq, &s->pe); + if (!ngtcp2_strm_streamfrq_empty(s)) { + assert(conn->tx.strmq_nretrans); + --conn->tx.strmq_nretrans; + } + } + + ngtcp2_strm_free(s); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, s); + + return 0; } -void ngtcp2_conn_extend_max_streams_bidi(ngtcp2_conn *conn, size_t n) { - handle_max_remote_streams_extension(&conn->remote.bidi.unsent_max_streams, n); -} +/* + * conn_discard_early_data_state discards any connection states which + * are altered by any operations during early data transfer. + */ +static void conn_discard_early_data_state(ngtcp2_conn *conn) { + ngtcp2_frame_chain **pfrc, *frc; -void ngtcp2_conn_extend_max_streams_uni(ngtcp2_conn *conn, size_t n) { - handle_max_remote_streams_extension(&conn->remote.uni.unsent_max_streams, n); -} + ngtcp2_rtb_remove_early_data(&conn->pktns.rtb, &conn->cstat); -const ngtcp2_cid *ngtcp2_conn_get_dcid(ngtcp2_conn *conn) { - return &conn->dcid.current.cid; -} + ngtcp2_map_each_free(&conn->strms, delete_strms_pq_each, conn); + ngtcp2_map_clear(&conn->strms); -uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn) { - return conn->version; -} + conn->tx.offset = 0; -int ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn) { - ngtcp2_pktns *pktns = &conn->pktns; - ngtcp2_rtb *rtb = &conn->pktns.rtb; - int rv; + conn->rx.unsent_max_offset = conn->rx.max_offset = + conn->local.transport_params.initial_max_data; - conn->flags |= NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED; + conn->remote.bidi.unsent_max_streams = conn->remote.bidi.max_streams = + conn->local.transport_params.initial_max_streams_bidi; - rv = ngtcp2_rtb_remove_all(rtb, conn, pktns, &conn->cstat); - if (rv != 0) { - return rv; + conn->remote.uni.unsent_max_streams = conn->remote.uni.max_streams = + conn->local.transport_params.initial_max_streams_uni; + + if (conn->server) { + conn->local.bidi.next_stream_id = 1; + conn->local.uni.next_stream_id = 3; + } else { + conn->local.bidi.next_stream_id = 0; + conn->local.uni.next_stream_id = 2; } - return rv; + for (pfrc = &conn->pktns.tx.frq; *pfrc;) { + frc = *pfrc; + *pfrc = (*pfrc)->next; + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); + } } -void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, - ngtcp2_duration ack_delay, ngtcp2_tstamp ts) { - ngtcp2_conn_stat *cstat = &conn->cstat; - ngtcp2_duration min_rtt; +void ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn) { + if (conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) { + return; + } + + conn->flags |= NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED; - rtt = ngtcp2_max(rtt, NGTCP2_GRANULARITY); + conn_discard_early_data_state(conn); +} - cstat->latest_rtt = rtt; +int ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, + ngtcp2_duration ack_delay, ngtcp2_tstamp ts) { + ngtcp2_conn_stat *cstat = &conn->cstat; if (cstat->min_rtt == UINT64_MAX) { + cstat->latest_rtt = rtt; cstat->min_rtt = rtt; cstat->smoothed_rtt = rtt; cstat->rttvar = rtt / 2; cstat->first_rtt_sample_ts = ts; } else { - min_rtt = ngtcp2_min(cstat->min_rtt, rtt); if (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) { + assert(conn->remote.transport_params); + ack_delay = - ngtcp2_min(ack_delay, conn->remote.transport_params.max_ack_delay); - } else if (ack_delay > 0 && rtt < cstat->min_rtt + ack_delay) { + ngtcp2_min(ack_delay, conn->remote.transport_params->max_ack_delay); + } else if (ack_delay > 0 && rtt >= cstat->min_rtt && + rtt < cstat->min_rtt + ack_delay) { /* Ignore RTT sample if adjusting ack_delay causes the sample less than min_rtt before handshake confirmation. */ ngtcp2_log_info( &conn->log, NGTCP2_LOG_EVENT_RCV, "ignore rtt sample because ack_delay is too large latest_rtt=%" PRIu64 " min_rtt=%" PRIu64 " ack_delay=%" PRIu64, - (uint64_t)(rtt / NGTCP2_MILLISECONDS), - (uint64_t)(cstat->min_rtt / NGTCP2_MILLISECONDS), - (uint64_t)(ack_delay / NGTCP2_MILLISECONDS)); - return; + rtt / NGTCP2_MILLISECONDS, cstat->min_rtt / NGTCP2_MILLISECONDS, + ack_delay / NGTCP2_MILLISECONDS); + return NGTCP2_ERR_INVALID_ARGUMENT; } - if (rtt > min_rtt + ack_delay) { + cstat->latest_rtt = rtt; + cstat->min_rtt = ngtcp2_min(cstat->min_rtt, rtt); + + if (rtt >= cstat->min_rtt + ack_delay) { rtt -= ack_delay; } - cstat->min_rtt = min_rtt; cstat->rttvar = (cstat->rttvar * 3 + (cstat->smoothed_rtt < rtt ? rtt - cstat->smoothed_rtt : cstat->smoothed_rtt - rtt)) / @@ -10566,56 +12572,91 @@ void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, cstat->smoothed_rtt = (cstat->smoothed_rtt * 7 + rtt) / 8; } - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, - "latest_rtt=%" PRIu64 " min_rtt=%" PRIu64 - " smoothed_rtt=%" PRIu64 " rttvar=%" PRIu64 - " ack_delay=%" PRIu64, - (uint64_t)(cstat->latest_rtt / NGTCP2_MILLISECONDS), - (uint64_t)(cstat->min_rtt / NGTCP2_MILLISECONDS), - cstat->smoothed_rtt / NGTCP2_MILLISECONDS, - cstat->rttvar / NGTCP2_MILLISECONDS, - (uint64_t)(ack_delay / NGTCP2_MILLISECONDS)); + ngtcp2_log_info( + &conn->log, NGTCP2_LOG_EVENT_RCV, + "latest_rtt=%" PRIu64 " min_rtt=%" PRIu64 " smoothed_rtt=%" PRIu64 + " rttvar=%" PRIu64 " ack_delay=%" PRIu64, + cstat->latest_rtt / NGTCP2_MILLISECONDS, + cstat->min_rtt / NGTCP2_MILLISECONDS, + cstat->smoothed_rtt / NGTCP2_MILLISECONDS, + cstat->rttvar / NGTCP2_MILLISECONDS, ack_delay / NGTCP2_MILLISECONDS); + + return 0; } -void ngtcp2_conn_get_conn_stat(ngtcp2_conn *conn, ngtcp2_conn_stat *cstat) { +void ngtcp2_conn_get_conn_stat_versioned(ngtcp2_conn *conn, + int conn_stat_version, + ngtcp2_conn_stat *cstat) { + (void)conn_stat_version; + *cstat = conn->cstat; } -static ngtcp2_pktns *conn_get_earliest_pktns(ngtcp2_conn *conn, - ngtcp2_tstamp *pts, - const ngtcp2_tstamp *times) { +static void conn_get_loss_time_and_pktns(ngtcp2_conn *conn, + ngtcp2_tstamp *ploss_time, + ngtcp2_pktns **ppktns) { + ngtcp2_pktns *const ns[] = {conn->hs_pktns, &conn->pktns}; + ngtcp2_conn_stat *cstat = &conn->cstat; + ngtcp2_duration *loss_time = cstat->loss_time; + ngtcp2_tstamp earliest_loss_time = loss_time[NGTCP2_PKTNS_ID_INITIAL]; + ngtcp2_pktns *pktns = conn->in_pktns; + size_t i; + + for (i = 0; i < sizeof(ns) / sizeof(ns[0]); ++i) { + if (ns[i] == NULL || ns[i]->rtb.num_pto_eliciting == 0 || + loss_time[i] >= earliest_loss_time) { + continue; + } + + earliest_loss_time = loss_time[i]; + pktns = ns[i]; + } + + if (ploss_time) { + *ploss_time = earliest_loss_time; + } + if (ppktns) { + *ppktns = pktns; + } +} + +static ngtcp2_tstamp conn_get_earliest_pto_expiry(ngtcp2_conn *conn, + ngtcp2_tstamp ts) { ngtcp2_pktns *ns[] = {conn->in_pktns, conn->hs_pktns, &conn->pktns}; - ngtcp2_pktns *res = ns[0]; size_t i; - ngtcp2_tstamp earliest_ts = times[NGTCP2_PKTNS_ID_INITIAL]; + ngtcp2_tstamp earliest_ts = UINT64_MAX, t; + ngtcp2_conn_stat *cstat = &conn->cstat; + ngtcp2_tstamp *times = cstat->last_tx_pkt_ts; + ngtcp2_duration duration = + compute_pto(cstat->smoothed_rtt, cstat->rttvar, /* max_ack_delay = */ 0) * + (1ULL << cstat->pto_count); - for (i = NGTCP2_PKTNS_ID_HANDSHAKE; i < NGTCP2_PKTNS_ID_MAX; ++i) { - if (ns[i] == NULL || ns[i]->rtb.num_retransmittable == 0 || + for (i = NGTCP2_PKTNS_ID_INITIAL; i < NGTCP2_PKTNS_ID_MAX; ++i) { + if (ns[i] == NULL || ns[i]->rtb.num_pto_eliciting == 0 || (times[i] == UINT64_MAX || - (earliest_ts != UINT64_MAX && times[i] >= earliest_ts) || (i == NGTCP2_PKTNS_ID_APPLICATION && !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) { continue; } - earliest_ts = times[i]; - res = ns[i]; - } + t = times[i] + duration; - if (res == NULL && !conn->server) { - earliest_ts = UINT64_MAX; + if (i == NGTCP2_PKTNS_ID_APPLICATION) { + assert(conn->remote.transport_params); + t += conn->remote.transport_params->max_ack_delay * + (1ULL << cstat->pto_count); + } - if (conn->hs_pktns && conn->hs_pktns->crypto.tx.ckm) { - res = conn->hs_pktns; - } else { - res = conn->in_pktns; + if (t < earliest_ts) { + earliest_ts = t; } } - if (pts) { - *pts = earliest_ts; + if (earliest_ts == UINT64_MAX) { + return ts + duration; } - return res; + + return earliest_ts; } void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { @@ -10624,11 +12665,9 @@ void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { ngtcp2_pktns *in_pktns = conn->in_pktns; ngtcp2_pktns *hs_pktns = conn->hs_pktns; ngtcp2_pktns *pktns = &conn->pktns; - ngtcp2_pktns *earliest_pktns; ngtcp2_tstamp earliest_loss_time; - ngtcp2_tstamp last_tx_pkt_ts; - conn_get_earliest_pktns(conn, &earliest_loss_time, cstat->loss_time); + conn_get_loss_time_and_pktns(conn, &earliest_loss_time, NULL); if (earliest_loss_time != UINT64_MAX) { cstat->loss_detection_timer = earliest_loss_time; @@ -10639,9 +12678,9 @@ void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { return; } - if ((!in_pktns || in_pktns->rtb.num_retransmittable == 0) && - (!hs_pktns || hs_pktns->rtb.num_retransmittable == 0) && - (pktns->rtb.num_retransmittable == 0 || + if ((!in_pktns || in_pktns->rtb.num_pto_eliciting == 0) && + (!hs_pktns || hs_pktns->rtb.num_pto_eliciting == 0) && + (pktns->rtb.num_pto_eliciting == 0 || !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) && (conn->server || (conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED | @@ -10655,24 +12694,14 @@ void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { return; } - earliest_pktns = - conn_get_earliest_pktns(conn, &last_tx_pkt_ts, cstat->last_tx_pkt_ts); - - assert(earliest_pktns); - - if (last_tx_pkt_ts == UINT64_MAX) { - last_tx_pkt_ts = ts; - } - - timeout = conn_compute_pto(conn, earliest_pktns) * (1ULL << cstat->pto_count); + cstat->loss_detection_timer = conn_get_earliest_pto_expiry(conn, ts); - cstat->loss_detection_timer = last_tx_pkt_ts + timeout; + timeout = + cstat->loss_detection_timer > ts ? cstat->loss_detection_timer - ts : 0; ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, - "loss_detection_timer=%" PRIu64 " last_tx_pkt_ts=%" PRIu64 - " timeout=%" PRIu64, - cstat->loss_detection_timer, last_tx_pkt_ts, - (uint64_t)(timeout / NGTCP2_MILLISECONDS)); + "loss_detection_timer=%" PRIu64 " timeout=%" PRIu64, + cstat->loss_detection_timer, timeout / NGTCP2_MILLISECONDS); } int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { @@ -10681,9 +12710,7 @@ int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { ngtcp2_pktns *in_pktns = conn->in_pktns; ngtcp2_pktns *hs_pktns = conn->hs_pktns; ngtcp2_tstamp earliest_loss_time; - ngtcp2_pktns *loss_pktns = - conn_get_earliest_pktns(conn, &earliest_loss_time, cstat->loss_time); - ngtcp2_pktns *earliest_pktns; + ngtcp2_pktns *loss_pktns = NULL; conn->log.last_ts = ts; conn->qlog.last_ts = ts; @@ -10702,10 +12729,14 @@ int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { return 0; } + conn_get_loss_time_and_pktns(conn, &earliest_loss_time, &loss_pktns); + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, "loss detection timer fired"); if (earliest_loss_time != UINT64_MAX) { + assert(loss_pktns); + rv = ngtcp2_conn_detect_lost_pkt(conn, loss_pktns, cstat, ts); if (rv != 0) { return rv; @@ -10714,35 +12745,26 @@ int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { return 0; } - if (!conn->server && !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + if (!conn->server && !conn_is_handshake_completed(conn)) { if (hs_pktns->crypto.tx.ckm) { hs_pktns->rtb.probe_pkt_left = 1; } else { in_pktns->rtb.probe_pkt_left = 1; } } else { - earliest_pktns = conn_get_earliest_pktns(conn, NULL, cstat->last_tx_pkt_ts); + if (in_pktns && in_pktns->rtb.num_pto_eliciting) { + in_pktns->rtb.probe_pkt_left = 1; - assert(earliest_pktns); + assert(hs_pktns); - switch (earliest_pktns->rtb.pktns_id) { - case NGTCP2_PKTNS_ID_INITIAL: - assert(in_pktns); - in_pktns->rtb.probe_pkt_left = 1; - if (!conn->server) { - break; + if (conn->server && hs_pktns->rtb.num_pto_eliciting) { + /* let server coalesce packets */ + hs_pktns->rtb.probe_pkt_left = 1; } - /* fall through for server so that it can coalesce packets. */ - /* fall through */ - case NGTCP2_PKTNS_ID_HANDSHAKE: - assert(hs_pktns); + } else if (hs_pktns && hs_pktns->rtb.num_pto_eliciting) { hs_pktns->rtb.probe_pkt_left = 1; - break; - case NGTCP2_PKTNS_ID_APPLICATION: + } else { conn->pktns.rtb.probe_pkt_left = 2; - break; - default: - assert(0); } } @@ -10756,6 +12778,34 @@ int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { return 0; } +static int conn_buffer_crypto_data(ngtcp2_conn *conn, const uint8_t **pdata, + ngtcp2_pktns *pktns, const uint8_t *data, + size_t datalen) { + int rv; + ngtcp2_buf_chain **pbufchain = &pktns->crypto.tx.data; + + if (*pbufchain) { + for (; (*pbufchain)->next; pbufchain = &(*pbufchain)->next) + ; + + if (ngtcp2_buf_left(&(*pbufchain)->buf) < datalen) { + pbufchain = &(*pbufchain)->next; + } + } + + if (!*pbufchain) { + rv = ngtcp2_buf_chain_new(pbufchain, ngtcp2_max(1024, datalen), conn->mem); + if (rv != 0) { + return rv; + } + } + + *pdata = (*pbufchain)->buf.last; + (*pbufchain)->buf.last = ngtcp2_cpymem((*pbufchain)->buf.last, data, datalen); + + return 0; +} + int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, const uint8_t *data, const size_t datalen) { @@ -10784,7 +12834,12 @@ int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn, return NGTCP2_ERR_INVALID_ARGUMENT; } - rv = ngtcp2_frame_chain_new(&frc, conn->mem); + rv = conn_buffer_crypto_data(conn, &data, pktns, data, datalen); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_frame_chain_objalloc_new(&frc, &conn->frc_objalloc); if (rv != 0) { return rv; } @@ -10799,7 +12854,7 @@ int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn, rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &fr->offset, frc); if (rv != 0) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } @@ -10813,24 +12868,18 @@ int ngtcp2_conn_submit_new_token(ngtcp2_conn *conn, const uint8_t *token, size_t tokenlen) { int rv; ngtcp2_frame_chain *nfrc; - uint8_t *p; + ngtcp2_vec tokenv = {(uint8_t *)token, tokenlen}; assert(conn->server); assert(token); assert(tokenlen); - rv = ngtcp2_frame_chain_extralen_new(&nfrc, tokenlen, conn->mem); + rv = ngtcp2_frame_chain_new_token_objalloc_new( + &nfrc, &tokenv, &conn->frc_objalloc, conn->mem); if (rv != 0) { return rv; } - nfrc->fr.type = NGTCP2_FRAME_NEW_TOKEN; - - p = (uint8_t *)nfrc + sizeof(*nfrc); - memcpy(p, token, tokenlen); - - ngtcp2_vec_init(&nfrc->fr.new_token.token, p, tokenlen); - nfrc->next = conn->pktns.tx.frq; conn->pktns.tx.frq = nfrc; @@ -10853,11 +12902,20 @@ int ngtcp2_conn_tx_strmq_push(ngtcp2_conn *conn, ngtcp2_strm *strm) { return ngtcp2_pq_push(&conn->tx.strmq, &strm->pe); } +static int conn_has_uncommited_preferred_address_cid(ngtcp2_conn *conn) { + return conn->server && + !(conn->flags & NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED) && + conn->oscid.datalen && + conn->local.transport_params.preferred_address_present; +} + size_t ngtcp2_conn_get_num_scid(ngtcp2_conn *conn) { - return ngtcp2_ksl_len(&conn->scid.set); + return ngtcp2_ksl_len(&conn->scid.set) + + (size_t)conn_has_uncommited_preferred_address_cid(conn); } size_t ngtcp2_conn_get_scid(ngtcp2_conn *conn, ngtcp2_cid *dest) { + ngtcp2_cid *origdest = dest; ngtcp2_ksl_it it; ngtcp2_scid *scid; @@ -10867,7 +12925,11 @@ size_t ngtcp2_conn_get_scid(ngtcp2_conn *conn, ngtcp2_cid *dest) { *dest++ = scid->cid; } - return ngtcp2_ksl_len(&conn->scid.set); + if (conn_has_uncommited_preferred_address_cid(conn)) { + *dest++ = conn->local.transport_params.preferred_address.cid; + } + + return (size_t)(dest - origdest); } size_t ngtcp2_conn_get_num_active_dcid(ngtcp2_conn *conn) { @@ -10889,7 +12951,7 @@ size_t ngtcp2_conn_get_num_active_dcid(ngtcp2_conn *conn) { } } - n += ngtcp2_ringbuf_len(&conn->dcid.retired); + n += ngtcp2_ringbuf_len(&conn->dcid.retired.rb); return n; } @@ -10899,9 +12961,10 @@ static void copy_dcid_to_cid_token(ngtcp2_cid_token *dest, dest->seq = src->seq; dest->cid = src->cid; ngtcp2_path_storage_init2(&dest->ps, &src->ps.path); - dest->token_present = - (uint8_t)!ngtcp2_check_invalid_stateless_reset_token(src->token); - memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN); + if ((dest->token_present = + (src->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) != 0)) { + memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN); + } } size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, ngtcp2_cid_token *dest) { @@ -10930,9 +12993,9 @@ size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, ngtcp2_cid_token *dest) { } } - len = ngtcp2_ringbuf_len(&conn->dcid.retired); + len = ngtcp2_ringbuf_len(&conn->dcid.retired.rb); for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i); + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, i); copy_dcid_to_cid_token(dest, dcid); ++dest; } @@ -10943,71 +13006,148 @@ size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, ngtcp2_cid_token *dest) { void ngtcp2_conn_set_local_addr(ngtcp2_conn *conn, const ngtcp2_addr *addr) { ngtcp2_addr *dest = &conn->dcid.current.ps.path.local; - assert(addr->addrlen <= sizeof(conn->dcid.current.ps.local_addrbuf)); + assert(addr->addrlen <= + (ngtcp2_socklen)sizeof(conn->dcid.current.ps.local_addrbuf)); ngtcp2_addr_copy(dest, addr); } -void ngtcp2_conn_set_remote_addr(ngtcp2_conn *conn, const ngtcp2_addr *addr) { - ngtcp2_addr *dest = &conn->dcid.current.ps.path.remote; - - assert(addr->addrlen <= sizeof(conn->dcid.current.ps.remote_addrbuf)); - ngtcp2_addr_copy(dest, addr); +void ngtcp2_conn_set_path_user_data(ngtcp2_conn *conn, void *path_user_data) { + conn->dcid.current.ps.path.user_data = path_user_data; } const ngtcp2_path *ngtcp2_conn_get_path(ngtcp2_conn *conn) { return &conn->dcid.current.ps.path; } -int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path, - ngtcp2_tstamp ts) { - int rv; - ngtcp2_dcid *dcid; +size_t ngtcp2_conn_get_max_udp_payload_size(ngtcp2_conn *conn) { + return conn->local.settings.max_udp_payload_size; +} - assert(!conn->server); +size_t ngtcp2_conn_get_path_max_udp_payload_size(ngtcp2_conn *conn) { + if (conn->local.settings.no_udp_payload_size_shaping) { + return ngtcp2_conn_get_max_udp_payload_size(conn); + } - conn->log.last_ts = ts; - conn->qlog.last_ts = ts; + return conn->dcid.current.max_udp_payload_size; +} - if (conn->remote.transport_params.disable_active_migration || +static int conn_initiate_migration_precheck(ngtcp2_conn *conn, + const ngtcp2_addr *local_addr) { + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) || + conn->remote.transport_params->disable_active_migration || conn->dcid.current.cid.datalen == 0 || - !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) { + (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_PREFERRED_ADDR))) { return NGTCP2_ERR_INVALID_STATE; } - if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + + if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { return NGTCP2_ERR_CONN_ID_BLOCKED; } - if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { + if (ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, local_addr)) { return NGTCP2_ERR_INVALID_ARGUMENT; } - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + return 0; +} + +int ngtcp2_conn_initiate_immediate_migration(ngtcp2_conn *conn, + const ngtcp2_path *path, + ngtcp2_tstamp ts) { + int rv; + ngtcp2_dcid *dcid; + + assert(!conn->server); + + conn->log.last_ts = ts; + conn->qlog.last_ts = ts; - rv = conn_stop_pv(conn, ts); + rv = conn_initiate_migration_precheck(conn, &path->local); if (rv != 0) { return rv; } + ngtcp2_conn_stop_pmtud(conn); + + if (conn->pv) { + rv = conn_abort_pv(conn, ts); + if (rv != 0) { + return rv; + } + } + rv = conn_retire_dcid(conn, &conn->dcid.current, ts); if (rv != 0) { return rv; } + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); + ngtcp2_dcid_set_path(dcid, path); + ngtcp2_dcid_copy(&conn->dcid.current, dcid); - ngtcp2_path_copy(&conn->dcid.current.ps.path, path); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); rv = conn_call_activate_dcid(conn, &conn->dcid.current); if (rv != 0) { return rv; } - conn_reset_congestion_state(conn); + conn_reset_congestion_state(conn, ts); conn_reset_ecn_validation_state(conn); + if (!conn->local.settings.no_pmtud) { + rv = conn_start_pmtud(conn); + if (rv != 0) { + return rv; + } + } + return 0; } +int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path, + ngtcp2_tstamp ts) { + int rv; + ngtcp2_dcid *dcid; + ngtcp2_duration pto, initial_pto, timeout; + ngtcp2_pv *pv; + + assert(!conn->server); + + conn->log.last_ts = ts; + conn->qlog.last_ts = ts; + + rv = conn_initiate_migration_precheck(conn, &path->local); + if (rv != 0) { + return rv; + } + + if (conn->pv) { + rv = conn_abort_pv(conn, ts); + if (rv != 0) { + return rv; + } + } + + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); + ngtcp2_dcid_set_path(dcid, path); + + pto = conn_compute_pto(conn, &conn->pktns); + initial_pto = conn_compute_initial_pto(conn, &conn->pktns); + timeout = 3 * ngtcp2_max(pto, initial_pto); + + rv = ngtcp2_pv_new(&pv, dcid, timeout, NGTCP2_PV_FLAG_NONE, &conn->log, + conn->mem); + if (rv != 0) { + return rv; + } + + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); + conn->pv = pv; + + return conn_call_activate_dcid(conn, &pv->dcid); +} + uint64_t ngtcp2_conn_get_max_local_streams_uni(ngtcp2_conn *conn) { return conn->local.uni.max_streams; } @@ -11016,6 +13156,17 @@ uint64_t ngtcp2_conn_get_max_data_left(ngtcp2_conn *conn) { return conn->tx.max_offset - conn->tx.offset; } +uint64_t ngtcp2_conn_get_max_stream_data_left(ngtcp2_conn *conn, + int64_t stream_id) { + ngtcp2_strm *strm = ngtcp2_conn_find_stream(conn, stream_id); + + if (strm == NULL) { + return 0; + } + + return strm->tx.max_offset - strm->tx.offset; +} + uint64_t ngtcp2_conn_get_streams_bidi_left(ngtcp2_conn *conn) { uint64_t n = ngtcp2_ord_stream_id(conn->local.bidi.next_stream_id); @@ -11031,6 +13182,17 @@ uint64_t ngtcp2_conn_get_streams_uni_left(ngtcp2_conn *conn) { : conn->local.uni.max_streams - n + 1; } +uint64_t ngtcp2_conn_get_cwnd_left(ngtcp2_conn *conn) { + uint64_t bytes_in_flight = conn->cstat.bytes_in_flight; + uint64_t cwnd = conn_get_cwnd(conn); + + if (cwnd > bytes_in_flight) { + return cwnd - bytes_in_flight; + } + + return 0; +} + ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn) { ngtcp2_duration trpto; ngtcp2_duration idle_timeout; @@ -11038,33 +13200,30 @@ ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn) { /* TODO Remote max_idle_timeout becomes effective after handshake completion. */ - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) || - conn->remote.transport_params.max_idle_timeout == 0 || + if (!conn_is_handshake_completed(conn) || + conn->remote.transport_params->max_idle_timeout == 0 || (conn->local.transport_params.max_idle_timeout && conn->local.transport_params.max_idle_timeout < - conn->remote.transport_params.max_idle_timeout)) { + conn->remote.transport_params->max_idle_timeout)) { idle_timeout = conn->local.transport_params.max_idle_timeout; } else { - idle_timeout = conn->remote.transport_params.max_idle_timeout; + idle_timeout = conn->remote.transport_params->max_idle_timeout; } if (idle_timeout == 0) { return UINT64_MAX; } - trpto = 3 * conn_compute_pto( - conn, (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) - ? &conn->pktns - : conn->hs_pktns); + trpto = 3 * conn_compute_pto(conn, conn_is_handshake_completed(conn) + ? &conn->pktns + : conn->hs_pktns); return conn->idle_ts + ngtcp2_max(idle_timeout, trpto); } ngtcp2_duration ngtcp2_conn_get_pto(ngtcp2_conn *conn) { - return conn_compute_pto(conn, - (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) - ? &conn->pktns - : conn->hs_pktns); + return conn_compute_pto( + conn, conn_is_handshake_completed(conn) ? &conn->pktns : conn->hs_pktns); } void ngtcp2_conn_set_initial_crypto_ctx(ngtcp2_conn *conn, @@ -11116,9 +13275,9 @@ void ngtcp2_conn_set_tls_native_handle(ngtcp2_conn *conn, conn->crypto.tls_native_handle = tls_native_handle; } -void ngtcp2_conn_get_connection_close_error_code( - ngtcp2_conn *conn, ngtcp2_connection_close_error_code *ccec) { - *ccec = conn->rx.ccec; +void ngtcp2_conn_get_connection_close_error( + ngtcp2_conn *conn, ngtcp2_connection_close_error *ccerr) { + *ccerr = conn->rx.ccerr; } void ngtcp2_conn_set_tls_error(ngtcp2_conn *conn, int liberr) { @@ -11129,6 +13288,14 @@ int ngtcp2_conn_get_tls_error(ngtcp2_conn *conn) { return conn->crypto.tls_error; } +void ngtcp2_conn_set_tls_alert(ngtcp2_conn *conn, uint8_t alert) { + conn->crypto.tls_alert = alert; +} + +uint8_t ngtcp2_conn_get_tls_alert(ngtcp2_conn *conn) { + return conn->crypto.tls_alert; +} + int ngtcp2_conn_is_local_stream(ngtcp2_conn *conn, int64_t stream_id) { return conn_local_stream(conn, stream_id); } @@ -11152,6 +13319,71 @@ int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn, int64_t stream_id, return 0; } +void ngtcp2_conn_update_pkt_tx_time(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + if (!(conn->cstat.pacing_rate > 0) || conn->tx.pacing.pktlen == 0) { + return; + } + + conn->tx.pacing.next_ts = + ts + (ngtcp2_duration)((double)conn->tx.pacing.pktlen / + conn->cstat.pacing_rate); + conn->tx.pacing.pktlen = 0; +} + +size_t ngtcp2_conn_get_send_quantum(ngtcp2_conn *conn) { + return conn->cstat.send_quantum; +} + +int ngtcp2_conn_track_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq) { + size_t i; + + if (conn->dcid.retire_unacked.len >= + sizeof(conn->dcid.retire_unacked.seqs) / + sizeof(conn->dcid.retire_unacked.seqs[0])) { + return NGTCP2_ERR_CONNECTION_ID_LIMIT; + } + + /* Make sure that we do not have a duplicate */ + for (i = 0; i < conn->dcid.retire_unacked.len; ++i) { + if (conn->dcid.retire_unacked.seqs[i] == seq) { + assert(0); + } + } + + conn->dcid.retire_unacked.seqs[conn->dcid.retire_unacked.len++] = seq; + + return 0; +} + +void ngtcp2_conn_untrack_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq) { + size_t i; + + for (i = 0; i < conn->dcid.retire_unacked.len; ++i) { + if (conn->dcid.retire_unacked.seqs[i] != seq) { + continue; + } + + if (i != conn->dcid.retire_unacked.len - 1) { + conn->dcid.retire_unacked.seqs[i] = + conn->dcid.retire_unacked.seqs[conn->dcid.retire_unacked.len - 1]; + } + + --conn->dcid.retire_unacked.len; + + return; + } +} + +size_t ngtcp2_conn_get_stream_loss_count(ngtcp2_conn *conn, int64_t stream_id) { + ngtcp2_strm *strm = ngtcp2_conn_find_stream(conn, stream_id); + + if (strm == NULL) { + return 0; + } + + return strm->tx.loss_count; +} + void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent, const ngtcp2_path *path, const uint8_t *data) { @@ -11159,16 +13391,24 @@ void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent, memcpy(pcent->data, data, sizeof(pcent->data)); } -void ngtcp2_settings_default(ngtcp2_settings *settings) { +void ngtcp2_settings_default_versioned(int settings_version, + ngtcp2_settings *settings) { + (void)settings_version; + memset(settings, 0, sizeof(*settings)); settings->cc_algo = NGTCP2_CC_ALGO_CUBIC; settings->initial_rtt = NGTCP2_DEFAULT_INITIAL_RTT; settings->ack_thresh = 2; + settings->max_udp_payload_size = 1500 - 48; + settings->handshake_timeout = NGTCP2_DEFAULT_HANDSHAKE_TIMEOUT; } -void ngtcp2_transport_params_default(ngtcp2_transport_params *params) { +void ngtcp2_transport_params_default_versioned( + int transport_params_version, ngtcp2_transport_params *params) { + (void)transport_params_version; + memset(params, 0, sizeof(*params)); - params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE; + params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE; params->ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; params->max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY; params->active_connection_id_limit = @@ -11181,9 +13421,10 @@ void ngtcp2_transport_params_default(ngtcp2_transport_params *params) { here. */ ngtcp2_ssize ngtcp2_pkt_write_connection_close( uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, uint64_t error_code, ngtcp2_encrypt encrypt, - const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, - const uint8_t *iv, ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp, + const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason, + size_t reasonlen, ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, + ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp, const ngtcp2_crypto_cipher_ctx *hp_ctx) { ngtcp2_pkt_hd hd; ngtcp2_crypto_km ckm; @@ -11223,6 +13464,8 @@ ngtcp2_ssize ngtcp2_pkt_write_connection_close( fr.type = NGTCP2_FRAME_CONNECTION_CLOSE; fr.connection_close.error_code = error_code; + fr.connection_close.reasonlen = reasonlen; + fr.connection_close.reason = (uint8_t *)reason; rv = ngtcp2_ppe_encode_frame(&ppe, &fr); if (rv != 0) { @@ -11234,3 +13477,26 @@ ngtcp2_ssize ngtcp2_pkt_write_connection_close( } int ngtcp2_is_bidi_stream(int64_t stream_id) { return bidi_stream(stream_id); } + +uint32_t ngtcp2_select_version(const uint32_t *preferred_versions, + size_t preferred_versionslen, + const uint32_t *offered_versions, + size_t offered_versionslen) { + size_t i, j; + + if (!preferred_versionslen || !offered_versionslen) { + return 0; + } + + for (i = 0; i < preferred_versionslen; ++i) { + assert(ngtcp2_is_supported_version(preferred_versions[i])); + + for (j = 0; j < offered_versionslen; ++j) { + if (preferred_versions[i] == offered_versions[j]) { + return preferred_versions[i]; + } + } + } + + return 0; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h index 825e4502e13e03..b1c6564175d482 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h @@ -36,14 +36,16 @@ #include "ngtcp2_acktr.h" #include "ngtcp2_rtb.h" #include "ngtcp2_strm.h" -#include "ngtcp2_mem.h" #include "ngtcp2_idtr.h" #include "ngtcp2_str.h" #include "ngtcp2_pkt.h" #include "ngtcp2_log.h" #include "ngtcp2_pq.h" #include "ngtcp2_cc.h" +#include "ngtcp2_bbr.h" +#include "ngtcp2_bbr2.h" #include "ngtcp2_pv.h" +#include "ngtcp2_pmtud.h" #include "ngtcp2_cid.h" #include "ngtcp2_buf.h" #include "ngtcp2_ppe.h" @@ -117,6 +119,21 @@ typedef enum { packets sent in NGTCP2_ECN_STATE_TESTING period. */ #define NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS 10 +/* NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN is the maximum length + of reason phrase to remember. If the received reason phrase is + longer than this value, it is truncated. */ +#define NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN 1024 + +/* NGTCP2_WRITE_PKT_FLAG_NONE indicates that no flag is set. */ +#define NGTCP2_WRITE_PKT_FLAG_NONE 0x00u +/* NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING indicates that packet other + than Initial packet should be padded. Initial packet might be + padded based on QUIC requirement regardless of this flag. */ +#define NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING 0x01u +/* NGTCP2_WRITE_PKT_FLAG_MORE indicates that more frames might come + and it should be encoded into the current packet. */ +#define NGTCP2_WRITE_PKT_FLAG_MORE 0x02u + /* * ngtcp2_max_frame is defined so that it covers the largest ACK * frame. @@ -140,52 +157,69 @@ void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent, const uint8_t *data); /* NGTCP2_CONN_FLAG_NONE indicates that no flag is set. */ -#define NGTCP2_CONN_FLAG_NONE 0x00 -/* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED is set if handshake - completed. */ -#define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED 0x01 +#define NGTCP2_CONN_FLAG_NONE 0x00u +/* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED is set when TLS stack declares + that TLS handshake has completed. The condition of this + declaration varies between TLS implementations and this flag does + not indicate the completion of QUIC handshake. Some + implementations declare TLS handshake completion as server when + they write off Server Finished and before deriving application rx + secret. */ +#define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED 0x01u /* NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED is set if connection ID is negotiated. This is only used for client. */ -#define NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED 0x02 +#define NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED 0x02u /* NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED is set if transport parameters are received. */ -#define NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED 0x04 -/* NGTCP2_CONN_FLAG_RECV_PROTECTED_PKT is set when a protected packet - is received, and decrypted successfully. This flag is used to stop - retransmitting handshake packets. It might be replaced with an - another mechanism when we implement key update. */ -#define NGTCP2_CONN_FLAG_RECV_PROTECTED_PKT 0x08 +#define NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED 0x04u +/* NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED is set when a + local transport parameters are applied. */ +#define NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED 0x08u /* NGTCP2_CONN_FLAG_RECV_RETRY is set when a client receives Retry packet. */ -#define NGTCP2_CONN_FLAG_RECV_RETRY 0x10 +#define NGTCP2_CONN_FLAG_RECV_RETRY 0x10u /* NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED is set when 0-RTT packet is rejected by a peer. */ -#define NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED 0x20 +#define NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED 0x20u +/* NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED is set when the expired + keep-alive timer has been cancelled. */ +#define NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED 0x40u /* NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED is set when an endpoint confirmed completion of handshake. */ -#define NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED 0x80 +#define NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED 0x80u /* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED is set when the library transitions its state to "post handshake". */ -#define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED 0x0100 +#define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED 0x0100u /* NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT is set when the early handshake retransmission has done when server receives overlapping Initial crypto data. */ -#define NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT 0x0200 +#define NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT 0x0200u +/* NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT indicates that the local endpoint + sends a QUIC packet without Fixed Bit set if a remote endpoint + supports Greasing QUIC Bit extension. */ +#define NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT 0x0400u /* NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED is set when key update is not confirmed by the local endpoint. That is, it has not received ACK frame which acknowledges packet which is encrypted with new key. */ -#define NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED 0x0800 +#define NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED 0x0800u /* NGTCP2_CONN_FLAG_PPE_PENDING is set when NGTCP2_WRITE_STREAM_FLAG_MORE is used and the intermediate state of ngtcp2_ppe is stored in pkt struct of ngtcp2_conn. */ -#define NGTCP2_CONN_FLAG_PPE_PENDING 0x1000 +#define NGTCP2_CONN_FLAG_PPE_PENDING 0x1000u /* NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE is set when idle timer should be restarted on next write. */ -#define NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE 0x2000 +#define NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE 0x2000u /* NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED indicates that server as peer verified client address. This flag is only used by client. */ -#define NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED 0x4000 +#define NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED 0x4000u +/* NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED indicates that an early key is + installed. conn->early.ckm cannot be used for this purpose because + it might be discarded when a certain condition is met. */ +#define NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED 0x8000u +/* NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR is set when the local + endpoint has initiated key update. */ +#define NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR 0x10000u typedef struct ngtcp2_crypto_data { ngtcp2_buf buf; @@ -264,9 +298,9 @@ typedef struct ngtcp2_pktns { struct { /* ect0, ect1, ce are the ECN counts received in the latest ACK frame. */ - size_t ect0; - size_t ect1; - size_t ce; + uint64_t ect0; + uint64_t ect1; + uint64_t ce; } ack; } ecn; } rx; @@ -283,6 +317,8 @@ typedef struct ngtcp2_pktns { ngtcp2_crypto_km *ckm; /* hp_ctx is cipher context for packet header protection. */ ngtcp2_crypto_cipher_ctx hp_ctx; + /* data is the submitted crypto data. */ + ngtcp2_buf_chain *data; } tx; struct { @@ -308,16 +344,28 @@ typedef enum ngtcp2_ecn_state { NGTCP2_ECN_STATE_CAPABLE, } ngtcp2_ecn_state; +ngtcp2_static_ringbuf_def(dcid_bound, NGTCP2_MAX_BOUND_DCID_POOL_SIZE, + sizeof(ngtcp2_dcid)); +ngtcp2_static_ringbuf_def(dcid_unused, NGTCP2_MAX_DCID_POOL_SIZE, + sizeof(ngtcp2_dcid)); +ngtcp2_static_ringbuf_def(dcid_retired, NGTCP2_MAX_DCID_RETIRED_SIZE, + sizeof(ngtcp2_dcid)); +ngtcp2_static_ringbuf_def(path_challenge, 4, + sizeof(ngtcp2_path_challenge_entry)); + +ngtcp2_objalloc_def(strm, ngtcp2_strm, oplent); + struct ngtcp2_conn { + ngtcp2_objalloc frc_objalloc; + ngtcp2_objalloc rtb_entry_objalloc; + ngtcp2_objalloc strm_objalloc; ngtcp2_conn_state state; ngtcp2_callbacks callbacks; /* rcid is a connection ID present in Initial or 0-RTT packet from client as destination connection ID. Server uses this field to check that duplicated Initial or 0-RTT packet are indeed sent to - this connection. It is also sent to client as - original_destination_connection_id transport parameter. Client - uses this field to validate original_destination_connection_id - transport parameter if no Retry packet is involved. */ + this connection. Client uses this field to validate + original_destination_connection_id transport parameter. */ ngtcp2_cid rcid; /* oscid is the source connection ID initially used by the local endpoint. */ @@ -335,21 +383,25 @@ struct ngtcp2_conn { ngtcp2_dcid current; /* bound is a set of destination connection IDs which are bound to particular paths. These paths are not validated yet. */ - ngtcp2_ringbuf bound; + ngtcp2_static_ringbuf_dcid_bound bound; /* unused is a set of unused CID received from peer. */ - ngtcp2_ringbuf unused; + ngtcp2_static_ringbuf_dcid_unused unused; /* retired is a set of CID retired by local endpoint. Keep them in 3*PTO to catch packets in flight along the old path. */ - ngtcp2_ringbuf retired; + ngtcp2_static_ringbuf_dcid_retired retired; /* seqgap tracks received sequence numbers in order to ignore retransmitted duplicated NEW_CONNECTION_ID frame. */ ngtcp2_gaptr seqgap; /* retire_prior_to is the largest retire_prior_to received so far. */ uint64_t retire_prior_to; - /* num_retire_queued is the number of RETIRE_CONNECTION_ID frames - queued for transmission. */ - size_t num_retire_queued; + struct { + /* seqs contains sequence number of Connection ID whose + retirement is not acknowledged by the remote endpoint yet. */ + uint64_t seqs[NGTCP2_MAX_DCID_POOL_SIZE * 2]; + /* len is the number of sequence numbers that seq contains. */ + size_t len; + } retire_unacked; /* zerolen_seq is a pseudo sequence number of zero-length Destination Connection ID in order to distinguish between them. */ @@ -374,6 +426,9 @@ struct ngtcp2_conn { struct { /* strmq contains ngtcp2_strm which has frames to send. */ ngtcp2_pq strmq; + /* strmq_nretrans is the number of entries in strmq which has + stream data to resent. */ + size_t strmq_nretrans; /* ack is ACK frame. The underlying buffer is reused. */ ngtcp2_frame *ack; /* max_ack_blks is the number of additional ngtcp2_ack_blk which @@ -399,6 +454,16 @@ struct ngtcp2_conn { validation period. */ size_t dgram_sent; } ecn; + + struct { + /* pktlen is the number of bytes written before calling + ngtcp2_conn_update_pkt_tx_time which resets this field to + 0. */ + size_t pktlen; + /* next_ts is the time to send next packet. It is UINT64_MAX if + packet pacing is disabled or expired.*/ + ngtcp2_tstamp next_ts; + } pacing; } tx; struct { @@ -415,9 +480,9 @@ struct ngtcp2_conn { /* window is the connection-level flow control window size. */ uint64_t window; /* path_challenge stores received PATH_CHALLENGE data. */ - ngtcp2_ringbuf path_challenge; - /* ccec is the received connection close error code. */ - ngtcp2_connection_close_error_code ccec; + ngtcp2_static_ringbuf_path_challenge path_challenge; + /* ccerr is the received connection close error. */ + ngtcp2_connection_close_error ccerr; } rx; struct { @@ -427,6 +492,21 @@ struct ngtcp2_conn { /* discard_started_ts is the timestamp when the timer to discard early key has started. Used by server only. */ ngtcp2_tstamp discard_started_ts; + /* transport_params is the values remembered by client from the + previous session. These are set by + ngtcp2_conn_set_early_remote_transport_params(). Server does + not use this field. Server must not set values for these + parameters that are smaller than the remembered values. */ + struct { + uint64_t initial_max_streams_bidi; + uint64_t initial_max_streams_uni; + uint64_t initial_max_stream_data_bidi_local; + uint64_t initial_max_stream_data_bidi_remote; + uint64_t initial_max_stream_data_uni; + uint64_t initial_max_data; + uint64_t active_connection_id_limit; + uint64_t max_datagram_frame_size; + } transport_params; } early; struct { @@ -456,11 +536,11 @@ struct ngtcp2_conn { struct { /* transport_params is the received transport parameters during handshake. It is used for Short packet only. */ - ngtcp2_transport_params transport_params; + ngtcp2_transport_params *transport_params; /* pending_transport_params is received transport parameters during handshake. It is copied to transport_params when 1RTT key is available. */ - ngtcp2_transport_params pending_transport_params; + ngtcp2_transport_params *pending_transport_params; struct { ngtcp2_idtr idtr; /* unsent_max_streams is the maximum number of streams of peer @@ -519,6 +599,8 @@ struct ngtcp2_conn { ngtcp2_crypto_aead_ctx retry_aead_ctx; /* tls_error is TLS related error. */ int tls_error; + /* tls_alert is TLS alert generated by the local endpoint. */ + uint8_t tls_alert; /* decryption_failure_count is the number of received packets that fail authentication. */ uint64_t decryption_failure_count; @@ -533,14 +615,63 @@ struct ngtcp2_conn { ngtcp2_frame_chain **pfrc; int pkt_empty; int hd_logged; - uint8_t rtb_entry_flags; - int was_client_initial; + /* flags is bitwise OR of zero or more of + NGTCP2_RTB_ENTRY_FLAG_*. */ + uint16_t rtb_entry_flags; ngtcp2_ssize hs_spktlen; + int require_padding; } pkt; + struct { + /* last_ts is a timestamp when a last packet is sent or received + on a current path. */ + ngtcp2_tstamp last_ts; + /* timeout is keep-alive timeout. When it expires, a packet + should be sent to a current path to keep connection alive. It + might be used to keep NAT binding intact. If 0 is set, + keep-alive timer is disabled. */ + ngtcp2_duration timeout; + } keep_alive; + + struct { + /* Initial keys for negotiated version. If original version == + negotiated version, these fields are not used. */ + struct { + ngtcp2_crypto_km *ckm; + ngtcp2_crypto_cipher_ctx hp_ctx; + } rx; + struct { + ngtcp2_crypto_km *ckm; + ngtcp2_crypto_cipher_ctx hp_ctx; + } tx; + /* version is QUIC version that the above Initial keys are created + for. */ + uint32_t version; + /* preferred_versions is the array of versions that are preferred + by the local endpoint. Server negotiates one of those versions + in this array if a client initially selects a less preferred + version. Client uses this field and original_version field to + prevent version downgrade attack if it reacted upon Version + Negotiation packet. */ + uint32_t *preferred_versions; + /* preferred_versionslen is the number of versions stored in the + array pointed by preferred_versions. This field is only used + by server. */ + size_t preferred_versionslen; + /* other_versions is the versions that the local endpoint sends in + version_information transport parameter. This is the wire + image of other_versions field of version_information transport + parameter. */ + uint8_t *other_versions; + /* other_versionslen is the length of data pointed by + other_versions field. */ + size_t other_versionslen; + } vneg; + ngtcp2_map strms; ngtcp2_conn_stat cstat; ngtcp2_pv *pv; + ngtcp2_pmtud *pmtud; ngtcp2_log log; ngtcp2_qlog qlog; ngtcp2_rst rst; @@ -550,9 +681,10 @@ struct ngtcp2_conn { /* idle_ts is the time instant when idle timer started. */ ngtcp2_tstamp idle_ts; void *user_data; - uint32_t version; + uint32_t client_chosen_version; + uint32_t negotiated_version; /* flags is bitwise OR of zero or more of NGTCP2_CONN_FLAG_*. */ - uint16_t flags; + uint32_t flags; int server; }; @@ -583,6 +715,8 @@ typedef struct ngtcp2_vmsg_datagram { const ngtcp2_vec *data; /* datacnt is the number of ngtcp2_vec pointed by data. */ size_t datacnt; + /* dgram_id is an opaque identifier chosen by an application. */ + uint64_t dgram_id; /* flags is bitwise OR of zero or more of NGTCP2_WRITE_DATAGRAM_FLAG_*. */ uint32_t flags; @@ -647,8 +781,7 @@ int ngtcp2_conn_init_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, * NGTCP2_ERR_CALLBACK_FAILURE * User-defined callback function failed. */ -int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, - uint64_t app_error_code); +int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm); /* * ngtcp2_conn_close_stream closes stream |strm| if no further @@ -664,8 +797,7 @@ int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, * NGTCP2_ERR_CALLBACK_FAILURE * User-defined callback function failed. */ -int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm, - uint64_t app_error_code); +int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm); /* * ngtcp2_conn_update_rtt updates RTT measurements. |rtt| is a latest @@ -673,12 +805,20 @@ int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm, * ack_delay included in ACK frame. |ack_delay| is actually tainted * (sent by peer), so don't assume that |ack_delay| is always smaller * than, or equals to |rtt|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * RTT sample is ignored. */ -void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, - ngtcp2_duration ack_delay, ngtcp2_tstamp ts); +int ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, + ngtcp2_duration ack_delay, ngtcp2_tstamp ts); void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts); +int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts); + /* * ngtcp2_conn_detect_lost_pkt detects lost packets. * @@ -721,16 +861,17 @@ int ngtcp2_conn_tx_strmq_push(ngtcp2_conn *conn, ngtcp2_strm *strm); ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn); ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, - ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, ngtcp2_vmsg *vmsg, - ngtcp2_tstamp ts); + int pkt_info_version, ngtcp2_pkt_info *pi, + uint8_t *dest, size_t destlen, + ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts); /* - * ngtcp2_conn_write_single_frame_pkt writes a packet which contains |fr| - * frame only in the buffer pointed by |dest| whose length if + * ngtcp2_conn_write_single_frame_pkt writes a packet which contains + * |fr| frame only in the buffer pointed by |dest| whose length if * |destlen|. |type| is a long packet type to send. If |type| is 0, * Short packet is used. |dcid| is used as a destination connection - * ID. + * ID. |flags| is zero or more of NGTCP2_WRITE_PKT_FLAG_*. Only + * NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING is recognized. * * The packet written by this function will not be retransmitted. * @@ -742,8 +883,8 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, */ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, - uint8_t type, const ngtcp2_cid *dcid, ngtcp2_frame *fr, uint8_t rtb_flags, - const ngtcp2_path *path, ngtcp2_tstamp ts); + uint8_t type, uint8_t flags, const ngtcp2_cid *dcid, ngtcp2_frame *fr, + uint16_t rtb_entry_flags, const ngtcp2_path *path, ngtcp2_tstamp ts); /* * ngtcp2_conn_commit_local_transport_params commits the local @@ -819,4 +960,156 @@ void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn, */ ngtcp2_tstamp ngtcp2_conn_loss_detection_expiry(ngtcp2_conn *conn); +/** + * @function + * + * `ngtcp2_conn_get_idle_expiry` returns the time when a connection + * should be closed if it continues to be idle. If idle timeout is + * disabled, this function returns ``UINT64_MAX``. + */ +ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn); + +ngtcp2_duration ngtcp2_conn_compute_pto(ngtcp2_conn *conn, ngtcp2_pktns *pktns); + +/* + * ngtcp2_conn_track_retired_dcid_seq tracks the sequence number |seq| + * of unacknowledged retiring Destination Connection ID. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_CONNECTION_ID_LIMIT + * The number of unacknowledged retirement exceeds the limit. + */ +int ngtcp2_conn_track_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq); + +/* + * ngtcp2_conn_untrack_retired_dcid_seq deletes the sequence number + * |seq| of unacknowledged retiring Destination Connection ID. It is + * fine if such sequence number is not found. + */ +void ngtcp2_conn_untrack_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq); + +/* + * ngtcp2_conn_server_negotiate_version negotiates QUIC version. It + * is compatible version negotiation. It returns the negotiated QUIC + * version. This function must not be called by client. + */ +uint32_t +ngtcp2_conn_server_negotiate_version(ngtcp2_conn *conn, + const ngtcp2_version_info *version_info); + +/** + * @function + * + * `ngtcp2_conn_write_connection_close_pkt` writes a packet which + * contains a CONNECTION_CLOSE frame (type 0x1c) in the buffer pointed + * by |dest| whose capacity is |datalen|. + * + * If |path| is not ``NULL``, this function stores the network path + * with which the packet should be sent. Each addr field must point + * to the buffer which should be at least ``sizeof(struct + * sockaddr_storage)`` bytes long. The assignment might not be done + * if nothing is written to |dest|. + * + * If |pi| is not ``NULL``, this function stores packet metadata in it + * if it succeeds. The metadata includes ECN markings. + * + * This function must not be called from inside the callback + * functions. + * + * At the moment, successful call to this function makes connection + * close. We may change this behaviour in the future to allow + * graceful shutdown. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory + * :macro:`NGTCP2_ERR_NOBUF` + * Buffer is too small + * :macro:`NGTCP2_ERR_INVALID_STATE` + * The current state does not allow sending CONNECTION_CLOSE. + * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` + * Packet number is exhausted, and cannot send any more packet. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * User callback failed + */ +ngtcp2_ssize ngtcp2_conn_write_connection_close_pkt( + ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, uint64_t error_code, const uint8_t *reason, + size_t reasonlen, ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_write_application_close_pkt` writes a packet which + * contains a CONNECTION_CLOSE frame (type 0x1d) in the buffer pointed + * by |dest| whose capacity is |datalen|. + * + * If |path| is not ``NULL``, this function stores the network path + * with which the packet should be sent. Each addr field must point + * to the buffer which should be at least ``sizeof(struct + * sockaddr_storage)`` bytes long. The assignment might not be done + * if nothing is written to |dest|. + * + * If |pi| is not ``NULL``, this function stores packet metadata in it + * if it succeeds. The metadata includes ECN markings. + * + * If handshake has not been confirmed yet, CONNECTION_CLOSE (type + * 0x1c) with error code :macro:`NGTCP2_APPLICATION_ERROR` is written + * instead. + * + * This function must not be called from inside the callback + * functions. + * + * At the moment, successful call to this function makes connection + * close. We may change this behaviour in the future to allow + * graceful shutdown. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory + * :macro:`NGTCP2_ERR_NOBUF` + * Buffer is too small + * :macro:`NGTCP2_ERR_INVALID_STATE` + * The current state does not allow sending CONNECTION_CLOSE. + * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` + * Packet number is exhausted, and cannot send any more packet. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * User callback failed + */ +ngtcp2_ssize ngtcp2_conn_write_application_close_pkt( + ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, uint64_t app_error_code, const uint8_t *reason, + size_t reasonlen, ngtcp2_tstamp ts); + +int ngtcp2_conn_start_pmtud(ngtcp2_conn *conn); + +void ngtcp2_conn_stop_pmtud(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_set_remote_transport_params` sets transport parameter + * |params| from a remote endpoint to |conn|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_TRANSPORT_PARAM` + * Failed to validate a remote transport parameters. + * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE` + * Version negotiation failure. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * User callback failed + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory. + */ +int ngtcp2_conn_set_remote_transport_params( + ngtcp2_conn *conn, const ngtcp2_transport_params *params); + #endif /* NGTCP2_CONN_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.c index 9064218dacb61d..dcf72e4d0ec980 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.c @@ -29,6 +29,7 @@ #include "ngtcp2_str.h" #include "ngtcp2_pkt.h" +#include "ngtcp2_net.h" uint64_t ngtcp2_get_uint64(const uint8_t *p) { uint64_t n; @@ -155,13 +156,13 @@ uint8_t *ngtcp2_put_varint(uint8_t *p, uint64_t n) { return rv; } -uint8_t *ngtcp2_put_varint14(uint8_t *p, uint16_t n) { +uint8_t *ngtcp2_put_varint30(uint8_t *p, uint32_t n) { uint8_t *rv; - assert(n < 16384); + assert(n < 1073741824); - rv = ngtcp2_put_uint16be(p, n); - *p |= 0x40; + rv = ngtcp2_put_uint32be(p, n); + *p |= 0x80; return rv; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.h index dcff0fdb8c174b..99746fdb4cefb7 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.h @@ -29,107 +29,8 @@ # include #endif /* HAVE_CONFIG_H */ -#ifdef HAVE_ARPA_INET_H -# include -#endif /* HAVE_ARPA_INET_H */ - -#ifdef HAVE_NETINET_IN_H -# include -#endif /* HAVE_NETINET_IN_H */ - -#ifdef HAVE_BYTESWAP_H -# include -#endif /* HAVE_BYTESWAP_H */ - -#ifdef HAVE_ENDIAN_H -# include -#endif /* HAVE_ENDIAN_H */ - -#ifdef HAVE_SYS_ENDIAN_H -# include -#endif /* HAVE_SYS_ENDIAN_H */ - #include -#if defined(HAVE_BSWAP_64) || \ - (defined(HAVE_DECL_BSWAP_64) && HAVE_DECL_BSWAP_64 > 0) -# define ngtcp2_bswap64 bswap_64 -#else /* !HAVE_BSWAP_64 */ -# define ngtcp2_bswap64(N) \ - ((uint64_t)(ngtcp2_ntohl((uint32_t)(N))) << 32 | \ - ngtcp2_ntohl((uint32_t)((N) >> 32))) -#endif /* !HAVE_BSWAP_64 */ - -#if defined(HAVE_BE64TOH) || \ - (defined(HAVE_DECL_BE64TOH) && HAVE_DECL_BE64TOH > 0) -# define ngtcp2_ntohl64(N) be64toh(N) -# define ngtcp2_htonl64(N) htobe64(N) -#else /* !HAVE_BE64TOH */ -# if defined(WORDS_BIGENDIAN) -# define ngtcp2_ntohl64(N) (N) -# define ngtcp2_htonl64(N) (N) -# else /* !WORDS_BIGENDIAN */ -# define ngtcp2_ntohl64(N) ngtcp2_bswap64(N) -# define ngtcp2_htonl64(N) ngtcp2_bswap64(N) -# endif /* !WORDS_BIGENDIAN */ -#endif /* !HAVE_BE64TOH */ - -#if defined(WIN32) -/* Windows requires ws2_32 library for ntonl family functions. We - define inline functions for those function so that we don't have - dependeny on that lib. */ - -# ifdef _MSC_VER -# define STIN static __inline -# else -# define STIN static inline -# endif - -STIN uint32_t ngtcp2_htonl(uint32_t hostlong) { - uint32_t res; - unsigned char *p = (unsigned char *)&res; - *p++ = hostlong >> 24; - *p++ = (hostlong >> 16) & 0xffu; - *p++ = (hostlong >> 8) & 0xffu; - *p = hostlong & 0xffu; - return res; -} - -STIN uint16_t ngtcp2_htons(uint16_t hostshort) { - uint16_t res; - unsigned char *p = (unsigned char *)&res; - *p++ = hostshort >> 8; - *p = hostshort & 0xffu; - return res; -} - -STIN uint32_t ngtcp2_ntohl(uint32_t netlong) { - uint32_t res; - unsigned char *p = (unsigned char *)&netlong; - res = *p++ << 24; - res += *p++ << 16; - res += *p++ << 8; - res += *p; - return res; -} - -STIN uint16_t ngtcp2_ntohs(uint16_t netshort) { - uint16_t res; - unsigned char *p = (unsigned char *)&netshort; - res = *p++ << 8; - res += *p; - return res; -} - -#else /* !WIN32 */ - -# define ngtcp2_htonl htonl -# define ngtcp2_htons htons -# define ngtcp2_ntohl ntohl -# define ngtcp2_ntohs ntohs - -#endif /* !WIN32 */ - /* * ngtcp2_get_uint64 reads 8 bytes from |p| as 64 bits unsigned * integer encoded as network byte order, and returns it in host byte @@ -220,12 +121,12 @@ uint8_t *ngtcp2_put_uint16be(uint8_t *p, uint16_t n); uint8_t *ngtcp2_put_varint(uint8_t *p, uint64_t n); /* - * ngtcp2_put_varint14 writes |n| in |p| using variable-length integer - * encoding. |n| must be strictly less than 16384. The function - * always encodes |n| in 2 bytes. It returns the one beyond of the + * ngtcp2_put_varint30 writes |n| in |p| using variable-length integer + * encoding. |n| must be strictly less than 1073741824. The function + * always encodes |n| in 4 bytes. It returns the one beyond of the * last written position. */ -uint8_t *ngtcp2_put_varint14(uint8_t *p, uint16_t n); +uint8_t *ngtcp2_put_varint30(uint8_t *p, uint32_t n); /* * ngtcp2_put_pkt_num encodes |pkt_num| using |len| bytes. It diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.c index e11287fd4ad8dd..f7592f885b4cb2 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.c @@ -30,6 +30,7 @@ #include "ngtcp2_str.h" #include "ngtcp2_conv.h" #include "ngtcp2_conn.h" +#include "ngtcp2_net.h" int ngtcp2_crypto_km_new(ngtcp2_crypto_km **pckm, const uint8_t *secret, size_t secretlen, @@ -91,6 +92,8 @@ void ngtcp2_crypto_create_nonce(uint8_t *dest, const uint8_t *iv, size_t ivlen, size_t i; uint64_t n; + assert(ivlen >= 8); + memcpy(dest, iv, ivlen); n = ngtcp2_htonl64((uint64_t)pkt_num); @@ -146,14 +149,17 @@ static uint8_t *write_cid_param(uint8_t *p, ngtcp2_transport_param_id id, return p; } -ngtcp2_ssize -ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen, - ngtcp2_transport_params_type exttype, - const ngtcp2_transport_params *params) { +static const uint8_t empty_address[16]; + +ngtcp2_ssize ngtcp2_encode_transport_params_versioned( + uint8_t *dest, size_t destlen, ngtcp2_transport_params_type exttype, + int transport_params_version, const ngtcp2_transport_params *params) { uint8_t *p; size_t len = 0; /* For some reason, gcc 7.3.0 requires this initialization. */ size_t preferred_addrlen = 0; + size_t version_infolen = 0; + (void)transport_params_version; switch (exttype) { case NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO: @@ -218,7 +224,8 @@ ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen, len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI, params->initial_max_streams_uni); } - if (params->max_udp_payload_size != NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE) { + if (params->max_udp_payload_size != + NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE) { len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE, params->max_udp_payload_size); } @@ -249,6 +256,20 @@ ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen, len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE, params->max_datagram_frame_size); } + if (params->grease_quic_bit) { + len += ngtcp2_put_varint_len(NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT) + + ngtcp2_put_varint_len(0); + } + if (params->version_info_present) { + version_infolen = sizeof(uint32_t) + params->version_info.other_versionslen; + len += ngtcp2_put_varint_len( + NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT) + + ngtcp2_put_varint_len(version_infolen) + version_infolen; + } + + if (dest == NULL && destlen == 0) { + return (ngtcp2_ssize)len; + } if (destlen < len) { return NGTCP2_ERR_NOBUF; @@ -271,13 +292,25 @@ ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen, p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS); p = ngtcp2_put_varint(p, preferred_addrlen); - p = ngtcp2_cpymem(p, params->preferred_address.ipv4_addr, - sizeof(params->preferred_address.ipv4_addr)); - p = ngtcp2_put_uint16be(p, params->preferred_address.ipv4_port); + if (params->preferred_address.ipv4_present) { + p = ngtcp2_cpymem(p, params->preferred_address.ipv4_addr, + sizeof(params->preferred_address.ipv4_addr)); + p = ngtcp2_put_uint16be(p, params->preferred_address.ipv4_port); + } else { + p = ngtcp2_cpymem(p, empty_address, + sizeof(params->preferred_address.ipv4_addr)); + p = ngtcp2_put_uint16be(p, 0); + } - p = ngtcp2_cpymem(p, params->preferred_address.ipv6_addr, - sizeof(params->preferred_address.ipv6_addr)); - p = ngtcp2_put_uint16be(p, params->preferred_address.ipv6_port); + if (params->preferred_address.ipv6_present) { + p = ngtcp2_cpymem(p, params->preferred_address.ipv6_addr, + sizeof(params->preferred_address.ipv6_addr)); + p = ngtcp2_put_uint16be(p, params->preferred_address.ipv6_port); + } else { + p = ngtcp2_cpymem(p, empty_address, + sizeof(params->preferred_address.ipv6_addr)); + p = ngtcp2_put_uint16be(p, 0); + } *p++ = (uint8_t)params->preferred_address.cid.datalen; if (params->preferred_address.cid.datalen) { @@ -330,7 +363,8 @@ ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen, params->initial_max_streams_uni); } - if (params->max_udp_payload_size != NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE) { + if (params->max_udp_payload_size != + NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE) { p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE, params->max_udp_payload_size); } @@ -367,6 +401,21 @@ ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen, params->max_datagram_frame_size); } + if (params->grease_quic_bit) { + p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT); + p = ngtcp2_put_varint(p, 0); + } + + if (params->version_info_present) { + p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT); + p = ngtcp2_put_varint(p, version_infolen); + p = ngtcp2_put_uint32be(p, params->version_info.chosen_version); + if (params->version_info.other_versionslen) { + p = ngtcp2_cpymem(p, params->version_info.other_versions, + params->version_info.other_versionslen); + } + } + assert((size_t)(p - dest) == len); return (ngtcp2_ssize)len; @@ -467,9 +516,9 @@ static ngtcp2_ssize decode_cid_param(ngtcp2_cid *pdest, const uint8_t *p, return (ngtcp2_ssize)(p - begin); } -int ngtcp2_decode_transport_params(ngtcp2_transport_params *params, - ngtcp2_transport_params_type exttype, - const uint8_t *data, size_t datalen) { +int ngtcp2_decode_transport_params_versioned( + int transport_params_version, ngtcp2_transport_params *params, + ngtcp2_transport_params_type exttype, const uint8_t *data, size_t datalen) { const uint8_t *p, *end; size_t len; uint64_t param_type; @@ -477,9 +526,12 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params, ngtcp2_ssize nread; int initial_scid_present = 0; int original_dcid_present = 0; + size_t i; + (void)transport_params_version; - p = data; - end = data + datalen; + if (datalen == 0) { + return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM; + } /* Set default values */ memset(params, 0, sizeof(*params)); @@ -488,7 +540,7 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params, params->initial_max_stream_data_bidi_local = 0; params->initial_max_stream_data_bidi_remote = 0; params->initial_max_stream_data_uni = 0; - params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE; + params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE; params->ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; params->stateless_reset_token_present = 0; params->preferred_address_present = 0; @@ -502,10 +554,10 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params, memset(¶ms->retry_scid, 0, sizeof(params->retry_scid)); memset(¶ms->initial_scid, 0, sizeof(params->initial_scid)); memset(¶ms->original_dcid, 0, sizeof(params->original_dcid)); + params->version_info_present = 0; - if (datalen == 0) { - return 0; - } + p = data; + end = data + datalen; for (; (size_t)(end - p) >= 2;) { nread = decode_varint(¶m_type, p, end); @@ -634,12 +686,24 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params, params->preferred_address.ipv4_port = ngtcp2_get_uint16(p); p += sizeof(uint16_t); + if (params->preferred_address.ipv4_port || + memcmp(empty_address, params->preferred_address.ipv4_addr, + sizeof(params->preferred_address.ipv4_addr)) != 0) { + params->preferred_address.ipv4_present = 1; + } + memcpy(params->preferred_address.ipv6_addr, p, sizeof(params->preferred_address.ipv6_addr)); p += sizeof(params->preferred_address.ipv6_addr); params->preferred_address.ipv6_port = ngtcp2_get_uint16(p); p += sizeof(uint16_t); + if (params->preferred_address.ipv6_port || + memcmp(empty_address, params->preferred_address.ipv6_addr, + sizeof(params->preferred_address.ipv6_addr)) != 0) { + params->preferred_address.ipv6_present = 1; + } + /* cid */ params->preferred_address.cid.datalen = *p++; len += params->preferred_address.cid.datalen; @@ -720,6 +784,45 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params, } p += nread; break; + case NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT: + nread = decode_varint(&valuelen, p, end); + if (nread < 0 || valuelen != 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + params->grease_quic_bit = 1; + break; + case NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT: + nread = decode_varint(&valuelen, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + if ((size_t)(end - p) < valuelen) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + if (valuelen < sizeof(uint32_t) || (valuelen & 0x3)) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + params->version_info.chosen_version = ngtcp2_get_uint32(p); + if (params->version_info.chosen_version == 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += sizeof(uint32_t); + if (valuelen > sizeof(uint32_t)) { + params->version_info.other_versions = (uint8_t *)p; + params->version_info.other_versionslen = + (size_t)valuelen - sizeof(uint32_t); + + for (i = sizeof(uint32_t); i < valuelen; + i += sizeof(uint32_t), p += sizeof(uint32_t)) { + if (ngtcp2_get_uint32(p) == 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + } + } + params->version_info_present = 1; + break; default: /* Ignore unknown parameter */ nread = decode_varint(&valuelen, p, end); @@ -747,3 +850,76 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params, return 0; } + +static int transport_params_copy_new(ngtcp2_transport_params **pdest, + const ngtcp2_transport_params *src, + const ngtcp2_mem *mem) { + size_t len = sizeof(**pdest); + ngtcp2_transport_params *dest; + uint8_t *p; + + if (src->version_info_present) { + len += src->version_info.other_versionslen; + } + + dest = ngtcp2_mem_malloc(mem, len); + if (dest == NULL) { + return NGTCP2_ERR_NOMEM; + } + + *dest = *src; + + if (src->version_info_present && src->version_info.other_versionslen) { + p = (uint8_t *)dest + sizeof(*dest); + memcpy(p, src->version_info.other_versions, + src->version_info.other_versionslen); + dest->version_info.other_versions = p; + } + + *pdest = dest; + + return 0; +} + +int ngtcp2_decode_transport_params_new(ngtcp2_transport_params **pparams, + ngtcp2_transport_params_type exttype, + const uint8_t *data, size_t datalen, + const ngtcp2_mem *mem) { + int rv; + ngtcp2_transport_params params; + + rv = ngtcp2_decode_transport_params(¶ms, exttype, data, datalen); + if (rv < 0) { + return rv; + } + + if (mem == NULL) { + mem = ngtcp2_mem_default(); + } + + return transport_params_copy_new(pparams, ¶ms, mem); +} + +void ngtcp2_transport_params_del(ngtcp2_transport_params *params, + const ngtcp2_mem *mem) { + if (params == NULL) { + return; + } + + if (mem == NULL) { + mem = ngtcp2_mem_default(); + } + + ngtcp2_mem_free(mem, params); +} + +int ngtcp2_transport_params_copy_new(ngtcp2_transport_params **pdest, + const ngtcp2_transport_params *src, + const ngtcp2_mem *mem) { + if (src == NULL) { + *pdest = NULL; + return 0; + } + + return transport_params_copy_new(pdest, src, mem); +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.h index 6e6f12a0956ade..9a9d95f5b9fe82 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.h @@ -41,11 +41,38 @@ /* NGTCP2_MAX_AEAD_OVERHEAD is expected maximum AEAD overhead. */ #define NGTCP2_MAX_AEAD_OVERHEAD 16 +/* ngtcp2_transport_param_id is the registry of QUIC transport + parameter ID. */ +typedef enum ngtcp2_transport_param_id { + NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID = 0x0000, + NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT = 0x0001, + NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN = 0x0002, + NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE = 0x0003, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA = 0x0004, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL = 0x0005, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE = 0x0006, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI = 0x0007, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI = 0x0008, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI = 0x0009, + NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT = 0x000a, + NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY = 0x000b, + NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION = 0x000c, + NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS = 0x000d, + NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT = 0x000e, + NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID = 0x000f, + NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID = 0x0010, + /* https://datatracker.ietf.org/doc/html/rfc9221 */ + NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE = 0x0020, + NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT = 0x2ab2, + /* https://quicwg.org/quic-v2/draft-ietf-quic-v2.html */ + NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT = 0xff73db, +} ngtcp2_transport_param_id; + /* NGTCP2_CRYPTO_KM_FLAG_NONE indicates that no flag is set. */ -#define NGTCP2_CRYPTO_KM_FLAG_NONE 0x00 +#define NGTCP2_CRYPTO_KM_FLAG_NONE 0x00u /* NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE is set if key phase bit is set. */ -#define NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE 0x01 +#define NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE 0x01u typedef struct ngtcp2_crypto_km { ngtcp2_vec secret; @@ -100,4 +127,21 @@ typedef struct ngtcp2_crypto_cc { void ngtcp2_crypto_create_nonce(uint8_t *dest, const uint8_t *iv, size_t ivlen, int64_t pkt_num); +/* + * ngtcp2_transport_params_copy_new makes a copy of |src|, and assigns + * it to |*pdest|. If |src| is NULL, NULL is assigned to |*pdest|. + * + * Caller is responsible to call ngtcp2_transport_params_del to free + * the memory assigned to |*pdest|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_transport_params_copy_new(ngtcp2_transport_params **pdest, + const ngtcp2_transport_params *src, + const ngtcp2_mem *mem); + #endif /* NGTCP2_CRYPTO_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.c index bd15e0988be9d8..8f676da3ef0a13 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.c @@ -64,8 +64,8 @@ const char *ngtcp2_strerror(int liberr) { return "ERR_MALFORMED_TRANSPORT_PARAM"; case NGTCP2_ERR_FRAME_ENCODING: return "ERR_FRAME_ENCODING"; - case NGTCP2_ERR_TLS_DECRYPT: - return "ERR_TLS_DECRYPT"; + case NGTCP2_ERR_DECRYPT: + return "ERR_DECRYPT"; case NGTCP2_ERR_STREAM_SHUT_WR: return "ERR_STREAM_SHUT_WR"; case NGTCP2_ERR_STREAM_NOT_FOUND: @@ -82,8 +82,6 @@ const char *ngtcp2_strerror(int liberr) { return "ERR_TRANSPORT_PARAM"; case NGTCP2_ERR_DISCARD_PKT: return "ERR_DISCARD_PKT"; - case NGTCP2_ERR_PATH_VALIDATION_FAILED: - return "ERR_PATH_VALIDATION_FAILED"; case NGTCP2_ERR_CONN_ID_BLOCKED: return "ERR_CONN_ID_BLOCKED"; case NGTCP2_ERR_CALLBACK_FAILURE: @@ -102,6 +100,14 @@ const char *ngtcp2_strerror(int liberr) { return "ERR_AEAD_LIMIT_REACHED"; case NGTCP2_ERR_NO_VIABLE_PATH: return "ERR_NO_VIABLE_PATH"; + case NGTCP2_ERR_VERSION_NEGOTIATION: + return "ERR_VERSION_NEGOTIATION"; + case NGTCP2_ERR_HANDSHAKE_TIMEOUT: + return "ERR_HANDSHAKE_TIMEOUT"; + case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE: + return "ERR_VERSION_NEGOTIATION_FAILURE"; + case NGTCP2_ERR_IDLE_CLOSE: + return "ERR_IDLE_CLOSE"; default: return "(unknown)"; } @@ -129,6 +135,8 @@ uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr) { case NGTCP2_ERR_TRANSPORT_PARAM: return NGTCP2_TRANSPORT_PARAMETER_ERROR; case NGTCP2_ERR_INVALID_ARGUMENT: + case NGTCP2_ERR_NOMEM: + case NGTCP2_ERR_CALLBACK_FAILURE: return NGTCP2_INTERNAL_ERROR; case NGTCP2_ERR_STREAM_STATE: return NGTCP2_STREAM_STATE_ERROR; @@ -138,6 +146,8 @@ uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr) { return NGTCP2_AEAD_LIMIT_REACHED; case NGTCP2_ERR_NO_VIABLE_PATH: return NGTCP2_NO_VIABLE_PATH; + case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE: + return NGTCP2_VERSION_NEGOTIATION_ERROR_DRAFT; default: return NGTCP2_PROTOCOL_VIOLATION; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c index 6e7f3b7e554826..87c23898e8207d 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c @@ -23,29 +23,26 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "ngtcp2_gaptr.h" -#include "ngtcp2_range.h" #include #include -int ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem) { - int rv; - ngtcp2_range range = {0, UINT64_MAX}; +void ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem) { + ngtcp2_ksl_init(&gaptr->gap, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), + mem); - rv = ngtcp2_ksl_init(&gaptr->gap, ngtcp2_ksl_range_compar, - sizeof(ngtcp2_range), mem); - if (rv != 0) { - return rv; - } + gaptr->mem = mem; +} + +static int gaptr_gap_init(ngtcp2_gaptr *gaptr) { + ngtcp2_range range = {0, UINT64_MAX}; + int rv; rv = ngtcp2_ksl_insert(&gaptr->gap, NULL, &range, NULL); if (rv != 0) { - ngtcp2_ksl_free(&gaptr->gap); return rv; } - gaptr->mem = mem; - return 0; } @@ -57,11 +54,18 @@ void ngtcp2_gaptr_free(ngtcp2_gaptr *gaptr) { ngtcp2_ksl_free(&gaptr->gap); } -int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen) { +int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, uint64_t datalen) { int rv; ngtcp2_range k, m, l, r, q = {offset, offset + datalen}; ngtcp2_ksl_it it; + if (ngtcp2_ksl_len(&gaptr->gap) == 0) { + rv = gaptr_gap_init(gaptr); + if (rv != 0) { + return rv; + } + } + it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q, ngtcp2_ksl_range_exclusive_compar); @@ -73,7 +77,7 @@ int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen) { } if (ngtcp2_range_eq(&k, &m)) { - ngtcp2_ksl_remove(&gaptr->gap, &it, &k); + ngtcp2_ksl_remove_hint(&gaptr->gap, &it, &it, &k); continue; } ngtcp2_range_cut(&l, &r, &k, &m); @@ -95,35 +99,69 @@ int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen) { } uint64_t ngtcp2_gaptr_first_gap_offset(ngtcp2_gaptr *gaptr) { - ngtcp2_ksl_it it = ngtcp2_ksl_begin(&gaptr->gap); - ngtcp2_range r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + ngtcp2_ksl_it it; + ngtcp2_range r; + + if (ngtcp2_ksl_len(&gaptr->gap) == 0) { + return 0; + } + + it = ngtcp2_ksl_begin(&gaptr->gap); + r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + return r.begin; } -ngtcp2_ksl_it ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr, - uint64_t offset) { +ngtcp2_range ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr, + uint64_t offset) { ngtcp2_range q = {offset, offset + 1}; - return ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q, - ngtcp2_ksl_range_exclusive_compar); + ngtcp2_ksl_it it; + + if (ngtcp2_ksl_len(&gaptr->gap) == 0) { + ngtcp2_range r = {0, UINT64_MAX}; + return r; + } + + it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q, + ngtcp2_ksl_range_exclusive_compar); + + assert(!ngtcp2_ksl_it_end(&it)); + + return *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); } int ngtcp2_gaptr_is_pushed(ngtcp2_gaptr *gaptr, uint64_t offset, - size_t datalen) { + uint64_t datalen) { ngtcp2_range q = {offset, offset + datalen}; - ngtcp2_ksl_it it = ngtcp2_ksl_lower_bound_compar( - &gaptr->gap, &q, ngtcp2_ksl_range_exclusive_compar); - ngtcp2_range k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); - ngtcp2_range m = ngtcp2_range_intersect(&q, &k); + ngtcp2_ksl_it it; + ngtcp2_range k; + ngtcp2_range m; + + if (ngtcp2_ksl_len(&gaptr->gap) == 0) { + return 0; + } + + it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q, + ngtcp2_ksl_range_exclusive_compar); + k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + m = ngtcp2_range_intersect(&q, &k); + return ngtcp2_range_len(&m) == 0; } void ngtcp2_gaptr_drop_first_gap(ngtcp2_gaptr *gaptr) { - ngtcp2_ksl_it it = ngtcp2_ksl_begin(&gaptr->gap); + ngtcp2_ksl_it it; ngtcp2_range r; + if (ngtcp2_ksl_len(&gaptr->gap) == 0) { + return; + } + + it = ngtcp2_ksl_begin(&gaptr->gap); + assert(!ngtcp2_ksl_it_end(&it)); r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); - ngtcp2_ksl_remove(&gaptr->gap, NULL, &r); + ngtcp2_ksl_remove_hint(&gaptr->gap, NULL, &it, &r); } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.h index 500d376008ac29..0f100a81c4286c 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.h @@ -33,6 +33,7 @@ #include "ngtcp2_mem.h" #include "ngtcp2_ksl.h" +#include "ngtcp2_range.h" /* * ngtcp2_gaptr maintains the gap in the range [0, UINT64_MAX). @@ -47,14 +48,8 @@ typedef struct ngtcp2_gaptr { /* * ngtcp2_gaptr_init initializes |gaptr|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. */ -int ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem); +void ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem); /* * ngtcp2_gaptr_free frees resources allocated for |gaptr|. @@ -71,7 +66,7 @@ void ngtcp2_gaptr_free(ngtcp2_gaptr *gaptr); * NGTCP2_ERR_NOMEM * Out of memory */ -int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen); +int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, uint64_t datalen); /* * ngtcp2_gaptr_first_gap_offset returns the offset to the first gap. @@ -80,18 +75,18 @@ int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen); uint64_t ngtcp2_gaptr_first_gap_offset(ngtcp2_gaptr *gaptr); /* - * ngtcp2_gaptr_get_first_gap_after returns the iterator pointing to - * the first gap which overlaps or comes after |offset|. + * ngtcp2_gaptr_get_first_gap_after returns the first gap which + * overlaps or comes after |offset|. */ -ngtcp2_ksl_it ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr, - uint64_t offset); +ngtcp2_range ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr, + uint64_t offset); /* * ngtcp2_gaptr_is_pushed returns nonzero if range [offset, offset + * datalen) is completely pushed into this object. */ int ngtcp2_gaptr_is_pushed(ngtcp2_gaptr *gaptr, uint64_t offset, - size_t datalen); + uint64_t datalen); /* * ngtcp2_gaptr_drop_first_gap deletes the first gap entirely as if diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.c index f04806b4a8b22f..d9880227690faf 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.c @@ -26,17 +26,10 @@ #include -int ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem) { - int rv; - - rv = ngtcp2_gaptr_init(&idtr->gap, mem); - if (rv != 0) { - return rv; - } +void ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem) { + ngtcp2_gaptr_init(&idtr->gap, mem); idtr->server = server; - - return 0; } void ngtcp2_idtr_free(ngtcp2_idtr *idtr) { diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.h index 1be64dc16e7ae7..edb8c68c8db9b5 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.h @@ -51,14 +51,8 @@ typedef struct ngtcp2_idtr { * * If this object records server initiated ID (even number), set * |server| to nonzero. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. */ -int ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem); +void ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem); /* * ngtcp2_idtr_free frees resources allocated for |idtr|. diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c index fd25e3514e827b..0bd424cb0bc1f1 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c @@ -33,9 +33,11 @@ #include "ngtcp2_mem.h" #include "ngtcp2_range.h" +static ngtcp2_ksl_blk null_blk = {{{NULL, NULL, 0, 0, {0}}}}; + static size_t ksl_nodelen(size_t keylen) { - return (sizeof(ngtcp2_ksl_node) + keylen - sizeof(uint64_t) + 0xf) & - (size_t)~0xf; + return (sizeof(ngtcp2_ksl_node) + keylen - sizeof(uint64_t) + 0xfu) & + ~(uintptr_t)0xfu; } static size_t ksl_blklen(size_t nodelen) { @@ -51,31 +53,48 @@ static void ksl_node_set_key(ngtcp2_ksl *ksl, ngtcp2_ksl_node *node, memcpy(node->key, key, ksl->keylen); } -int ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen, - const ngtcp2_mem *mem) { +void ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen, + const ngtcp2_mem *mem) { size_t nodelen = ksl_nodelen(keylen); - size_t blklen = ksl_blklen(nodelen); - ngtcp2_ksl_blk *head; - ksl->head = ngtcp2_mem_malloc(mem, blklen); - if (!ksl->head) { - return NGTCP2_ERR_NOMEM; - } - ksl->front = ksl->back = ksl->head; + ngtcp2_objalloc_init(&ksl->blkalloc, + ((ksl_blklen(nodelen) + 0xfu) & ~(uintptr_t)0xfu) * 8, + mem); + + ksl->head = NULL; + ksl->front = ksl->back = NULL; ksl->compar = compar; ksl->keylen = keylen; ksl->nodelen = nodelen; ksl->n = 0; - ksl->mem = mem; +} + +static ngtcp2_ksl_blk *ksl_blk_objalloc_new(ngtcp2_ksl *ksl) { + return ngtcp2_objalloc_ksl_blk_len_get(&ksl->blkalloc, + ksl_blklen(ksl->nodelen)); +} + +static void ksl_blk_objalloc_del(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) { + ngtcp2_objalloc_ksl_blk_release(&ksl->blkalloc, blk); +} + +static int ksl_head_init(ngtcp2_ksl *ksl) { + ngtcp2_ksl_blk *head = ksl_blk_objalloc_new(ksl); + if (!head) { + return NGTCP2_ERR_NOMEM; + } - head = ksl->head; head->next = head->prev = NULL; head->n = 0; head->leaf = 1; + ksl->head = head; + ksl->front = ksl->back = head; + return 0; } +#ifdef NOMEMPOOL /* * ksl_free_blk frees |blk| recursively. */ @@ -88,15 +107,20 @@ static void ksl_free_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) { } } - ngtcp2_mem_free(ksl->mem, blk); + ksl_blk_objalloc_del(ksl, blk); } +#endif /* NOMEMPOOL */ void ngtcp2_ksl_free(ngtcp2_ksl *ksl) { - if (!ksl) { + if (!ksl || !ksl->head) { return; } +#ifdef NOMEMPOOL ksl_free_blk(ksl, ksl->head); +#endif /* NOMEMPOOL */ + + ngtcp2_objalloc_free(&ksl->blkalloc); } /* @@ -110,7 +134,7 @@ void ngtcp2_ksl_free(ngtcp2_ksl *ksl) { static ngtcp2_ksl_blk *ksl_split_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) { ngtcp2_ksl_blk *rblk; - rblk = ngtcp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); + rblk = ksl_blk_objalloc_new(ksl); if (rblk == NULL) { return NULL; } @@ -194,9 +218,9 @@ static int ksl_split_head(ngtcp2_ksl *ksl) { lblk = ksl->head; - nhead = ngtcp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); + nhead = ksl_blk_objalloc_new(ksl); if (nhead == NULL) { - ngtcp2_mem_free(ksl->mem, rblk); + ksl_blk_objalloc_del(ksl, rblk); return NGTCP2_ERR_NOMEM; } nhead->next = nhead->prev = NULL; @@ -240,29 +264,33 @@ static void ksl_insert_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i, static size_t ksl_bsearch(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, const ngtcp2_ksl_key *key, ngtcp2_ksl_compar compar) { - ngtcp2_ssize left = -1, right = (ngtcp2_ssize)blk->n, mid; + size_t i; ngtcp2_ksl_node *node; - while (right - left > 1) { - mid = (left + right) >> 1; - node = ngtcp2_ksl_nth_node(ksl, blk, (size_t)mid); - if (compar((ngtcp2_ksl_key *)node->key, key)) { - left = mid; - } else { - right = mid; - } - } + for (i = 0, node = (ngtcp2_ksl_node *)(void *)blk->nodes; + i < blk->n && compar((ngtcp2_ksl_key *)node->key, key); + ++i, node = (ngtcp2_ksl_node *)(void *)((uint8_t *)node + ksl->nodelen)) + ; - return (size_t)right; + return i; } int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, const ngtcp2_ksl_key *key, void *data) { - ngtcp2_ksl_blk *blk = ksl->head; + ngtcp2_ksl_blk *blk; ngtcp2_ksl_node *node; size_t i; int rv; + if (!ksl->head) { + rv = ksl_head_init(ksl); + if (rv != 0) { + return rv; + } + } + + blk = ksl->head; + if (blk->n == NGTCP2_KSL_MAX_NBLK) { rv = ksl_split_head(ksl); if (rv != 0) { @@ -374,10 +402,10 @@ static ngtcp2_ksl_blk *ksl_merge_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, ksl->back = lblk; } - ngtcp2_mem_free(ksl->mem, rblk); + ksl_blk_objalloc_del(ksl, rblk); if (ksl->head == blk && blk->n == 2) { - ngtcp2_mem_free(ksl->mem, ksl->head); + ksl_blk_objalloc_del(ksl, ksl->head); ksl->head = lblk; } else { ksl_remove_node(ksl, blk, i + 1); @@ -469,12 +497,42 @@ static int key_equal(ngtcp2_ksl_compar compar, const ngtcp2_ksl_key *lhs, return !compar(lhs, rhs) && !compar(rhs, lhs); } +int ngtcp2_ksl_remove_hint(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, + const ngtcp2_ksl_it *hint, + const ngtcp2_ksl_key *key) { + ngtcp2_ksl_blk *blk = hint->blk; + + assert(ksl->head); + + if (blk->n <= NGTCP2_KSL_MIN_NBLK) { + return ngtcp2_ksl_remove(ksl, it, key); + } + + ksl_remove_node(ksl, blk, hint->i); + + --ksl->n; + + if (it) { + if (hint->i == blk->n && blk->next) { + ngtcp2_ksl_it_init(it, ksl, blk->next, 0); + } else { + ngtcp2_ksl_it_init(it, ksl, blk, hint->i); + } + } + + return 0; +} + int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, const ngtcp2_ksl_key *key) { ngtcp2_ksl_blk *blk = ksl->head; ngtcp2_ksl_node *node; size_t i; + if (!ksl->head) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + if (!blk->leaf && blk->n == 2 && ngtcp2_ksl_nth_node(ksl, blk, 0)->blk->n == NGTCP2_KSL_MIN_NBLK && ngtcp2_ksl_nth_node(ksl, blk, 1)->blk->n == NGTCP2_KSL_MIN_NBLK) { @@ -550,6 +608,11 @@ ngtcp2_ksl_it ngtcp2_ksl_lower_bound(ngtcp2_ksl *ksl, ngtcp2_ksl_it it; size_t i; + if (!blk) { + ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0); + return it; + } + for (;;) { i = ksl_bsearch(ksl, blk, key, ksl->compar); @@ -587,6 +650,11 @@ ngtcp2_ksl_it ngtcp2_ksl_lower_bound_compar(ngtcp2_ksl *ksl, ngtcp2_ksl_it it; size_t i; + if (!blk) { + ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0); + return it; + } + for (;;) { i = ksl_bsearch(ksl, blk, key, compar); @@ -623,6 +691,8 @@ void ngtcp2_ksl_update_key(ngtcp2_ksl *ksl, const ngtcp2_ksl_key *old_key, ngtcp2_ksl_node *node; size_t i; + assert(ksl->head); + for (;;) { i = ksl_bsearch(ksl, blk, old_key, ksl->compar); @@ -667,36 +737,49 @@ static void ksl_print(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t level) { size_t ngtcp2_ksl_len(ngtcp2_ksl *ksl) { return ksl->n; } void ngtcp2_ksl_clear(ngtcp2_ksl *ksl) { - size_t i; - ngtcp2_ksl_blk *head; - - if (!ksl->head->leaf) { - for (i = 0; i < ksl->head->n; ++i) { - ksl_free_blk(ksl, ngtcp2_ksl_nth_node(ksl, ksl->head, i)->blk); - } + if (!ksl->head) { + return; } - ksl->front = ksl->back = ksl->head; - ksl->n = 0; +#ifdef NOMEMPOOL + ksl_free_blk(ksl, ksl->head); +#endif /* NOMEMPOOL */ - head = ksl->head; + ksl->front = ksl->back = ksl->head = NULL; + ksl->n = 0; - head->next = head->prev = NULL; - head->n = 0; - head->leaf = 1; + ngtcp2_objalloc_clear(&ksl->blkalloc); } -void ngtcp2_ksl_print(ngtcp2_ksl *ksl) { ksl_print(ksl, ksl->head, 0); } +void ngtcp2_ksl_print(ngtcp2_ksl *ksl) { + if (!ksl->head) { + return; + } + + ksl_print(ksl, ksl->head, 0); +} ngtcp2_ksl_it ngtcp2_ksl_begin(const ngtcp2_ksl *ksl) { ngtcp2_ksl_it it; - ngtcp2_ksl_it_init(&it, ksl, ksl->front, 0); + + if (ksl->head) { + ngtcp2_ksl_it_init(&it, ksl, ksl->front, 0); + } else { + ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0); + } + return it; } ngtcp2_ksl_it ngtcp2_ksl_end(const ngtcp2_ksl *ksl) { ngtcp2_ksl_it it; - ngtcp2_ksl_it_init(&it, ksl, ksl->back, ksl->back->n); + + if (ksl->head) { + ngtcp2_ksl_it_init(&it, ksl, ksl->back, ksl->back->n); + } else { + ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0); + } + return it; } @@ -707,11 +790,6 @@ void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl, it->i = i; } -void *ngtcp2_ksl_it_get(const ngtcp2_ksl_it *it) { - assert(it->i < it->blk->n); - return ngtcp2_ksl_nth_node(it->ksl, it->blk, it->i)->data; -} - void ngtcp2_ksl_it_prev(ngtcp2_ksl_it *it) { assert(!ngtcp2_ksl_it_begin(it)); diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h index f24487d03e9524..312a151d4aa9ec 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h @@ -33,6 +33,8 @@ #include +#include "ngtcp2_objalloc.h" + /* * Skip List using single key instead of range. */ @@ -79,25 +81,33 @@ struct ngtcp2_ksl_node { * ngtcp2_ksl_blk contains ngtcp2_ksl_node objects. */ struct ngtcp2_ksl_blk { - /* next points to the next block if leaf field is nonzero. */ - ngtcp2_ksl_blk *next; - /* prev points to the previous block if leaf field is nonzero. */ - ngtcp2_ksl_blk *prev; - /* n is the number of nodes this object contains in nodes. */ - uint32_t n; - /* leaf is nonzero if this block contains leaf nodes. */ - uint32_t leaf; union { - uint64_t align; - /* nodes is a buffer to contain NGTCP2_KSL_MAX_NBLK - ngtcp2_ksl_node objects. Because ngtcp2_ksl_node object is - allocated along with the additional variable length key - storage, the size of buffer is unknown until ngtcp2_ksl_init is - called. */ - uint8_t nodes[1]; + struct { + /* next points to the next block if leaf field is nonzero. */ + ngtcp2_ksl_blk *next; + /* prev points to the previous block if leaf field is nonzero. */ + ngtcp2_ksl_blk *prev; + /* n is the number of nodes this object contains in nodes. */ + uint32_t n; + /* leaf is nonzero if this block contains leaf nodes. */ + uint32_t leaf; + union { + uint64_t align; + /* nodes is a buffer to contain NGTCP2_KSL_MAX_NBLK + ngtcp2_ksl_node objects. Because ngtcp2_ksl_node object is + allocated along with the additional variable length key + storage, the size of buffer is unknown until ngtcp2_ksl_init is + called. */ + uint8_t nodes[1]; + }; + }; + + ngtcp2_opl_entry oplent; }; }; +ngtcp2_objalloc_def(ksl_blk, ngtcp2_ksl_blk, oplent); + /* * ngtcp2_ksl_compar is a function type which returns nonzero if key * |lhs| should be placed before |rhs|. It returns 0 otherwise. @@ -122,6 +132,7 @@ struct ngtcp2_ksl_it { * ngtcp2_ksl is a deterministic paged skip list. */ struct ngtcp2_ksl { + ngtcp2_objalloc blkalloc; /* head points to the root block. */ ngtcp2_ksl_blk *head; /* front points to the first leaf block. */ @@ -135,21 +146,14 @@ struct ngtcp2_ksl { /* nodelen is the actual size of ngtcp2_ksl_node including key storage. */ size_t nodelen; - const ngtcp2_mem *mem; }; /* * ngtcp2_ksl_init initializes |ksl|. |compar| specifies compare * function. |keylen| is the length of key. - * - * It returns 0 if it succeeds, or one of the following negative error - * codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. */ -int ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen, - const ngtcp2_mem *mem); +void ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen, + const ngtcp2_mem *mem); /* * ngtcp2_ksl_free frees resources allocated for |ksl|. If |ksl| is @@ -191,6 +195,17 @@ int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, const ngtcp2_ksl_key *key); +/* + * ngtcp2_ksl_remove_hint removes the |key| from |ksl|. |hint| must + * point to the same node denoted by |key|. |hint| is used to remove + * a node efficiently in some cases. Other than that, it behaves + * exactly like ngtcp2_ksl_remove. |it| and |hint| can point to the + * same object. + */ +int ngtcp2_ksl_remove_hint(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, + const ngtcp2_ksl_it *hint, + const ngtcp2_ksl_key *key); + /* * ngtcp2_ksl_lower_bound returns the iterator which points to the * first node which has the key which is equal to |key| or the last @@ -266,7 +281,8 @@ void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl, * |it| points to. It is undefined to call this function when * ngtcp2_ksl_it_end(it) returns nonzero. */ -void *ngtcp2_ksl_it_get(const ngtcp2_ksl_it *it); +#define ngtcp2_ksl_it_get(IT) \ + ngtcp2_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->data /* * ngtcp2_ksl_it_next advances the iterator by one. It is undefined diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c index 0259404d3e2c06..ee37ff3517b2bc 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c @@ -34,6 +34,7 @@ #include "ngtcp2_str.h" #include "ngtcp2_vec.h" #include "ngtcp2_macro.h" +#include "ngtcp2_conv.h" void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid, ngtcp2_printf log_printf, ngtcp2_tstamp ts, @@ -69,7 +70,7 @@ void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid, * * # Frame event * - * () () + * () * * : * Flow direction. tx=transmission, rx=reception @@ -78,10 +79,7 @@ void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid, * Packet number. * * : - * Packet name. (e.g., Initial, Handshake, S01) - * - * : - * Packet type in hex string. + * Packet name. (e.g., Initial, Handshake, 1RTT) * * : * Frame name. (e.g., STREAM, ACK, PING) @@ -94,16 +92,16 @@ void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid, /* TODO Split second and remaining fraction with comma */ #define NGTCP2_LOG_HD "I%08" PRIu64 " 0x%s %s" -#define NGTCP2_LOG_PKT NGTCP2_LOG_HD " %s %" PRId64 " %s(0x%02x)" +#define NGTCP2_LOG_PKT NGTCP2_LOG_HD " %s %" PRId64 " %s" #define NGTCP2_LOG_TP NGTCP2_LOG_HD " remote transport_parameters" #define NGTCP2_LOG_FRM_HD_FIELDS(DIR) \ timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "frm", \ - (DIR), hd->pkt_num, strpkttype(hd), hd->type + (DIR), hd->pkt_num, strpkttype(hd) #define NGTCP2_LOG_PKT_HD_FIELDS(DIR) \ timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "pkt", \ - (DIR), hd->pkt_num, strpkttype(hd), hd->type + (DIR), hd->pkt_num, strpkttype(hd) #define NGTCP2_LOG_TP_HD_FIELDS \ timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "cry" @@ -140,6 +138,8 @@ static const char *strerrorcode(uint64_t error_code) { return "CRYPTO_BUFFER_EXCEEDED"; case NGTCP2_KEY_UPDATE_ERROR: return "KEY_UPDATE_ERROR"; + case NGTCP2_VERSION_NEGOTIATION_ERROR_DRAFT: + return "VERSION_NEGOTIATION_ERROR"; default: if (0x100u <= error_code && error_code <= 0x1ffu) { return "CRYPTO_ERROR"; @@ -155,8 +155,6 @@ static const char *strapperrorcode(uint64_t app_error_code) { static const char *strpkttype_long(uint8_t type) { switch (type) { - case NGTCP2_PKT_VERSION_NEGOTIATION: - return "VN"; case NGTCP2_PKT_INITIAL: return "Initial"; case NGTCP2_PKT_RETRY: @@ -174,7 +172,26 @@ static const char *strpkttype(const ngtcp2_pkt_hd *hd) { if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) { return strpkttype_long(hd->type); } - return "Short"; + + switch (hd->type) { + case NGTCP2_PKT_VERSION_NEGOTIATION: + return "VN"; + case NGTCP2_PKT_STATELESS_RESET: + return "SR"; + case NGTCP2_PKT_1RTT: + return "1RTT"; + default: + return "(unknown)"; + } +} + +static const char *strpkttype_type_flags(uint8_t type, uint8_t flags) { + ngtcp2_pkt_hd hd = {0}; + + hd.type = type; + hd.flags = flags; + + return strpkttype(&hd); } static const char *strevent(ngtcp2_log_event ev) { @@ -201,13 +218,13 @@ static uint64_t timestamp_cast(uint64_t ns) { return ns / NGTCP2_MILLISECONDS; } static void log_fr_stream(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, const ngtcp2_stream *fr, const char *dir) { - log->log_printf(log->user_data, - (NGTCP2_LOG_PKT " STREAM(0x%02x) id=0x%" PRIx64 - " fin=%d offset=%" PRIu64 " len=%zu uni=%d"), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type | fr->flags, - fr->stream_id, fr->fin, fr->offset, - ngtcp2_vec_len(fr->data, fr->datacnt), - (fr->stream_id & 0x2) != 0); + log->log_printf( + log->user_data, + (NGTCP2_LOG_PKT " STREAM(0x%02x) id=0x%" PRIx64 " fin=%d offset=%" PRIu64 + " len=%" PRIu64 " uni=%d"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type | fr->flags, fr->stream_id, + fr->fin, fr->offset, ngtcp2_vec_len(fr->data, fr->datacnt), + (fr->stream_id & 0x2) != 0); } static void log_fr_ack(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, @@ -396,17 +413,11 @@ static void log_fr_path_response(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, static void log_fr_crypto(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, const ngtcp2_crypto *fr, const char *dir) { - size_t datalen = 0; - size_t i; - - for (i = 0; i < fr->datacnt; ++i) { - datalen += fr->data[i].len; - } - log->log_printf( log->user_data, (NGTCP2_LOG_PKT " CRYPTO(0x%02x) offset=%" PRIu64 " len=%" PRIu64), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset, datalen); + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset, + ngtcp2_vec_len(fr->data, fr->datacnt)); } static void log_fr_new_token(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, @@ -446,7 +457,8 @@ static void log_fr_handshake_done(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, static void log_fr_datagram(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, const ngtcp2_datagram *fr, const char *dir) { - log->log_printf(log->user_data, (NGTCP2_LOG_PKT " DATAGRAM(0x%02x) len=%zu"), + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " DATAGRAM(0x%02x) len=%" PRIu64), NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, ngtcp2_vec_len(fr->data, fr->datacnt)); } @@ -570,6 +582,8 @@ void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset *sr) { memset(&shd, 0, sizeof(shd)); + shd.type = NGTCP2_PKT_STATELESS_RESET; + log->log_printf( log->user_data, (NGTCP2_LOG_PKT " token=0x%s randlen=%zu"), NGTCP2_LOG_PKT_HD_FIELDS("rx"), @@ -583,6 +597,7 @@ void ngtcp2_log_remote_tp(ngtcp2_log *log, uint8_t exttype, uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN * 2 + 1]; uint8_t addr[16 * 2 + 7 + 1]; uint8_t cid[NGTCP2_MAX_CIDLEN * 2 + 1]; + size_t i; if (!log->log_printf) { return; @@ -694,6 +709,26 @@ void ngtcp2_log_remote_tp(ngtcp2_log *log, uint8_t exttype, log->log_printf(log->user_data, (NGTCP2_LOG_TP " max_datagram_frame_size=%" PRIu64), NGTCP2_LOG_TP_HD_FIELDS, params->max_datagram_frame_size); + log->log_printf(log->user_data, (NGTCP2_LOG_TP " grease_quic_bit=%d"), + NGTCP2_LOG_TP_HD_FIELDS, params->grease_quic_bit); + + if (params->version_info_present) { + log->log_printf( + log->user_data, + (NGTCP2_LOG_TP " version_information.chosen_version=0x%08x"), + NGTCP2_LOG_TP_HD_FIELDS, params->version_info.chosen_version); + + assert(!(params->version_info.other_versionslen & 0x3)); + + for (i = 0; i < params->version_info.other_versionslen; + i += sizeof(uint32_t)) { + log->log_printf( + log->user_data, + (NGTCP2_LOG_TP " version_information.other_versions[%zu]=0x%08x"), + NGTCP2_LOG_TP_HD_FIELDS, i >> 2, + ngtcp2_get_uint32(¶ms->version_info.other_versions[i])); + } + } } void ngtcp2_log_pkt_lost(ngtcp2_log *log, int64_t pkt_num, uint8_t type, @@ -702,11 +737,9 @@ void ngtcp2_log_pkt_lost(ngtcp2_log *log, int64_t pkt_num, uint8_t type, return; } - ngtcp2_log_info( - log, NGTCP2_LOG_EVENT_RCV, - "pkn=%" PRId64 " lost type=%s(0x%02x) sent_ts=%" PRIu64, pkt_num, - (flags & NGTCP2_PKT_FLAG_LONG_FORM) ? strpkttype_long(type) : "Short", - type, sent_ts); + ngtcp2_log_info(log, NGTCP2_LOG_EVENT_RCV, + "pkn=%" PRId64 " lost type=%s sent_ts=%" PRIu64, pkt_num, + strpkttype_type_flags(type, flags), sent_ts); } static void log_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, @@ -718,15 +751,21 @@ static void log_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, return; } - ngtcp2_log_info( - log, NGTCP2_LOG_EVENT_PKT, - "%s pkn=%" PRId64 " dcid=0x%s scid=0x%s type=%s(0x%02x) len=%zu k=%d", - dir, hd->pkt_num, - (const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen), - (const char *)ngtcp2_encode_hex(scid, hd->scid.data, hd->scid.datalen), - (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) ? strpkttype_long(hd->type) - : "Short", - hd->type, hd->len, (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) != 0); + if (hd->type == NGTCP2_PKT_1RTT) { + ngtcp2_log_info( + log, NGTCP2_LOG_EVENT_PKT, "%s pkn=%" PRId64 " dcid=0x%s type=%s k=%d", + dir, hd->pkt_num, + (const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen), + strpkttype(hd), (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) != 0); + } else { + ngtcp2_log_info( + log, NGTCP2_LOG_EVENT_PKT, + "%s pkn=%" PRId64 " dcid=0x%s scid=0x%s version=0x%08x type=%s len=%zu", + dir, hd->pkt_num, + (const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen), + (const char *)ngtcp2_encode_hex(scid, hd->scid.data, hd->scid.datalen), + hd->version, strpkttype(hd), hd->len); + } } void ngtcp2_log_rx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) { @@ -762,6 +801,6 @@ void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, const char *fmt, void ngtcp2_log_tx_cancel(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) { ngtcp2_log_info(log, NGTCP2_LOG_EVENT_PKT, - "cancel tx pkn=%" PRId64 " type=%s(0x%02x)", hd->pkt_num, - strpkttype(hd), hd->type); + "cancel tx pkn=%" PRId64 " type=%s", hd->pkt_num, + strpkttype(hd)); } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.h index bd1ac240a9398a..029ef1b757ab09 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.h @@ -33,9 +33,7 @@ #include "ngtcp2_pkt.h" -typedef struct ngtcp2_log ngtcp2_log; - -struct ngtcp2_log { +typedef struct ngtcp2_log { /* log_printf is a sink to write log. NULL means no logging output. */ ngtcp2_printf log_printf; @@ -49,7 +47,44 @@ struct ngtcp2_log { void *user_data; /* scid is SCID encoded as NULL-terminated hex string. */ uint8_t scid[NGTCP2_MAX_CIDLEN * 2 + 1]; -}; +} ngtcp2_log; + +/** + * @enum + * + * :type:`ngtcp2_log_event` defines an event of ngtcp2 library + * internal logger. + */ +typedef enum ngtcp2_log_event { + /** + * :enum:`NGTCP2_LOG_EVENT_NONE` represents no event. + */ + NGTCP2_LOG_EVENT_NONE, + /** + * :enum:`NGTCP2_LOG_EVENT_CON` is a connection (catch-all) event + */ + NGTCP2_LOG_EVENT_CON, + /** + * :enum:`NGTCP2_LOG_EVENT_PKT` is a packet event. + */ + NGTCP2_LOG_EVENT_PKT, + /** + * :enum:`NGTCP2_LOG_EVENT_FRM` is a QUIC frame event. + */ + NGTCP2_LOG_EVENT_FRM, + /** + * :enum:`NGTCP2_LOG_EVENT_RCV` is a congestion and recovery event. + */ + NGTCP2_LOG_EVENT_RCV, + /** + * :enum:`NGTCP2_LOG_EVENT_CRY` is a crypto event. + */ + NGTCP2_LOG_EVENT_CRY, + /** + * :enum:`NGTCP2_LOG_EVENT_PTV` is a path validation event. + */ + NGTCP2_LOG_EVENT_PTV, +} ngtcp2_log_event; void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid, ngtcp2_printf log_printf, ngtcp2_tstamp ts, @@ -77,4 +112,12 @@ void ngtcp2_log_tx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd); void ngtcp2_log_tx_cancel(ngtcp2_log *log, const ngtcp2_pkt_hd *hd); +/** + * @function + * + * `ngtcp2_log_info` writes info level log. + */ +void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, const char *fmt, + ...); + #endif /* NGTCP2_LOG_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c index 5d24961dc98afe..12bc6e84bd4f0c 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c @@ -27,170 +27,166 @@ #include #include +#include #include "ngtcp2_conv.h" -#define INITIAL_TABLE_LENGTH 256 +#define NGTCP2_INITIAL_TABLE_LENBITS 4 -int ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem) { +void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem) { map->mem = mem; - map->tablelen = INITIAL_TABLE_LENGTH; - map->table = ngtcp2_mem_calloc(mem, map->tablelen, sizeof(ngtcp2_map_bucket)); - if (map->table == NULL) { - return NGTCP2_ERR_NOMEM; - } - + map->tablelen = 0; + map->tablelenbits = 0; + map->table = NULL; map->size = 0; - - return 0; } void ngtcp2_map_free(ngtcp2_map *map) { - size_t i; - ngtcp2_map_bucket *bkt; - if (!map) { return; } - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - if (bkt->ksl) { - ngtcp2_ksl_free(bkt->ksl); - ngtcp2_mem_free(map->mem, bkt->ksl); - } - } - ngtcp2_mem_free(map->mem, map->table); } -void ngtcp2_map_each_free(ngtcp2_map *map, - int (*func)(ngtcp2_map_entry *entry, void *ptr), +void ngtcp2_map_each_free(ngtcp2_map *map, int (*func)(void *data, void *ptr), void *ptr) { uint32_t i; ngtcp2_map_bucket *bkt; - ngtcp2_ksl_it it; for (i = 0; i < map->tablelen; ++i) { bkt = &map->table[i]; - if (bkt->ptr) { - func(bkt->ptr, ptr); - bkt->ptr = NULL; - assert(bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0); + if (bkt->data == NULL) { continue; } - if (bkt->ksl) { - for (it = ngtcp2_ksl_begin(bkt->ksl); !ngtcp2_ksl_it_end(&it); - ngtcp2_ksl_it_next(&it)) { - func(ngtcp2_ksl_it_get(&it), ptr); - } - - ngtcp2_ksl_free(bkt->ksl); - ngtcp2_mem_free(map->mem, bkt->ksl); - bkt->ksl = NULL; - } + func(bkt->data, ptr); } } -int ngtcp2_map_each(ngtcp2_map *map, - int (*func)(ngtcp2_map_entry *entry, void *ptr), +int ngtcp2_map_each(ngtcp2_map *map, int (*func)(void *data, void *ptr), void *ptr) { int rv; uint32_t i; ngtcp2_map_bucket *bkt; - ngtcp2_ksl_it it; + + if (map->size == 0) { + return 0; + } for (i = 0; i < map->tablelen; ++i) { bkt = &map->table[i]; - if (bkt->ptr) { - rv = func(bkt->ptr, ptr); - if (rv != 0) { - return rv; - } - assert(bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0); + if (bkt->data == NULL) { continue; } - if (bkt->ksl) { - for (it = ngtcp2_ksl_begin(bkt->ksl); !ngtcp2_ksl_it_end(&it); - ngtcp2_ksl_it_next(&it)) { - rv = func(ngtcp2_ksl_it_get(&it), ptr); - if (rv != 0) { - return rv; - } - } + rv = func(bkt->data, ptr); + if (rv != 0) { + return rv; } } + return 0; } -void ngtcp2_map_entry_init(ngtcp2_map_entry *entry, key_type key) { - entry->key = key; +static uint32_t hash(ngtcp2_map_key_type key) { + return (uint32_t)((key * 11400714819323198485llu) >> 32); } -/* FNV1a hash */ -static uint32_t hash(key_type key, uint32_t mod) { - uint8_t *p, *end; - uint32_t h = 0x811C9DC5u; +static size_t h2idx(uint32_t hash, uint32_t bits) { + return hash >> (32 - bits); +} - key = ngtcp2_htonl64(key); - p = (uint8_t *)&key; - end = p + sizeof(key_type); +static size_t distance(uint32_t tablelen, uint32_t tablelenbits, + ngtcp2_map_bucket *bkt, size_t idx) { + return (idx - h2idx(bkt->hash, tablelenbits)) & (tablelen - 1); +} - for (; p != end;) { - h ^= *p++; - h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24); - } +static void map_bucket_swap(ngtcp2_map_bucket *bkt, uint32_t *phash, + ngtcp2_map_key_type *pkey, void **pdata) { + uint32_t h = bkt->hash; + ngtcp2_map_key_type key = bkt->key; + void *data = bkt->data; - return h & (mod - 1); + bkt->hash = *phash; + bkt->key = *pkey; + bkt->data = *pdata; + + *phash = h; + *pkey = key; + *pdata = data; } -static int less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { - return *(key_type *)lhs < *(key_type *)rhs; +static void map_bucket_set_data(ngtcp2_map_bucket *bkt, uint32_t hash, + ngtcp2_map_key_type key, void *data) { + bkt->hash = hash; + bkt->key = key; + bkt->data = data; } -static int map_insert(ngtcp2_map *map, ngtcp2_map_bucket *table, - uint32_t tablelen, ngtcp2_map_entry *entry) { - uint32_t h = hash(entry->key, tablelen); - ngtcp2_map_bucket *bkt = &table[h]; - const ngtcp2_mem *mem = map->mem; - int rv; +void ngtcp2_map_print_distance(ngtcp2_map *map) { + uint32_t i; + size_t idx; + ngtcp2_map_bucket *bkt; - if (bkt->ptr == NULL && (bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0)) { - bkt->ptr = entry; - return 0; - } + for (i = 0; i < map->tablelen; ++i) { + bkt = &map->table[i]; - if (!bkt->ksl) { - bkt->ksl = ngtcp2_mem_malloc(mem, sizeof(*bkt->ksl)); - if (bkt->ksl == NULL) { - return NGTCP2_ERR_NOMEM; + if (bkt->data == NULL) { + fprintf(stderr, "@%u \n", i); + continue; } - ngtcp2_ksl_init(bkt->ksl, less, sizeof(key_type), mem); + + idx = h2idx(bkt->hash, map->tablelenbits); + fprintf(stderr, "@%u hash=%08x key=%" PRIu64 " base=%zu distance=%zu\n", i, + bkt->hash, bkt->key, idx, + distance(map->tablelen, map->tablelenbits, bkt, idx)); } +} - if (bkt->ptr) { - rv = ngtcp2_ksl_insert(bkt->ksl, NULL, &bkt->ptr->key, bkt->ptr); - if (rv != 0) { - return rv; +static int insert(ngtcp2_map_bucket *table, uint32_t tablelen, + uint32_t tablelenbits, uint32_t hash, ngtcp2_map_key_type key, + void *data) { + size_t idx = h2idx(hash, tablelenbits); + size_t d = 0, dd; + ngtcp2_map_bucket *bkt; + + for (;;) { + bkt = &table[idx]; + + if (bkt->data == NULL) { + map_bucket_set_data(bkt, hash, key, data); + return 0; } - bkt->ptr = NULL; - } + dd = distance(tablelen, tablelenbits, bkt, idx); + if (d > dd) { + map_bucket_swap(bkt, &hash, &key, &data); + d = dd; + } else if (bkt->key == key) { + /* TODO This check is just a waste after first swap or if this + function is called from map_resize. That said, there is no + difference with or without this conditional in performance + wise. */ + return NGTCP2_ERR_INVALID_ARGUMENT; + } - return ngtcp2_ksl_insert(bkt->ksl, NULL, &entry->key, entry); + ++d; + idx = (idx + 1) & (tablelen - 1); + } } -/* new_tablelen must be power of 2 */ -static int map_resize(ngtcp2_map *map, uint32_t new_tablelen) { +/* new_tablelen must be power of 2 and new_tablelen == (1 << + new_tablelenbits) must hold. */ +static int map_resize(ngtcp2_map *map, uint32_t new_tablelen, + uint32_t new_tablelenbits) { uint32_t i; ngtcp2_map_bucket *new_table; ngtcp2_map_bucket *bkt; - ngtcp2_ksl_it it; int rv; + (void)rv; new_table = ngtcp2_mem_calloc(map->mem, new_tablelen, sizeof(ngtcp2_map_bucket)); @@ -200,64 +196,46 @@ static int map_resize(ngtcp2_map *map, uint32_t new_tablelen) { for (i = 0; i < map->tablelen; ++i) { bkt = &map->table[i]; - - if (bkt->ptr) { - rv = map_insert(map, new_table, new_tablelen, bkt->ptr); - if (rv != 0) { - goto fail; - } - assert(bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0); + if (bkt->data == NULL) { continue; } + rv = insert(new_table, new_tablelen, new_tablelenbits, bkt->hash, bkt->key, + bkt->data); - if (bkt->ksl) { - for (it = ngtcp2_ksl_begin(bkt->ksl); !ngtcp2_ksl_it_end(&it); - ngtcp2_ksl_it_next(&it)) { - rv = map_insert(map, new_table, new_tablelen, ngtcp2_ksl_it_get(&it)); - if (rv != 0) { - goto fail; - } - } - } - } - - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - if (bkt->ksl) { - ngtcp2_ksl_free(bkt->ksl); - ngtcp2_mem_free(map->mem, bkt->ksl); - } + assert(0 == rv); } ngtcp2_mem_free(map->mem, map->table); map->tablelen = new_tablelen; + map->tablelenbits = new_tablelenbits; map->table = new_table; return 0; - -fail: - for (i = 0; i < new_tablelen; ++i) { - bkt = &new_table[i]; - if (bkt->ksl) { - ngtcp2_ksl_free(bkt->ksl); - ngtcp2_mem_free(map->mem, bkt->ksl); - } - } - - return rv; } -int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_entry *new_entry) { +int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_key_type key, void *data) { int rv; + assert(data); + /* Load factor is 0.75 */ if ((map->size + 1) * 4 > map->tablelen * 3) { - rv = map_resize(map, map->tablelen * 2); - if (rv != 0) { - return rv; + if (map->tablelen) { + rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1); + if (rv != 0) { + return rv; + } + } else { + rv = map_resize(map, 1 << NGTCP2_INITIAL_TABLE_LENBITS, + NGTCP2_INITIAL_TABLE_LENBITS); + if (rv != 0) { + return rv; + } } } - rv = map_insert(map, map->table, map->tablelen, new_entry); + + rv = insert(map->table, map->tablelen, map->tablelenbits, hash(key), key, + data); if (rv != 0) { return rv; } @@ -265,67 +243,93 @@ int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_entry *new_entry) { return 0; } -ngtcp2_map_entry *ngtcp2_map_find(ngtcp2_map *map, key_type key) { - ngtcp2_map_bucket *bkt = &map->table[hash(key, map->tablelen)]; - ngtcp2_ksl_it it; +void *ngtcp2_map_find(ngtcp2_map *map, ngtcp2_map_key_type key) { + uint32_t h; + size_t idx; + ngtcp2_map_bucket *bkt; + size_t d = 0; - if (bkt->ptr) { - if (bkt->ptr->key == key) { - return bkt->ptr; - } + if (map->size == 0) { return NULL; } - if (bkt->ksl) { - it = ngtcp2_ksl_lower_bound(bkt->ksl, &key); - if (ngtcp2_ksl_it_end(&it) || *(key_type *)ngtcp2_ksl_it_key(&it) != key) { + h = hash(key); + idx = h2idx(h, map->tablelenbits); + + for (;;) { + bkt = &map->table[idx]; + + if (bkt->data == NULL || + d > distance(map->tablelen, map->tablelenbits, bkt, idx)) { return NULL; } - return ngtcp2_ksl_it_get(&it); - } - return NULL; + if (bkt->key == key) { + return bkt->data; + } + + ++d; + idx = (idx + 1) & (map->tablelen - 1); + } } -int ngtcp2_map_remove(ngtcp2_map *map, key_type key) { - ngtcp2_map_bucket *bkt = &map->table[hash(key, map->tablelen)]; - int rv; +int ngtcp2_map_remove(ngtcp2_map *map, ngtcp2_map_key_type key) { + uint32_t h; + size_t idx, didx; + ngtcp2_map_bucket *bkt; + size_t d = 0; - if (bkt->ptr) { - if (bkt->ptr->key == key) { - bkt->ptr = NULL; - --map->size; - return 0; - } + if (map->size == 0) { return NGTCP2_ERR_INVALID_ARGUMENT; } - if (bkt->ksl) { - rv = ngtcp2_ksl_remove(bkt->ksl, NULL, &key); - if (rv != 0) { - return rv; + h = hash(key); + idx = h2idx(h, map->tablelenbits); + + for (;;) { + bkt = &map->table[idx]; + + if (bkt->data == NULL || + d > distance(map->tablelen, map->tablelenbits, bkt, idx)) { + return NGTCP2_ERR_INVALID_ARGUMENT; } - --map->size; - return 0; - } - return NGTCP2_ERR_INVALID_ARGUMENT; -} + if (bkt->key == key) { + map_bucket_set_data(bkt, 0, 0, NULL); -void ngtcp2_map_clear(ngtcp2_map *map) { - uint32_t i; - ngtcp2_map_bucket *bkt; + didx = idx; + idx = (idx + 1) & (map->tablelen - 1); - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - bkt->ptr = NULL; - if (bkt->ksl) { - ngtcp2_ksl_free(bkt->ksl); - ngtcp2_mem_free(map->mem, bkt->ksl); - bkt->ksl = NULL; + for (;;) { + bkt = &map->table[idx]; + if (bkt->data == NULL || + distance(map->tablelen, map->tablelenbits, bkt, idx) == 0) { + break; + } + + map->table[didx] = *bkt; + map_bucket_set_data(bkt, 0, 0, NULL); + didx = idx; + + idx = (idx + 1) & (map->tablelen - 1); + } + + --map->size; + + return 0; } + + ++d; + idx = (idx + 1) & (map->tablelen - 1); + } +} + +void ngtcp2_map_clear(ngtcp2_map *map) { + if (map->tablelen == 0) { + return; } + memset(map->table, 0, sizeof(*map->table) * map->tablelen); map->size = 0; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h index bbb2e705d69e6c..a64344a9a301a3 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h @@ -33,21 +33,15 @@ #include #include "ngtcp2_mem.h" -#include "ngtcp2_ksl.h" /* Implementation of unordered map */ -typedef uint64_t key_type; - -typedef struct ngtcp2_map_entry ngtcp2_map_entry; - -struct ngtcp2_map_entry { - key_type key; -}; +typedef uint64_t ngtcp2_map_key_type; typedef struct ngtcp2_map_bucket { - ngtcp2_map_entry *ptr; - ngtcp2_ksl *ksl; + uint32_t hash; + ngtcp2_map_key_type key; + void *data; } ngtcp2_map_bucket; typedef struct ngtcp2_map { @@ -55,18 +49,13 @@ typedef struct ngtcp2_map { const ngtcp2_mem *mem; size_t size; uint32_t tablelen; + uint32_t tablelenbits; } ngtcp2_map; /* * Initializes the map |map|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory */ -int ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem); +void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem); /* * Deallocates any resources allocated for |map|. The stored entries @@ -78,21 +67,14 @@ void ngtcp2_map_free(ngtcp2_map *map); /* * Deallocates each entries using |func| function and any resources * allocated for |map|. The |func| function is responsible for freeing - * given the |entry| object. The |ptr| will be passed to the |func| as + * given the |data| object. The |ptr| will be passed to the |func| as * send argument. The return value of the |func| will be ignored. */ -void ngtcp2_map_each_free(ngtcp2_map *map, - int (*func)(ngtcp2_map_entry *entry, void *ptr), +void ngtcp2_map_each_free(ngtcp2_map *map, int (*func)(void *data, void *ptr), void *ptr); /* - * Initializes the |entry| with the |key|. All entries to be inserted - * to the map must be initialized with this function. - */ -void ngtcp2_map_entry_init(ngtcp2_map_entry *entry, key_type key); - -/* - * Inserts the new |entry| with the key |entry->key| to the map |map|. + * Inserts the new |data| with the |key| to the map |map|. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -102,25 +84,25 @@ void ngtcp2_map_entry_init(ngtcp2_map_entry *entry, key_type key); * NGTCP2_ERR_NOMEM * Out of memory */ -int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_entry *entry); +int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_key_type key, void *data); /* - * Returns the entry associated by the key |key|. If there is no such - * entry, this function returns NULL. + * Returns the data associated by the key |key|. If there is no such + * data, this function returns NULL. */ -ngtcp2_map_entry *ngtcp2_map_find(ngtcp2_map *map, key_type key); +void *ngtcp2_map_find(ngtcp2_map *map, ngtcp2_map_key_type key); /* - * Removes the entry associated by the key |key| from the |map|. The - * removed entry is not freed by this function. + * Removes the data associated by the key |key| from the |map|. The + * removed data is not freed by this function. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGTCP2_ERR_INVALID_ARGUMENT - * The entry associated by |key| does not exist. + * The data associated by |key| does not exist. */ -int ngtcp2_map_remove(ngtcp2_map *map, key_type key); +int ngtcp2_map_remove(ngtcp2_map *map, ngtcp2_map_key_type key); /* * Removes all entries from |map|. @@ -133,20 +115,22 @@ void ngtcp2_map_clear(ngtcp2_map *map); size_t ngtcp2_map_size(ngtcp2_map *map); /* - * Applies the function |func| to each entry in the |map| with the + * Applies the function |func| to each data in the |map| with the * optional user supplied pointer |ptr|. * * If the |func| returns 0, this function calls the |func| with the - * next entry. If the |func| returns nonzero, it will not call the + * next data. If the |func| returns nonzero, it will not call the * |func| for further entries and return the return value of the * |func| immediately. Thus, this function returns 0 if all the * invocations of the |func| return 0, or nonzero value which the last * invocation of |func| returns. * - * Don't use this function to free each entry. Use + * Don't use this function to free each data. Use * ngtcp2_map_each_free() instead. */ -int ngtcp2_map_each(ngtcp2_map *map, - int (*func)(ngtcp2_map_entry *entry, void *ptr), void *ptr); +int ngtcp2_map_each(ngtcp2_map *map, int (*func)(void *data, void *ptr), + void *ptr); + +void ngtcp2_map_print_distance(ngtcp2_map *map); #endif /* NGTCP2_MAP_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c index 2c036ad1634b05..bcce0b5cdfcf02 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c @@ -25,26 +25,28 @@ */ #include "ngtcp2_mem.h" -static void *default_malloc(size_t size, void *mem_user_data) { - (void)mem_user_data; +#include + +static void *default_malloc(size_t size, void *user_data) { + (void)user_data; return malloc(size); } -static void default_free(void *ptr, void *mem_user_data) { - (void)mem_user_data; +static void default_free(void *ptr, void *user_data) { + (void)user_data; free(ptr); } -static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) { - (void)mem_user_data; +static void *default_calloc(size_t nmemb, size_t size, void *user_data) { + (void)user_data; return calloc(nmemb, size); } -static void *default_realloc(void *ptr, size_t size, void *mem_user_data) { - (void)mem_user_data; +static void *default_realloc(void *ptr, size_t size, void *user_data) { + (void)user_data; return realloc(ptr, size); } @@ -54,22 +56,58 @@ static const ngtcp2_mem mem_default = {NULL, default_malloc, default_free, const ngtcp2_mem *ngtcp2_mem_default(void) { return &mem_default; } +#ifndef MEMDEBUG void *ngtcp2_mem_malloc(const ngtcp2_mem *mem, size_t size) { - return mem->malloc(size, mem->mem_user_data); + return mem->malloc(size, mem->user_data); } void ngtcp2_mem_free(const ngtcp2_mem *mem, void *ptr) { - mem->free(ptr, mem->mem_user_data); -} - -void ngtcp2_mem_free2(ngtcp2_free free_func, void *ptr, void *mem_user_data) { - free_func(ptr, mem_user_data); + mem->free(ptr, mem->user_data); } void *ngtcp2_mem_calloc(const ngtcp2_mem *mem, size_t nmemb, size_t size) { - return mem->calloc(nmemb, size, mem->mem_user_data); + return mem->calloc(nmemb, size, mem->user_data); } void *ngtcp2_mem_realloc(const ngtcp2_mem *mem, void *ptr, size_t size) { - return mem->realloc(ptr, size, mem->mem_user_data); + return mem->realloc(ptr, size, mem->user_data); +} +#else /* MEMDEBUG */ +void *ngtcp2_mem_malloc_debug(const ngtcp2_mem *mem, size_t size, + const char *func, const char *file, size_t line) { + void *nptr = mem->malloc(size, mem->user_data); + + fprintf(stderr, "malloc %p size=%zu in %s at %s:%zu\n", nptr, size, func, + file, line); + + return nptr; +} + +void ngtcp2_mem_free_debug(const ngtcp2_mem *mem, void *ptr, const char *func, + const char *file, size_t line) { + fprintf(stderr, "free ptr=%p in %s at %s:%zu\n", ptr, func, file, line); + + mem->free(ptr, mem->user_data); +} + +void *ngtcp2_mem_calloc_debug(const ngtcp2_mem *mem, size_t nmemb, size_t size, + const char *func, const char *file, size_t line) { + void *nptr = mem->calloc(nmemb, size, mem->user_data); + + fprintf(stderr, "calloc %p nmemb=%zu size=%zu in %s at %s:%zu\n", nptr, nmemb, + size, func, file, line); + + return nptr; +} + +void *ngtcp2_mem_realloc_debug(const ngtcp2_mem *mem, void *ptr, size_t size, + const char *func, const char *file, + size_t line) { + void *nptr = mem->realloc(ptr, size, mem->user_data); + + fprintf(stderr, "realloc %p ptr=%p size=%zu in %s at %s:%zu\n", nptr, ptr, + size, func, file, line); + + return nptr; } +#endif /* MEMDEBUG */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.h index cdecf8763a5f36..c99b6c59726891 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.h @@ -34,10 +34,39 @@ /* Convenient wrapper functions to call allocator function in |mem|. */ +#ifndef MEMDEBUG void *ngtcp2_mem_malloc(const ngtcp2_mem *mem, size_t size); + void ngtcp2_mem_free(const ngtcp2_mem *mem, void *ptr); -void ngtcp2_mem_free2(ngtcp2_free free_func, void *ptr, void *mem_user_data); + void *ngtcp2_mem_calloc(const ngtcp2_mem *mem, size_t nmemb, size_t size); + void *ngtcp2_mem_realloc(const ngtcp2_mem *mem, void *ptr, size_t size); +#else /* MEMDEBUG */ +void *ngtcp2_mem_malloc_debug(const ngtcp2_mem *mem, size_t size, + const char *func, const char *file, size_t line); + +# define ngtcp2_mem_malloc(MEM, SIZE) \ + ngtcp2_mem_malloc_debug((MEM), (SIZE), __func__, __FILE__, __LINE__) + +void ngtcp2_mem_free_debug(const ngtcp2_mem *mem, void *ptr, const char *func, + const char *file, size_t line); + +# define ngtcp2_mem_free(MEM, PTR) \ + ngtcp2_mem_free_debug((MEM), (PTR), __func__, __FILE__, __LINE__) + +void *ngtcp2_mem_calloc_debug(const ngtcp2_mem *mem, size_t nmemb, size_t size, + const char *func, const char *file, size_t line); + +# define ngtcp2_mem_calloc(MEM, NMEMB, SIZE) \ + ngtcp2_mem_calloc_debug((MEM), (NMEMB), (SIZE), __func__, __FILE__, \ + __LINE__) + +void *ngtcp2_mem_realloc_debug(const ngtcp2_mem *mem, void *ptr, size_t size, + const char *func, const char *file, size_t line); + +# define ngtcp2_mem_realloc(MEM, PTR, SIZE) \ + ngtcp2_mem_realloc_debug((MEM), (PTR), (SIZE), __func__, __FILE__, __LINE__) +#endif /* MEMDEBUG */ #endif /* NGTCP2_MEM_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_net.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_net.h new file mode 100644 index 00000000000000..b1f28096174605 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_net.h @@ -0,0 +1,136 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_NET_H +#define NGTCP2_NET_H + +/* This header file is explicitly allowed to be shared with + ngtcp2_crypto library. */ + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_ARPA_INET_H +# include +#endif /* HAVE_ARPA_INET_H */ + +#ifdef HAVE_NETINET_IN_H +# include +#endif /* HAVE_NETINET_IN_H */ + +#ifdef HAVE_BYTESWAP_H +# include +#endif /* HAVE_BYTESWAP_H */ + +#ifdef HAVE_ENDIAN_H +# include +#endif /* HAVE_ENDIAN_H */ + +#ifdef HAVE_SYS_ENDIAN_H +# include +#endif /* HAVE_SYS_ENDIAN_H */ + +#include + +#if defined(HAVE_BSWAP_64) || \ + (defined(HAVE_DECL_BSWAP_64) && HAVE_DECL_BSWAP_64 > 0) +# define ngtcp2_bswap64 bswap_64 +#else /* !HAVE_BSWAP_64 */ +# define ngtcp2_bswap64(N) \ + ((uint64_t)(ngtcp2_ntohl((uint32_t)(N))) << 32 | \ + ngtcp2_ntohl((uint32_t)((N) >> 32))) +#endif /* !HAVE_BSWAP_64 */ + +#if defined(HAVE_BE64TOH) || \ + (defined(HAVE_DECL_BE64TOH) && HAVE_DECL_BE64TOH > 0) +# define ngtcp2_ntohl64(N) be64toh(N) +# define ngtcp2_htonl64(N) htobe64(N) +#else /* !HAVE_BE64TOH */ +# if defined(WORDS_BIGENDIAN) +# define ngtcp2_ntohl64(N) (N) +# define ngtcp2_htonl64(N) (N) +# else /* !WORDS_BIGENDIAN */ +# define ngtcp2_ntohl64(N) ngtcp2_bswap64(N) +# define ngtcp2_htonl64(N) ngtcp2_bswap64(N) +# endif /* !WORDS_BIGENDIAN */ +#endif /* !HAVE_BE64TOH */ + +#if defined(WIN32) +/* Windows requires ws2_32 library for ntonl family functions. We + define inline functions for those function so that we don't have + dependeny on that lib. */ + +# ifdef _MSC_VER +# define STIN static __inline +# else +# define STIN static inline +# endif + +STIN uint32_t ngtcp2_htonl(uint32_t hostlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = (unsigned char)(hostlong >> 24); + *p++ = (hostlong >> 16) & 0xffu; + *p++ = (hostlong >> 8) & 0xffu; + *p = hostlong & 0xffu; + return res; +} + +STIN uint16_t ngtcp2_htons(uint16_t hostshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = (unsigned char)(hostshort >> 8); + *p = hostshort & 0xffu; + return res; +} + +STIN uint32_t ngtcp2_ntohl(uint32_t netlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&netlong; + res = *p++ << 24; + res += *p++ << 16; + res += *p++ << 8; + res += *p; + return res; +} + +STIN uint16_t ngtcp2_ntohs(uint16_t netshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&netshort; + res = *p++ << 8; + res += *p; + return res; +} + +#else /* !WIN32 */ + +# define ngtcp2_htonl htonl +# define ngtcp2_htons htons +# define ngtcp2_ntohl ntohl +# define ngtcp2_ntohs ntohs + +#endif /* !WIN32 */ + +#endif /* NGTCP2_NET_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_objalloc.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_objalloc.c new file mode 100644 index 00000000000000..8b06cdd5de3d1d --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_objalloc.c @@ -0,0 +1,40 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_objalloc.h" + +void ngtcp2_objalloc_init(ngtcp2_objalloc *objalloc, size_t blklen, + const ngtcp2_mem *mem) { + ngtcp2_balloc_init(&objalloc->balloc, blklen, mem); + ngtcp2_opl_init(&objalloc->opl); +} + +void ngtcp2_objalloc_free(ngtcp2_objalloc *objalloc) { + ngtcp2_balloc_free(&objalloc->balloc); +} + +void ngtcp2_objalloc_clear(ngtcp2_objalloc *objalloc) { + ngtcp2_opl_clear(&objalloc->opl); + ngtcp2_balloc_clear(&objalloc->balloc); +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_objalloc.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_objalloc.h new file mode 100644 index 00000000000000..f1bbd3a5c9405c --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_objalloc.h @@ -0,0 +1,140 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_OBJALLOC_H +#define NGTCP2_OBJALLOC_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_balloc.h" +#include "ngtcp2_opl.h" +#include "ngtcp2_macro.h" +#include "ngtcp2_mem.h" + +/* + * ngtcp2_objalloc combines ngtcp2_balloc and ngtcp2_opl, and provides + * an object pool with the custom allocator to reduce the allocation + * and deallocation overheads for small objects. + */ +typedef struct ngtcp2_objalloc { + ngtcp2_balloc balloc; + ngtcp2_opl opl; +} ngtcp2_objalloc; + +/* + * ngtcp2_objalloc_init initializes |objalloc|. |blklen| is directly + * passed to ngtcp2_balloc_init. + */ +void ngtcp2_objalloc_init(ngtcp2_objalloc *objalloc, size_t blklen, + const ngtcp2_mem *mem); + +/* + * ngtcp2_objalloc_free releases all allocated resources. + */ +void ngtcp2_objalloc_free(ngtcp2_objalloc *objalloc); + +/* + * ngtcp2_objalloc_clear releases all allocated resources and + * initializes its state. + */ +void ngtcp2_objalloc_clear(ngtcp2_objalloc *objalloc); + +#ifndef NOMEMPOOL +# define ngtcp2_objalloc_def(NAME, TYPE, OPLENTFIELD) \ + inline static void ngtcp2_objalloc_##NAME##_init( \ + ngtcp2_objalloc *objalloc, size_t nmemb, const ngtcp2_mem *mem) { \ + ngtcp2_objalloc_init( \ + objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \ + } \ + \ + inline static TYPE *ngtcp2_objalloc_##NAME##_get( \ + ngtcp2_objalloc *objalloc) { \ + ngtcp2_opl_entry *oplent = ngtcp2_opl_pop(&objalloc->opl); \ + TYPE *obj; \ + int rv; \ + \ + if (!oplent) { \ + rv = \ + ngtcp2_balloc_get(&objalloc->balloc, (void **)&obj, sizeof(TYPE)); \ + if (rv != 0) { \ + return NULL; \ + } \ + \ + return obj; \ + } \ + \ + return ngtcp2_struct_of(oplent, TYPE, OPLENTFIELD); \ + } \ + \ + inline static TYPE *ngtcp2_objalloc_##NAME##_len_get( \ + ngtcp2_objalloc *objalloc, size_t len) { \ + ngtcp2_opl_entry *oplent = ngtcp2_opl_pop(&objalloc->opl); \ + TYPE *obj; \ + int rv; \ + \ + if (!oplent) { \ + rv = ngtcp2_balloc_get(&objalloc->balloc, (void **)&obj, len); \ + if (rv != 0) { \ + return NULL; \ + } \ + \ + return obj; \ + } \ + \ + return ngtcp2_struct_of(oplent, TYPE, OPLENTFIELD); \ + } \ + \ + inline static void ngtcp2_objalloc_##NAME##_release( \ + ngtcp2_objalloc *objalloc, TYPE *obj) { \ + ngtcp2_opl_push(&objalloc->opl, &obj->OPLENTFIELD); \ + } +#else /* NOMEMPOOL */ +# define ngtcp2_objalloc_def(NAME, TYPE, OPLENTFIELD) \ + inline static void ngtcp2_objalloc_##NAME##_init( \ + ngtcp2_objalloc *objalloc, size_t nmemb, const ngtcp2_mem *mem) { \ + ngtcp2_objalloc_init( \ + objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \ + } \ + \ + inline static TYPE *ngtcp2_objalloc_##NAME##_get( \ + ngtcp2_objalloc *objalloc) { \ + return ngtcp2_mem_malloc(objalloc->balloc.mem, sizeof(TYPE)); \ + } \ + \ + inline static TYPE *ngtcp2_objalloc_##NAME##_len_get( \ + ngtcp2_objalloc *objalloc, size_t len) { \ + return ngtcp2_mem_malloc(objalloc->balloc.mem, len); \ + } \ + \ + inline static void ngtcp2_objalloc_##NAME##_release( \ + ngtcp2_objalloc *objalloc, TYPE *obj) { \ + ngtcp2_mem_free(objalloc->balloc.mem, obj); \ + } +#endif /* NOMEMPOOL */ + +#endif /* NGTCP2_OBJALLOC_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_opl.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_opl.c new file mode 100644 index 00000000000000..a29361c6349f9d --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_opl.c @@ -0,0 +1,46 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_opl.h" + +void ngtcp2_opl_init(ngtcp2_opl *opl) { opl->head = NULL; } + +void ngtcp2_opl_push(ngtcp2_opl *opl, ngtcp2_opl_entry *ent) { + ent->next = opl->head; + opl->head = ent; +} + +ngtcp2_opl_entry *ngtcp2_opl_pop(ngtcp2_opl *opl) { + ngtcp2_opl_entry *ent = opl->head; + + if (!ent) { + return NULL; + } + + opl->head = ent->next; + + return ent; +} + +void ngtcp2_opl_clear(ngtcp2_opl *opl) { opl->head = NULL; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_opl.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_opl.h new file mode 100644 index 00000000000000..714aa366304f0d --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_opl.h @@ -0,0 +1,65 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_OPL_H +#define NGTCP2_OPL_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +typedef struct ngtcp2_opl_entry ngtcp2_opl_entry; + +struct ngtcp2_opl_entry { + ngtcp2_opl_entry *next; +}; + +/* + * ngtcp2_opl is an object memory pool. + */ +typedef struct ngtcp2_opl { + ngtcp2_opl_entry *head; +} ngtcp2_opl; + +/* + * ngtcp2_opl_init initializes |opl|. + */ +void ngtcp2_opl_init(ngtcp2_opl *opl); + +/* + * ngtcp2_opl_push inserts |ent| to |opl| head. + */ +void ngtcp2_opl_push(ngtcp2_opl *opl, ngtcp2_opl_entry *ent); + +/* + * ngtcp2_opl_pop removes the first ngtcp2_opl_entry from |opl| and + * returns it. If |opl| does not have any entry, it returns NULL. + */ +ngtcp2_opl_entry *ngtcp2_opl_pop(ngtcp2_opl *opl); + +void ngtcp2_opl_clear(ngtcp2_opl *opl); + +#endif /* NGTCP2_OPL_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.c index 3f35f28ef6a394..83238730033537 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.c @@ -37,6 +37,7 @@ void ngtcp2_path_init(ngtcp2_path *path, const ngtcp2_addr *local, void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src) { ngtcp2_addr_copy(&dest->local, &src->local); ngtcp2_addr_copy(&dest->remote, &src->remote); + dest->user_data = src->user_data; } int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b) { @@ -45,30 +46,32 @@ int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b) { } void ngtcp2_path_storage_init(ngtcp2_path_storage *ps, - const struct sockaddr *local_addr, - size_t local_addrlen, void *local_user_data, - const struct sockaddr *remote_addr, - size_t remote_addrlen, void *remote_user_data) { - ngtcp2_addr_init(&ps->path.local, (const struct sockaddr *)&ps->local_addrbuf, - 0, local_user_data); + const ngtcp2_sockaddr *local_addr, + ngtcp2_socklen local_addrlen, + const ngtcp2_sockaddr *remote_addr, + ngtcp2_socklen remote_addrlen, void *user_data) { + ngtcp2_addr_init(&ps->path.local, (const ngtcp2_sockaddr *)&ps->local_addrbuf, + 0); ngtcp2_addr_init(&ps->path.remote, - (const struct sockaddr *)&ps->remote_addrbuf, 0, - remote_user_data); + (const ngtcp2_sockaddr *)&ps->remote_addrbuf, 0); ngtcp2_addr_copy_byte(&ps->path.local, local_addr, local_addrlen); ngtcp2_addr_copy_byte(&ps->path.remote, remote_addr, remote_addrlen); + + ps->path.user_data = user_data; } void ngtcp2_path_storage_init2(ngtcp2_path_storage *ps, const ngtcp2_path *path) { ngtcp2_path_storage_init(ps, path->local.addr, path->local.addrlen, - path->local.user_data, path->remote.addr, - path->remote.addrlen, path->remote.user_data); + path->remote.addr, path->remote.addrlen, + path->user_data); } void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps) { - ngtcp2_addr_init(&ps->path.local, (const struct sockaddr *)&ps->local_addrbuf, - 0, NULL); + ngtcp2_addr_init(&ps->path.local, (const ngtcp2_sockaddr *)&ps->local_addrbuf, + 0); ngtcp2_addr_init(&ps->path.remote, - (const struct sockaddr *)&ps->remote_addrbuf, 0, NULL); + (const ngtcp2_sockaddr *)&ps->remote_addrbuf, 0); + ps->path.user_data = NULL; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c index ddfbd24bbc9c77..62fef7d6005ed6 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c @@ -26,7 +26,6 @@ #include #include -#include #include "ngtcp2_conv.h" #include "ngtcp2_str.h" @@ -61,13 +60,12 @@ void ngtcp2_pkt_chain_del(ngtcp2_pkt_chain *pc, const ngtcp2_mem *mem) { ngtcp2_mem_free(mem, pc); } -int ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid, - size_t *pdcidlen, const uint8_t **pscid, - size_t *pscidlen, const uint8_t *data, +int ngtcp2_pkt_decode_version_cid(ngtcp2_version_cid *dest, const uint8_t *data, size_t datalen, size_t short_dcidlen) { size_t len; uint32_t version; size_t dcidlen, scidlen; + int supported_version; assert(datalen); @@ -95,23 +93,31 @@ int ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid, version = ngtcp2_get_uint32(&data[1]); - if ((version == 0 || version == NGTCP2_PROTO_VER_V1 || - (NGTCP2_PROTO_VER_DRAFT_MIN <= version && - version <= NGTCP2_PROTO_VER_DRAFT_MAX)) && + supported_version = ngtcp2_is_supported_version(version); + + if (supported_version && (dcidlen > NGTCP2_MAX_CIDLEN || scidlen > NGTCP2_MAX_CIDLEN)) { return NGTCP2_ERR_INVALID_ARGUMENT; } - *pversion = version; - *pdcid = &data[6]; - *pdcidlen = dcidlen; - *pscid = &data[6 + dcidlen + 1]; - *pscidlen = scidlen; + if (version && !supported_version && + datalen < NGTCP2_MAX_UDP_PAYLOAD_SIZE) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } - if (version && version != NGTCP2_PROTO_VER_V1 && - (version < NGTCP2_PROTO_VER_DRAFT_MIN || - NGTCP2_PROTO_VER_DRAFT_MAX < version)) { - return 1; + dest->version = version; + dest->dcid = &data[6]; + dest->dcidlen = dcidlen; + dest->scid = &data[6 + dcidlen + 1]; + dest->scidlen = scidlen; + + if (!version) { + /* VN */ + return 0; + } + + if (!supported_version) { + return NGTCP2_ERR_VERSION_NEGOTIATION; } return 0; } @@ -123,11 +129,11 @@ int ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid, return NGTCP2_ERR_INVALID_ARGUMENT; } - *pversion = 0; - *pdcid = &data[1]; - *pdcidlen = short_dcidlen; - *pscid = NULL; - *pscidlen = 0; + dest->version = 0; + dest->dcid = &data[1]; + dest->dcidlen = short_dcidlen; + dest->scid = NULL; + dest->scidlen = 0; return 0; } @@ -170,6 +176,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, const uint8_t *token = NULL; size_t tokenlen = 0; uint64_t vi; + uint8_t flags = NGTCP2_PKT_FLAG_LONG_FORM; if (pktlen < 5) { return NGTCP2_ERR_INVALID_ARGUMENT; @@ -183,16 +190,20 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, if (version == 0) { type = NGTCP2_PKT_VERSION_NEGOTIATION; + /* Version Negotiation is not a long header packet. */ + flags = NGTCP2_PKT_FLAG_NONE; /* This must be Version Negotiation packet which lacks packet number and payload length fields. */ len = 5 + 2; } else { if (!(pkt[0] & NGTCP2_FIXED_BIT_MASK)) { - return NGTCP2_ERR_INVALID_ARGUMENT; + flags |= NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR; } - type = ngtcp2_pkt_get_type_long(pkt[0]); + type = ngtcp2_pkt_get_type_long(version, pkt[0]); switch (type) { + case 0: + return NGTCP2_ERR_INVALID_ARGUMENT; case NGTCP2_PKT_INITIAL: len = 1 /* Token Length */ + NGTCP2_MIN_LONG_HEADERLEN - 1; /* Cut packet number field */ @@ -267,10 +278,15 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, } switch (type) { - case NGTCP2_PKT_VERSION_NEGOTIATION: case NGTCP2_PKT_RETRY: break; default: + if (!(flags & NGTCP2_PKT_FLAG_LONG_FORM)) { + assert(type == NGTCP2_PKT_VERSION_NEGOTIATION); + /* Version Negotiation is not a long header packet. */ + break; + } + /* Length */ n = ngtcp2_get_varint_len(p); len += n - 1; @@ -280,7 +296,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, } } - dest->flags = NGTCP2_PKT_FLAG_LONG_FORM; + dest->flags = flags; dest->type = type; dest->version = version; dest->pkt_num = 0; @@ -297,11 +313,17 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, p += ntokenlen + tokenlen; switch (type) { - case NGTCP2_PKT_VERSION_NEGOTIATION: case NGTCP2_PKT_RETRY: dest->len = 0; break; default: + if (!(flags & NGTCP2_PKT_FLAG_LONG_FORM)) { + assert(type == NGTCP2_PKT_VERSION_NEGOTIATION); + /* Version Negotiation is not a long header packet. */ + dest->len = 0; + break; + } + vi = ngtcp2_get_varint(&n, p); if (vi > SIZE_MAX) { return NGTCP2_ERR_INVALID_ARGUMENT; @@ -319,6 +341,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt, size_t pktlen, size_t dcidlen) { size_t len = 1 + dcidlen; const uint8_t *p = pkt; + uint8_t flags = NGTCP2_PKT_FLAG_NONE; assert(dcidlen <= NGTCP2_MAX_CIDLEN); @@ -326,14 +349,17 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt, return NGTCP2_ERR_INVALID_ARGUMENT; } - if ((pkt[0] & NGTCP2_HEADER_FORM_BIT) || - (pkt[0] & NGTCP2_FIXED_BIT_MASK) == 0) { + if (pkt[0] & NGTCP2_HEADER_FORM_BIT) { return NGTCP2_ERR_INVALID_ARGUMENT; } + if (!(pkt[0] & NGTCP2_FIXED_BIT_MASK)) { + flags |= NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR; + } + p = &pkt[1]; - dest->type = NGTCP2_PKT_SHORT; + dest->type = NGTCP2_PKT_1RTT; ngtcp2_cid_init(&dest->dcid, p, dcidlen); p += dcidlen; @@ -342,11 +368,13 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt, garbage. */ ngtcp2_cid_zero(&dest->scid); - dest->flags = NGTCP2_PKT_FLAG_NONE; + dest->flags = flags; dest->version = 0; dest->len = 0; dest->pkt_num = 0; dest->pkt_numlen = 0; + dest->token.base = NULL; + dest->token.len = 0; assert((size_t)(p - pkt) == len); @@ -361,7 +389,7 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen, len and 1 byte for packet number. */ if (hd->type != NGTCP2_PKT_RETRY) { - len += 2 /* Length */ + hd->pkt_numlen; + len += NGTCP2_PKT_LENGTHLEN /* Length */ + hd->pkt_numlen; } if (hd->type == NGTCP2_PKT_INITIAL) { @@ -374,8 +402,15 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen, p = out; - *p++ = (uint8_t)(NGTCP2_HEADER_FORM_BIT | NGTCP2_FIXED_BIT_MASK | - (hd->type << 4) | (uint8_t)(hd->pkt_numlen - 1)); + *p = (uint8_t)(NGTCP2_HEADER_FORM_BIT | + (ngtcp2_pkt_versioned_type(hd->version, hd->type) << 4) | + (uint8_t)(hd->pkt_numlen - 1)); + if (!(hd->flags & NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR)) { + *p |= NGTCP2_FIXED_BIT_MASK; + } + + ++p; + p = ngtcp2_put_uint32be(p, hd->version); *p++ = (uint8_t)hd->dcid.datalen; if (hd->dcid.datalen) { @@ -394,7 +429,7 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen, } if (hd->type != NGTCP2_PKT_RETRY) { - p = ngtcp2_put_varint14(p, (uint16_t)hd->len); + p = ngtcp2_put_varint30(p, (uint32_t)hd->len); p = ngtcp2_put_pkt_num(p, hd->pkt_num, hd->pkt_numlen); } @@ -414,7 +449,10 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_short(uint8_t *out, size_t outlen, p = out; - *p = NGTCP2_FIXED_BIT_MASK | (uint8_t)(hd->pkt_numlen - 1); + *p = (uint8_t)(hd->pkt_numlen - 1); + if (!(hd->flags & NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR)) { + *p |= NGTCP2_FIXED_BIT_MASK; + } if (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) { *p |= NGTCP2_SHORT_KEY_PHASE_BIT; } @@ -1428,7 +1466,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_datagram_frame(ngtcp2_datagram *dest, vi = ngtcp2_get_varint(&n, p); if (payloadlen - len < vi) { - return NGTCP2_FRAME_ENCODING_ERROR; + return NGTCP2_ERR_FRAME_ENCODING; } datalen = (size_t)vi; @@ -1436,6 +1474,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_datagram_frame(ngtcp2_datagram *dest, break; default: assert(0); + abort(); } dest->type = type; @@ -2020,8 +2059,8 @@ ngtcp2_pkt_encode_handshake_done_frame(uint8_t *out, size_t outlen, ngtcp2_ssize ngtcp2_pkt_encode_datagram_frame(uint8_t *out, size_t outlen, const ngtcp2_datagram *fr) { - size_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt); - size_t len = + uint64_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt); + uint64_t len = 1 + (fr->type == NGTCP2_FRAME_DATAGRAM ? 0 : ngtcp2_put_varint_len(datalen)) + datalen; @@ -2039,7 +2078,7 @@ ngtcp2_ssize ngtcp2_pkt_encode_datagram_frame(uint8_t *out, size_t outlen, *p++ = fr->type; if (fr->type == NGTCP2_FRAME_DATAGRAM_LEN) { - p = ngtcp2_put_varint(p, (uint64_t)datalen); + p = ngtcp2_put_varint(p, datalen); } for (i = 0; i < fr->datacnt; ++i) { @@ -2228,8 +2267,8 @@ ngtcp2_ssize ngtcp2_pkt_write_retry( /* Retry packet is sent at most once per one connection attempt. In the first connection attempt, client has to send random DCID - which is at least 8 bytes long. */ - if (odcid->datalen < 8) { + which is at least NGTCP2_MIN_INITIAL_DCIDLEN bytes long. */ + if (odcid->datalen < NGTCP2_MIN_INITIAL_DCIDLEN) { return NGTCP2_ERR_INVALID_ARGUMENT; } @@ -2244,10 +2283,16 @@ ngtcp2_ssize ngtcp2_pkt_write_retry( return pseudo_retrylen; } - if (version == NGTCP2_PROTO_VER_V1) { + switch (version) { + case NGTCP2_PROTO_VER_V1: nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V1; noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1; - } else { + break; + case NGTCP2_PROTO_VER_V2_DRAFT: + nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V2_DRAFT; + noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1; + break; + default: nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_DRAFT; noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1; } @@ -2330,10 +2375,16 @@ int ngtcp2_pkt_verify_retry_tag(uint32_t version, const ngtcp2_pkt_retry *retry, pseudo_retrylen = (size_t)(p - pseudo_retry); - if (version == NGTCP2_PROTO_VER_V1) { + switch (version) { + case NGTCP2_PROTO_VER_V1: nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V1; noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1; - } else { + break; + case NGTCP2_PROTO_VER_V2_DRAFT: + nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V2_DRAFT; + noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1; + break; + default: nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_DRAFT; noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1; } @@ -2353,7 +2404,7 @@ int ngtcp2_pkt_verify_retry_tag(uint32_t version, const ngtcp2_pkt_retry *retry, } size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset, - size_t len, size_t left) { + uint64_t len, size_t left) { size_t n = 1 /* type */ + ngtcp2_put_varint_len((uint64_t)stream_id) + (offset ? ngtcp2_put_varint_len(offset) : 0); @@ -2367,21 +2418,21 @@ size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset, #if SIZE_MAX > UINT32_MAX len = ngtcp2_min(len, 4611686018427387903lu); #endif /* SIZE_MAX > UINT32_MAX */ - return ngtcp2_min(len, left - 8); + return (size_t)ngtcp2_min(len, (uint64_t)(left - 8)); } if (left > 4 + 16383 && len > 16383) { len = ngtcp2_min(len, 1073741823); - return ngtcp2_min(len, left - 4); + return (size_t)ngtcp2_min(len, (uint64_t)(left - 4)); } if (left > 2 + 63 && len > 63) { len = ngtcp2_min(len, 16383); - return ngtcp2_min(len, left - 2); + return (size_t)ngtcp2_min(len, (uint64_t)(left - 2)); } len = ngtcp2_min(len, 63); - return ngtcp2_min(len, left - 1); + return (size_t)ngtcp2_min(len, (uint64_t)(left - 1)); } size_t ngtcp2_pkt_crypto_max_datalen(uint64_t offset, size_t len, size_t left) { @@ -2417,11 +2468,101 @@ size_t ngtcp2_pkt_crypto_max_datalen(uint64_t offset, size_t len, size_t left) { } size_t ngtcp2_pkt_datagram_framelen(size_t len) { - return 1 /* type */ + ngtcp2_put_varint_len((uint64_t)len) + len; + return 1 /* type */ + ngtcp2_put_varint_len(len) + len; } -uint8_t ngtcp2_pkt_get_type_long(uint8_t c) { - return (uint8_t)((c & NGTCP2_LONG_TYPE_MASK) >> 4); +int ngtcp2_is_supported_version(uint32_t version) { + switch (version) { + case NGTCP2_PROTO_VER_V1: + case NGTCP2_PROTO_VER_V2_DRAFT: + return 1; + default: + return NGTCP2_PROTO_VER_DRAFT_MIN <= version && + version <= NGTCP2_PROTO_VER_DRAFT_MAX; + } +} + +int ngtcp2_is_reserved_version(uint32_t version) { + return (version & NGTCP2_RESERVED_VERSION_MASK) == + NGTCP2_RESERVED_VERSION_MASK; +} + +uint8_t ngtcp2_pkt_get_type_long(uint32_t version, uint8_t c) { + uint8_t pkt_type = (uint8_t)((c & NGTCP2_LONG_TYPE_MASK) >> 4); + + switch (version) { + case NGTCP2_PROTO_VER_V2_DRAFT: + switch (pkt_type) { + case NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT: + return NGTCP2_PKT_INITIAL; + case NGTCP2_PKT_TYPE_0RTT_V2_DRAFT: + return NGTCP2_PKT_0RTT; + case NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT: + return NGTCP2_PKT_HANDSHAKE; + case NGTCP2_PKT_TYPE_RETRY_V2_DRAFT: + return NGTCP2_PKT_RETRY; + default: + return 0; + } + default: + if (!ngtcp2_is_supported_version(version)) { + return 0; + } + + /* QUIC v1 and draft versions share the same numeric packet + types. */ + switch (pkt_type) { + case NGTCP2_PKT_TYPE_INITIAL_V1: + return NGTCP2_PKT_INITIAL; + case NGTCP2_PKT_TYPE_0RTT_V1: + return NGTCP2_PKT_0RTT; + case NGTCP2_PKT_TYPE_HANDSHAKE_V1: + return NGTCP2_PKT_HANDSHAKE; + case NGTCP2_PKT_TYPE_RETRY_V1: + return NGTCP2_PKT_RETRY; + default: + return 0; + } + } +} + +uint8_t ngtcp2_pkt_versioned_type(uint32_t version, uint32_t pkt_type) { + switch (version) { + case NGTCP2_PROTO_VER_V2_DRAFT: + switch (pkt_type) { + case NGTCP2_PKT_INITIAL: + return NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT; + case NGTCP2_PKT_0RTT: + return NGTCP2_PKT_TYPE_0RTT_V2_DRAFT; + case NGTCP2_PKT_HANDSHAKE: + return NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT; + case NGTCP2_PKT_RETRY: + return NGTCP2_PKT_TYPE_RETRY_V2_DRAFT; + default: + assert(0); + abort(); + } + default: + /* Assume that unsupported versions share the numeric long packet + types with QUIC v1 in order to send a packet to elicit Version + Negotiation packet. */ + + /* QUIC v1 and draft versions share the same numeric packet + types. */ + switch (pkt_type) { + case NGTCP2_PKT_INITIAL: + return NGTCP2_PKT_TYPE_INITIAL_V1; + case NGTCP2_PKT_0RTT: + return NGTCP2_PKT_TYPE_0RTT_V1; + case NGTCP2_PKT_HANDSHAKE: + return NGTCP2_PKT_TYPE_HANDSHAKE_V1; + case NGTCP2_PKT_RETRY: + return NGTCP2_PKT_TYPE_RETRY_V1; + default: + assert(0); + abort(); + } + } } int ngtcp2_pkt_verify_reserved_bits(uint8_t c) { diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h index 708e0f6df3f87d..2f7838a08a5625 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h @@ -103,6 +103,40 @@ /* NGTCP2_RETRY_TAGLEN is the length of Retry packet integrity tag. */ #define NGTCP2_RETRY_TAGLEN 16 +/* NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE is the maximum UDP payload size + that this library can write. */ +#define NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE ((1 << 24) - 1) + +/* NGTCP2_PKT_LENGTHLEN is the number of bytes that is occupied by + Length field in Long packet header. */ +#define NGTCP2_PKT_LENGTHLEN 4 + +/* NGTCP2_PKT_TYPE_INITIAL_V1 is Initial long header packet type for + QUIC v1. */ +#define NGTCP2_PKT_TYPE_INITIAL_V1 0x0 +/* NGTCP2_PKT_TYPE_0RTT_V1 is 0RTT long header packet type for QUIC + v1. */ +#define NGTCP2_PKT_TYPE_0RTT_V1 0x1 +/* NGTCP2_PKT_TYPE_HANDSHAKE_V1 is Handshake long header packet type + for QUIC v1. */ +#define NGTCP2_PKT_TYPE_HANDSHAKE_V1 0x2 +/* NGTCP2_PKT_TYPE_RETRY_V1 is Retry long header packet type for QUIC + v1. */ +#define NGTCP2_PKT_TYPE_RETRY_V1 0x3 + +/* NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT is Initial long header packet type + for QUIC v2 draft. */ +#define NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT 0x1 +/* NGTCP2_PKT_TYPE_0RTT_V2_DRAFT is 0RTT long header packet type for + QUIC v2 draft. */ +#define NGTCP2_PKT_TYPE_0RTT_V2_DRAFT 0x2 +/* NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT is Handshake long header packet + type for QUIC v2 draft. */ +#define NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT 0x3 +/* NGTCP2_PKT_TYPE_RETRY_V2_DRAFT is Retry long header packet type for + QUIC v2 draft. */ +#define NGTCP2_PKT_TYPE_RETRY_V2_DRAFT 0x0 + typedef struct ngtcp2_pkt_retry { ngtcp2_cid odcid; ngtcp2_vec token; @@ -258,12 +292,12 @@ typedef struct ngtcp2_stop_sending { typedef struct ngtcp2_path_challenge { uint8_t type; - uint8_t data[8]; + uint8_t data[NGTCP2_PATH_CHALLENGE_DATALEN]; } ngtcp2_path_challenge; typedef struct ngtcp2_path_response { uint8_t type; - uint8_t data[8]; + uint8_t data[NGTCP2_PATH_CHALLENGE_DATALEN]; } ngtcp2_path_response; typedef struct ngtcp2_crypto { @@ -293,6 +327,8 @@ typedef struct ngtcp2_handshake_done { typedef struct ngtcp2_datagram { uint8_t type; + /* dgram_id is an opaque identifier chosen by an application. */ + uint64_t dgram_id; /* datacnt is the number of elements that data contains. */ size_t datacnt; /* data is a pointer to ngtcp2_vec array that stores data. */ @@ -1123,7 +1159,7 @@ int ngtcp2_pkt_validate_ack(ngtcp2_ack *fr); * small to write STREAM frame, this function returns (size_t)-1. */ size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset, - size_t len, size_t left); + uint64_t len, size_t left); /* * ngtcp2_pkt_crypto_max_datalen returns the maximum number of bytes @@ -1184,12 +1220,21 @@ int ngtcp2_pkt_verify_retry_tag(uint32_t version, const ngtcp2_pkt_retry *retry, const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx); +/* + * ngtcp2_pkt_versioned_type returns versioned packet type for a + * version |version| that corresponds to the version-independent + * |pkt_type|. + */ +uint8_t ngtcp2_pkt_versioned_type(uint32_t version, uint32_t pkt_type); + /** * @function * - * `ngtcp2_pkt_get_type_long` returns the long packet type. |c| is - * the first byte of Long packet header. + * `ngtcp2_pkt_get_type_long` returns the version-independent long + * packet type. |version| is the QUIC version. |c| is the first byte + * of Long packet header. If |version| is not supported by the + * library, it returns 0. */ -uint8_t ngtcp2_pkt_get_type_long(uint8_t c); +uint8_t ngtcp2_pkt_get_type_long(uint32_t version, uint8_t c); #endif /* NGTCP2_PKT_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pmtud.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pmtud.c new file mode 100644 index 00000000000000..26318bb1c8e38c --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pmtud.c @@ -0,0 +1,160 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_pmtud.h" + +#include + +#include "ngtcp2_mem.h" +#include "ngtcp2_macro.h" + +/* NGTCP2_PMTUD_PROBE_NUM_MAX is the maximum number of packets sent + for each probe. */ +#define NGTCP2_PMTUD_PROBE_NUM_MAX 3 + +static size_t mtu_probes[] = { + 1454 - 48, /* The well known MTU used by a domestic optic fiber + service in Japan. */ + 1390 - 48, /* Typical Tunneled MTU */ + 1280 - 48, /* IPv6 minimum MTU */ + 1492 - 48, /* PPPoE */ +}; + +static size_t mtu_probeslen = sizeof(mtu_probes) / sizeof(mtu_probes[0]); + +int ngtcp2_pmtud_new(ngtcp2_pmtud **ppmtud, size_t max_udp_payload_size, + size_t hard_max_udp_payload_size, int64_t tx_pkt_num, + const ngtcp2_mem *mem) { + ngtcp2_pmtud *pmtud = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pmtud)); + + if (pmtud == NULL) { + return NGTCP2_ERR_NOMEM; + } + + pmtud->mem = mem; + pmtud->mtu_idx = 0; + pmtud->num_pkts_sent = 0; + pmtud->expiry = UINT64_MAX; + pmtud->tx_pkt_num = tx_pkt_num; + pmtud->max_udp_payload_size = max_udp_payload_size; + pmtud->hard_max_udp_payload_size = hard_max_udp_payload_size; + pmtud->min_fail_udp_payload_size = SIZE_MAX; + + for (; pmtud->mtu_idx < mtu_probeslen; ++pmtud->mtu_idx) { + if (mtu_probes[pmtud->mtu_idx] > pmtud->hard_max_udp_payload_size) { + continue; + } + if (mtu_probes[pmtud->mtu_idx] > pmtud->max_udp_payload_size) { + break; + } + } + + *ppmtud = pmtud; + + return 0; +} + +void ngtcp2_pmtud_del(ngtcp2_pmtud *pmtud) { + if (!pmtud) { + return; + } + + ngtcp2_mem_free(pmtud->mem, pmtud); +} + +size_t ngtcp2_pmtud_probelen(ngtcp2_pmtud *pmtud) { + assert(pmtud->mtu_idx < mtu_probeslen); + + return mtu_probes[pmtud->mtu_idx]; +} + +void ngtcp2_pmtud_probe_sent(ngtcp2_pmtud *pmtud, ngtcp2_duration pto, + ngtcp2_tstamp ts) { + ngtcp2_tstamp timeout; + + if (++pmtud->num_pkts_sent < NGTCP2_PMTUD_PROBE_NUM_MAX) { + timeout = pto; + } else { + timeout = 3 * pto; + } + + pmtud->expiry = ts + timeout; +} + +int ngtcp2_pmtud_require_probe(ngtcp2_pmtud *pmtud) { + return pmtud->expiry == UINT64_MAX; +} + +static void pmtud_next_probe(ngtcp2_pmtud *pmtud) { + assert(pmtud->mtu_idx < mtu_probeslen); + + ++pmtud->mtu_idx; + pmtud->num_pkts_sent = 0; + pmtud->expiry = UINT64_MAX; + + for (; pmtud->mtu_idx < mtu_probeslen; ++pmtud->mtu_idx) { + if (mtu_probes[pmtud->mtu_idx] <= pmtud->max_udp_payload_size || + mtu_probes[pmtud->mtu_idx] > pmtud->hard_max_udp_payload_size) { + continue; + } + + if (mtu_probes[pmtud->mtu_idx] < pmtud->min_fail_udp_payload_size) { + break; + } + } +} + +void ngtcp2_pmtud_probe_success(ngtcp2_pmtud *pmtud, size_t payloadlen) { + pmtud->max_udp_payload_size = + ngtcp2_max(pmtud->max_udp_payload_size, payloadlen); + + assert(pmtud->mtu_idx < mtu_probeslen); + + if (mtu_probes[pmtud->mtu_idx] > pmtud->max_udp_payload_size) { + return; + } + + pmtud_next_probe(pmtud); +} + +void ngtcp2_pmtud_handle_expiry(ngtcp2_pmtud *pmtud, ngtcp2_tstamp ts) { + if (ts < pmtud->expiry) { + return; + } + + pmtud->expiry = UINT64_MAX; + + if (pmtud->num_pkts_sent < NGTCP2_PMTUD_PROBE_NUM_MAX) { + return; + } + + pmtud->min_fail_udp_payload_size = + ngtcp2_min(pmtud->min_fail_udp_payload_size, mtu_probes[pmtud->mtu_idx]); + + pmtud_next_probe(pmtud); +} + +int ngtcp2_pmtud_finished(ngtcp2_pmtud *pmtud) { + return pmtud->mtu_idx >= mtu_probeslen; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pmtud.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pmtud.h new file mode 100644 index 00000000000000..6b2e691cfc793a --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pmtud.h @@ -0,0 +1,123 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_PMTUD_H +#define NGTCP2_PMTUD_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +typedef struct ngtcp2_pmtud { + const ngtcp2_mem *mem; + /* mtu_idx is the index of UDP payload size candidates to try + out. */ + size_t mtu_idx; + /* num_pkts_sent is the number of mtu_idx sized UDP datagram payload + sent */ + size_t num_pkts_sent; + /* expiry is the expired, if it is reached, send out the next UDP + datagram. UINT64_MAX means no expiry, or expiration is canceled. + In either case, new probe packet should be sent unless we have + done all attempts. */ + ngtcp2_tstamp expiry; + /* tx_pkt_num is the smallest outgoing packet number where the + current discovery is performed. In other words, acknowledging + packet whose packet number lower than that does not indicate the + success of Path MTU Discovery. */ + int64_t tx_pkt_num; + /* max_udp_payload_size is the maximum UDP payload size which is + known to work. */ + size_t max_udp_payload_size; + /* hard_max_udp_payload_size is the maximum UDP payload size that is + going to be probed. */ + size_t hard_max_udp_payload_size; + /* min_fail_udp_payload_size is the minimum UDP payload size that is + known to fail. */ + size_t min_fail_udp_payload_size; +} ngtcp2_pmtud; + +/* + * ngtcp2_pmtud_new creates new ngtcp2_pmtud object, and assigns its + * pointer to |*ppmtud|. |max_udp_payload_size| is the maximum UDP + * payload size that is known to work for the current path. + * |tx_pkt_num| should be the next packet number to send, which is + * used to differentiate the PMTUD probe packet sent by the previous + * PMTUD. PMTUD might finish immediately if |max_udp_payload_size| is + * larger than or equal to all UDP payload probe candidates. + * Therefore, call ngtcp2_pmtud_finished to check this situation. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_pmtud_new(ngtcp2_pmtud **ppmtud, size_t max_udp_payload_size, + size_t hard_max_udp_payload_size, int64_t tx_pkt_num, + const ngtcp2_mem *mem); + +/* + * ngtcp2_pmtud_del deletes |pmtud|. + */ +void ngtcp2_pmtud_del(ngtcp2_pmtud *pmtud); + +/* + * ngtcp2_pmtud_probelen returns the length of UDP payload size for a + * PMTUD probe packet. + */ +size_t ngtcp2_pmtud_probelen(ngtcp2_pmtud *pmtud); + +/* + * ngtcp2_pmtud_probe_sent should be invoked when a PMTUD probe packet is + * sent. + */ +void ngtcp2_pmtud_probe_sent(ngtcp2_pmtud *pmtud, ngtcp2_duration pto, + ngtcp2_tstamp ts); + +/* + * ngtcp2_pmtud_require_probe returns nonzero if a PMTUD probe packet + * should be sent. + */ +int ngtcp2_pmtud_require_probe(ngtcp2_pmtud *pmtud); + +/* + * ngtcp2_pmtud_probe_success should be invoked when a PMTUD probe + * UDP datagram sized |payloadlen| is acknowledged. + */ +void ngtcp2_pmtud_probe_success(ngtcp2_pmtud *pmtud, size_t payloadlen); + +/* + * ngtcp2_pmtud_handle_expiry handles expiry. + */ +void ngtcp2_pmtud_handle_expiry(ngtcp2_pmtud *pmtud, ngtcp2_tstamp ts); + +/* + * ngtcp2_pmtud_finished returns nonzero if PMTUD has finished. + */ +int ngtcp2_pmtud_finished(ngtcp2_pmtud *pmtud); + +#endif /* NGTCP2_PMTUD_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c index 47f4f10a29f34c..5376246bd4caa9 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c @@ -57,7 +57,7 @@ int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd) { if (hd->type == NGTCP2_PKT_INITIAL) { ppe->len_offset += ngtcp2_put_varint_len(hd->token.len) + hd->token.len; } - ppe->pkt_num_offset = ppe->len_offset + 2; + ppe->pkt_num_offset = ppe->len_offset + NGTCP2_PKT_LENGTHLEN; rv = ngtcp2_pkt_encode_hd_long( buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, hd); } else { @@ -115,7 +115,7 @@ ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt) { assert(cc->hp_mask); if (ppe->len_offset) { - ngtcp2_put_varint14( + ngtcp2_put_varint30( buf->begin + ppe->len_offset, (uint16_t)(payloadlen + ppe->pkt_numlen + cc->aead.max_overhead)); } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c index d5f7759d6c9bad..314e005293c279 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c @@ -42,19 +42,12 @@ void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent, const uint8_t *data, int ngtcp2_pv_new(ngtcp2_pv **ppv, const ngtcp2_dcid *dcid, ngtcp2_duration timeout, uint8_t flags, ngtcp2_log *log, const ngtcp2_mem *mem) { - int rv; - (*ppv) = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pv)); if (*ppv == NULL) { return NGTCP2_ERR_NOMEM; } - rv = ngtcp2_ringbuf_init(&(*ppv)->ents, NGTCP2_PV_MAX_ENTRIES, - sizeof(ngtcp2_pv_entry), mem); - if (rv != 0) { - ngtcp2_mem_free(mem, *ppv); - return 0; - } + ngtcp2_static_ringbuf_pv_ents_init(&(*ppv)->ents); ngtcp2_dcid_copy(&(*ppv)->dcid, dcid); @@ -74,7 +67,6 @@ void ngtcp2_pv_del(ngtcp2_pv *pv) { if (pv == NULL) { return; } - ngtcp2_ringbuf_free(&pv->ents); ngtcp2_mem_free(pv->mem, pv); } @@ -85,11 +77,11 @@ void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data, assert(pv->probe_pkt_left); - if (ngtcp2_ringbuf_len(&pv->ents) == 0) { + if (ngtcp2_ringbuf_len(&pv->ents.rb) == 0) { pv->started_ts = ts; } - ent = ngtcp2_ringbuf_push_back(&pv->ents); + ent = ngtcp2_ringbuf_push_back(&pv->ents.rb); ngtcp2_pv_entry_init(ent, data, expiry, flags); pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_CANCEL_TIMER; @@ -97,7 +89,7 @@ void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data, } int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data) { - size_t len = ngtcp2_ringbuf_len(&pv->ents); + size_t len = ngtcp2_ringbuf_len(&pv->ents.rb); size_t i; ngtcp2_pv_entry *ent; @@ -106,7 +98,7 @@ int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data) { } for (i = 0; i < len; ++i) { - ent = ngtcp2_ringbuf_get(&pv->ents, i); + ent = ngtcp2_ringbuf_get(&pv->ents.rb, i); if (memcmp(ent->data, data, sizeof(ent->data)) == 0) { *pflags = ent->flags; ngtcp2_log_info(pv->log, NGTCP2_LOG_EVENT_PTV, "path has been validated"); @@ -120,11 +112,11 @@ int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data) { void ngtcp2_pv_handle_entry_expiry(ngtcp2_pv *pv, ngtcp2_tstamp ts) { ngtcp2_pv_entry *ent; - if (ngtcp2_ringbuf_len(&pv->ents) == 0) { + if (ngtcp2_ringbuf_len(&pv->ents.rb) == 0) { return; } - ent = ngtcp2_ringbuf_get(&pv->ents, ngtcp2_ringbuf_len(&pv->ents) - 1); + ent = ngtcp2_ringbuf_get(&pv->ents.rb, ngtcp2_ringbuf_len(&pv->ents.rb) - 1); if (ent->expiry > ts) { return; @@ -146,9 +138,9 @@ int ngtcp2_pv_validation_timed_out(ngtcp2_pv *pv, ngtcp2_tstamp ts) { return 0; } - assert(ngtcp2_ringbuf_len(&pv->ents)); + assert(ngtcp2_ringbuf_len(&pv->ents.rb)); - ent = ngtcp2_ringbuf_get(&pv->ents, ngtcp2_ringbuf_len(&pv->ents) - 1); + ent = ngtcp2_ringbuf_get(&pv->ents.rb, ngtcp2_ringbuf_len(&pv->ents.rb) - 1); t = pv->started_ts + pv->timeout; t = ngtcp2_max(t, ent->expiry); @@ -160,11 +152,11 @@ ngtcp2_tstamp ngtcp2_pv_next_expiry(ngtcp2_pv *pv) { ngtcp2_pv_entry *ent; if ((pv->flags & NGTCP2_PV_FLAG_CANCEL_TIMER) || - ngtcp2_ringbuf_len(&pv->ents) == 0) { + ngtcp2_ringbuf_len(&pv->ents.rb) == 0) { return UINT64_MAX; } - ent = ngtcp2_ringbuf_get(&pv->ents, ngtcp2_ringbuf_len(&pv->ents) - 1); + ent = ngtcp2_ringbuf_get(&pv->ents.rb, ngtcp2_ringbuf_len(&pv->ents.rb) - 1); return ent->expiry; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h index b532dbca98356b..293cbcaaf6e881 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h @@ -46,10 +46,10 @@ typedef struct ngtcp2_log ngtcp2_log; typedef struct ngtcp2_frame_chain ngtcp2_frame_chain; /* NGTCP2_PV_ENTRY_FLAG_NONE indicates that no flag is set. */ -#define NGTCP2_PV_ENTRY_FLAG_NONE 0x00 +#define NGTCP2_PV_ENTRY_FLAG_NONE 0x00u /* NGTCP2_PV_ENTRY_FLAG_UNDERSIZED indicates that UDP datagram which contains PATH_CHALLENGE is undersized (< 1200 bytes) */ -#define NGTCP2_PV_ENTRY_FLAG_UNDERSIZED 0x01 +#define NGTCP2_PV_ENTRY_FLAG_UNDERSIZED 0x01u typedef struct ngtcp2_pv_entry { /* expiry is the timestamp when this PATH_CHALLENGE expires. */ @@ -64,25 +64,30 @@ void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent, const uint8_t *data, ngtcp2_tstamp expiry, uint8_t flags); /* NGTCP2_PV_FLAG_NONE indicates no flag is set. */ -#define NGTCP2_PV_FLAG_NONE 0x00 +#define NGTCP2_PV_FLAG_NONE 0x00u /* NGTCP2_PV_FLAG_DONT_CARE indicates that the outcome of path validation should be ignored entirely. */ -#define NGTCP2_PV_FLAG_DONT_CARE 0x01 +#define NGTCP2_PV_FLAG_DONT_CARE 0x01u /* NGTCP2_PV_FLAG_CANCEL_TIMER indicates that the expiry timer is cancelled. */ -#define NGTCP2_PV_FLAG_CANCEL_TIMER 0x02 +#define NGTCP2_PV_FLAG_CANCEL_TIMER 0x02u /* NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE indicates that fallback DCID is available in ngtcp2_pv. If path validation fails, fallback to the fallback DCID. If path validation succeeds, fallback DCID is retired if it does not equal to the current DCID. */ -#define NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE 0x04 +#define NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE 0x04u /* NGTCP2_PV_FLAG_MTU_PROBE indicates that a validation must probe least MTU that QUIC requires, which is 1200 bytes. If it fails, a path is not viable. */ -#define NGTCP2_PV_FLAG_MTU_PROBE 0x08 +#define NGTCP2_PV_FLAG_MTU_PROBE 0x08u +/* NGTCP2_PV_FLAG_PREFERRED_ADDR indicates that client is migrating to + server's preferred address. This flag is only used by client. */ +#define NGTCP2_PV_FLAG_PREFERRED_ADDR 0x10u typedef struct ngtcp2_pv ngtcp2_pv; +ngtcp2_static_ringbuf_def(pv_ents, NGTCP2_PV_MAX_ENTRIES, + sizeof(ngtcp2_pv_entry)); /* * ngtcp2_pv is the context of a single path validation. */ @@ -95,7 +100,7 @@ struct ngtcp2_pv { fallback if this path validation fails. */ ngtcp2_dcid fallback_dcid; /* ents is the ring buffer of ngtcp2_pv_entry */ - ngtcp2_ringbuf ents; + ngtcp2_static_ringbuf_pv_ents ents; /* timeout is the duration within which this path validation should succeed. */ ngtcp2_duration timeout; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.c index 7c31ab64af6955..69eaeb7367438d 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.c @@ -28,6 +28,8 @@ #include "ngtcp2_str.h" #include "ngtcp2_vec.h" +#include "ngtcp2_conv.h" +#include "ngtcp2_net.h" void ngtcp2_qlog_init(ngtcp2_qlog *qlog, ngtcp2_qlog_write write, ngtcp2_tstamp ts, void *user_data) { @@ -185,8 +187,8 @@ static uint8_t *write_pair_cid_impl(uint8_t *p, const uint8_t *name, static uint8_t *write_common_fields(uint8_t *p, const ngtcp2_cid *odcid) { p = write_verbatim( - p, "\"common_fields\":{\"protocol_type\":\"QUIC_HTTP3\",\"time_format\":" - "\"relative\",\"reference_time\":\"0\",\"group_id\":"); + p, "\"common_fields\":{\"protocol_type\":[\"QUIC\"],\"time_format\":" + "\"relative\",\"reference_time\":0,\"group_id\":"); p = write_cid(p, odcid); *p++ = '}'; return p; @@ -215,7 +217,7 @@ void ngtcp2_qlog_start(ngtcp2_qlog *qlog, const ngtcp2_cid *odcid, int server) { } p = write_verbatim( - p, "{\"qlog_format\":\"NDJSON\",\"qlog_version\":\"draft-02\","); + p, "\x1e{\"qlog_format\":\"JSON-SEQ\",\"qlog_version\":\"0.3\","); p = write_trace(p, server, odcid); p = write_verbatim(p, "}\n"); @@ -238,6 +240,10 @@ static ngtcp2_vec vec_pkt_type_handshake = ngtcp2_make_vec_lit("handshake"); static ngtcp2_vec vec_pkt_type_0rtt = ngtcp2_make_vec_lit("0RTT"); static ngtcp2_vec vec_pkt_type_1rtt = ngtcp2_make_vec_lit("1RTT"); static ngtcp2_vec vec_pkt_type_retry = ngtcp2_make_vec_lit("retry"); +static ngtcp2_vec vec_pkt_type_version_negotiation = + ngtcp2_make_vec_lit("version_negotiation"); +static ngtcp2_vec vec_pkt_type_stateless_reset = + ngtcp2_make_vec_lit("stateless_reset"); static ngtcp2_vec vec_pkt_type_unknown = ngtcp2_make_vec_lit("unknown"); static const ngtcp2_vec *qlog_pkt_type(const ngtcp2_pkt_hd *hd) { @@ -256,19 +262,33 @@ static const ngtcp2_vec *qlog_pkt_type(const ngtcp2_pkt_hd *hd) { } } - return &vec_pkt_type_1rtt; + switch (hd->type) { + case NGTCP2_PKT_VERSION_NEGOTIATION: + return &vec_pkt_type_version_negotiation; + case NGTCP2_PKT_STATELESS_RESET: + return &vec_pkt_type_stateless_reset; + case NGTCP2_PKT_1RTT: + return &vec_pkt_type_1rtt; + default: + return &vec_pkt_type_unknown; + } } static uint8_t *write_pkt_hd(uint8_t *p, const ngtcp2_pkt_hd *hd) { /* - * {"packet_type":"version_negotiation","packet_number":"0000000000000000000"} + * {"packet_type":"version_negotiation","packet_number":"0000000000000000000","token":{"data":""}} */ -#define NGTCP2_QLOG_PKT_HD_OVERHEAD 75 +#define NGTCP2_QLOG_PKT_HD_OVERHEAD 95 *p++ = '{'; p = write_pair(p, "packet_type", qlog_pkt_type(hd)); *p++ = ','; p = write_pair_number(p, "packet_number", (uint64_t)hd->pkt_num); + if (hd->type == NGTCP2_PKT_INITIAL && hd->token.len) { + p = write_verbatim(p, ",\"token\":{"); + p = write_pair_hex(p, "data", hd->token.base, hd->token.len); + *p++ = '}'; + } /* TODO Write DCIL and DCID */ /* TODO Write SCIL and SCID */ *p++ = '}'; @@ -313,10 +333,7 @@ static uint8_t *write_ack_frame(uint8_t *p, const ngtcp2_ack *fr) { p = write_verbatim(p, "{\"frame_type\":\"ack\","); p = write_pair_duration(p, "ack_delay", fr->ack_delay_unscaled); - *p++ = ','; - p = write_string(p, "acked_ranges"); - *p++ = ':'; - *p++ = '['; + p = write_verbatim(p, ",\"acked_ranges\":["); largest_ack = fr->largest_ack; min_ack = fr->largest_ack - (int64_t)fr->first_ack_blklen; @@ -410,14 +427,15 @@ static uint8_t *write_crypto_frame(uint8_t *p, const ngtcp2_crypto *fr) { static uint8_t *write_new_token_frame(uint8_t *p, const ngtcp2_new_token *fr) { /* - * {"frame_type":"new_token","length":0000000000000000000,"token":""} + * {"frame_type":"new_token","length":0000000000000000000,"token":{"data":""}} */ -#define NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD 66 +#define NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD 75 p = write_verbatim(p, "{\"frame_type\":\"new_token\","); p = write_pair_number(p, "length", fr->token.len); - *p++ = ','; - p = write_pair_hex(p, "token", fr->token.base, fr->token.len); + p = write_verbatim(p, ",\"token\":{"); + p = write_pair_hex(p, "data", fr->token.base, fr->token.len); + *p++ = '}'; *p++ = '}'; return p; @@ -480,9 +498,7 @@ static uint8_t *write_max_streams_frame(uint8_t *p, */ #define NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD 89 - p = write_verbatim(p, "{\"frame_type\":\"max_streams\","); - p = write_string(p, "stream_type"); - *p++ = ':'; + p = write_verbatim(p, "{\"frame_type\":\"max_streams\",\"stream_type\":"); if (fr->type == NGTCP2_FRAME_MAX_STREAMS_BIDI) { p = write_string(p, "bidirectional"); } else { @@ -541,9 +557,9 @@ static uint8_t *write_streams_blocked_frame(uint8_t *p, static uint8_t * write_new_connection_id_frame(uint8_t *p, const ngtcp2_new_connection_id *fr) { /* - * {"frame_type":"new_connection_id","sequence_number":0000000000000000000,"retire_prior_to":0000000000000000000,"connection_id_length":0000000000000000000,"connection_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","stateless_reset_token":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"} + * {"frame_type":"new_connection_id","sequence_number":0000000000000000000,"retire_prior_to":0000000000000000000,"connection_id_length":0000000000000000000,"connection_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","stateless_reset_token":{"data":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}} */ -#define NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD 271 +#define NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD 280 p = write_verbatim(p, "{\"frame_type\":\"new_connection_id\","); p = write_pair_number(p, "sequence_number", fr->seq); @@ -553,10 +569,11 @@ write_new_connection_id_frame(uint8_t *p, const ngtcp2_new_connection_id *fr) { p = write_pair_number(p, "connection_id_length", fr->cid.datalen); *p++ = ','; p = write_pair_cid(p, "connection_id", &fr->cid); - *p++ = ','; - p = write_pair_hex(p, "stateless_reset_token", fr->stateless_reset_token, + p = write_verbatim(p, ",\"stateless_reset_token\":{"); + p = write_pair_hex(p, "data", fr->stateless_reset_token, sizeof(fr->stateless_reset_token)); *p++ = '}'; + *p++ = '}'; return p; } @@ -611,9 +628,8 @@ write_connection_close_frame(uint8_t *p, const ngtcp2_connection_close *fr) { */ #define NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD 131 - p = write_verbatim(p, "{\"frame_type\":\"connection_close\","); - p = write_string(p, "error_space"); - *p++ = ':'; + p = write_verbatim(p, + "{\"frame_type\":\"connection_close\",\"error_space\":"); if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) { p = write_string(p, "transport"); } else { @@ -669,6 +685,7 @@ static void qlog_pkt_write_start(ngtcp2_qlog *qlog, int sent) { ngtcp2_buf_reset(&qlog->buf); p = qlog->buf.last; + *p++ = '\x1e'; *p++ = '{'; p = qlog_write_time(qlog, p); p = write_verbatim(p, ",\"name\":"); @@ -690,14 +707,18 @@ static void qlog_pkt_write_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, } /* - * ],"header":,"raw":{"packet_size":0000000000000000000}}} + * ],"header":,"raw":{"length":0000000000000000000}}} * * plus, terminating LF */ #define NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD \ - (1 + 55 + NGTCP2_QLOG_PKT_HD_OVERHEAD) + (1 + 50 + NGTCP2_QLOG_PKT_HD_OVERHEAD) + + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD + hd->token.len * 2) { + return; + } - assert(ngtcp2_buf_left(&qlog->buf) >= NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD); assert(ngtcp2_buf_len(&qlog->buf)); /* Eat last ',' */ @@ -707,7 +728,7 @@ static void qlog_pkt_write_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, p = write_verbatim(p, "],\"header\":"); p = write_pkt_hd(p, hd); - p = write_verbatim(p, ",\"raw\":{\"packet_size\":"); + p = write_verbatim(p, ",\"raw\":{\"length\":"); p = write_number(p, pktlen); p = write_verbatim(p, "}}}\n"); @@ -726,15 +747,13 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) { switch (fr->type) { case NGTCP2_FRAME_PADDING: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PADDING_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PADDING_FRAME_OVERHEAD + 1) { return; } p = write_padding_frame(p, &fr->padding); break; case NGTCP2_FRAME_PING: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PING_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PING_FRAME_OVERHEAD + 1) { return; } p = write_ping_frame(p, &fr->ping); @@ -746,86 +765,75 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) { (size_t)(fr->type == NGTCP2_FRAME_ACK_ECN ? NGTCP2_QLOG_ACK_FRAME_ECN_OVERHEAD : 0) + - NGTCP2_QLOG_ACK_FRAME_RANGE_OVERHEAD * (1 + fr->ack.num_blks) + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_ACK_FRAME_RANGE_OVERHEAD * (1 + fr->ack.num_blks) + 1) { return; } p = write_ack_frame(p, &fr->ack); break; case NGTCP2_FRAME_RESET_STREAM: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_RESET_STREAM_FRAME_OVERHEAD + - 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_RESET_STREAM_FRAME_OVERHEAD + 1) { return; } p = write_reset_stream_frame(p, &fr->reset_stream); break; case NGTCP2_FRAME_STOP_SENDING: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_STOP_SENDING_FRAME_OVERHEAD + - 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_STOP_SENDING_FRAME_OVERHEAD + 1) { return; } p = write_stop_sending_frame(p, &fr->stop_sending); break; case NGTCP2_FRAME_CRYPTO: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_CRYPTO_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_CRYPTO_FRAME_OVERHEAD + 1) { return; } p = write_crypto_frame(p, &fr->crypto); break; case NGTCP2_FRAME_NEW_TOKEN: if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD + - fr->new_token.token.len * 2 + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + fr->new_token.token.len * 2 + 1) { return; } p = write_new_token_frame(p, &fr->new_token); break; case NGTCP2_FRAME_STREAM: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_STREAM_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_STREAM_FRAME_OVERHEAD + 1) { return; } p = write_stream_frame(p, &fr->stream); break; case NGTCP2_FRAME_MAX_DATA: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_MAX_DATA_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_MAX_DATA_FRAME_OVERHEAD + 1) { return; } p = write_max_data_frame(p, &fr->max_data); break; case NGTCP2_FRAME_MAX_STREAM_DATA: if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_MAX_STREAM_DATA_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_MAX_STREAM_DATA_FRAME_OVERHEAD + 1) { return; } p = write_max_stream_data_frame(p, &fr->max_stream_data); break; case NGTCP2_FRAME_MAX_STREAMS_BIDI: case NGTCP2_FRAME_MAX_STREAMS_UNI: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD + - 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD + 1) { return; } p = write_max_streams_frame(p, &fr->max_streams); break; case NGTCP2_FRAME_DATA_BLOCKED: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_DATA_BLOCKED_FRAME_OVERHEAD + - 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_DATA_BLOCKED_FRAME_OVERHEAD + 1) { return; } p = write_data_blocked_frame(p, &fr->data_blocked); break; case NGTCP2_FRAME_STREAM_DATA_BLOCKED: if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_STREAM_DATA_BLOCKED_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_STREAM_DATA_BLOCKED_FRAME_OVERHEAD + 1) { return; } p = write_stream_data_blocked_frame(p, &fr->stream_data_blocked); @@ -833,40 +841,35 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) { case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_STREAMS_BLOCKED_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_STREAMS_BLOCKED_FRAME_OVERHEAD + 1) { return; } p = write_streams_blocked_frame(p, &fr->streams_blocked); break; case NGTCP2_FRAME_NEW_CONNECTION_ID: if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD + 1) { return; } p = write_new_connection_id_frame(p, &fr->new_connection_id); break; case NGTCP2_FRAME_RETIRE_CONNECTION_ID: if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_RETIRE_CONNECTION_ID_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_RETIRE_CONNECTION_ID_FRAME_OVERHEAD + 1) { return; } p = write_retire_connection_id_frame(p, &fr->retire_connection_id); break; case NGTCP2_FRAME_PATH_CHALLENGE: if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_PATH_CHALLENGE_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_PATH_CHALLENGE_FRAME_OVERHEAD + 1) { return; } p = write_path_challenge_frame(p, &fr->path_challenge); break; case NGTCP2_FRAME_PATH_RESPONSE: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PATH_RESPONSE_FRAME_OVERHEAD + - 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_PATH_RESPONSE_FRAME_OVERHEAD + 1) { return; } p = write_path_response_frame(p, &fr->path_response); @@ -874,24 +877,21 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) { case NGTCP2_FRAME_CONNECTION_CLOSE: case NGTCP2_FRAME_CONNECTION_CLOSE_APP: if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD + 1) { return; } p = write_connection_close_frame(p, &fr->connection_close); break; case NGTCP2_FRAME_HANDSHAKE_DONE: if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_HANDSHAKE_DONE_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_HANDSHAKE_DONE_FRAME_OVERHEAD + 1) { return; } p = write_handshake_done_frame(p, &fr->handshake_done); break; case NGTCP2_FRAME_DATAGRAM: case NGTCP2_FRAME_DATAGRAM_LEN: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_DATAGRAM_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_DATAGRAM_FRAME_OVERHEAD + 1) { return; } p = write_datagram_frame(p, &fr->datagram); @@ -934,6 +934,7 @@ void ngtcp2_qlog_parameters_set_transport_params( return; } + *p++ = '\x1e'; *p++ = '{'; p = qlog_write_time(qlog, p); p = write_verbatim( @@ -958,9 +959,10 @@ void ngtcp2_qlog_parameters_set_transport_params( *p++ = ','; } if (params->stateless_reset_token_present) { - p = write_pair_hex(p, "stateless_reset_token", - params->stateless_reset_token, + p = write_verbatim(p, "\"stateless_reset_token\":{"); + p = write_pair_hex(p, "data", params->stateless_reset_token, sizeof(params->stateless_reset_token)); + *p++ = '}'; *p++ = ','; } p = write_pair_bool(p, "disable_active_migration", @@ -1009,14 +1011,17 @@ void ngtcp2_qlog_parameters_set_transport_params( p = write_pair_number(p, "port_v6", paddr->ipv6_port); *p++ = ','; p = write_pair_cid(p, "connection_id", &paddr->cid); - *p++ = ','; - p = write_pair_hex(p, "stateless_reset_token", paddr->stateless_reset_token, + p = write_verbatim(p, ",\"stateless_reset_token\":{"); + p = write_pair_hex(p, "data", paddr->stateless_reset_token, sizeof(paddr->stateless_reset_token)); *p++ = '}'; + *p++ = '}'; } *p++ = ','; p = write_pair_number(p, "max_datagram_frame_size", params->max_datagram_frame_size); + *p++ = ','; + p = write_pair_bool(p, "grease_quic_bit", params->grease_quic_bit); p = write_verbatim(p, "}}\n"); qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf, @@ -1032,6 +1037,7 @@ void ngtcp2_qlog_metrics_updated(ngtcp2_qlog *qlog, return; } + *p++ = '\x1e'; *p++ = '{'; p = qlog_write_time(qlog, p); p = write_verbatim(p, ",\"name\":\"recovery:metrics_updated\",\"data\":{"); @@ -1071,6 +1077,7 @@ void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent) { return; } + *p++ = '\x1e'; *p++ = '{'; p = qlog_write_time(qlog, p); p = write_verbatim( @@ -1087,22 +1094,110 @@ void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent) { (size_t)(p - buf)); } -void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, - const ngtcp2_pkt_hd *hd) { +void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, + const ngtcp2_pkt_retry *retry) { + uint8_t rawbuf[1024]; + ngtcp2_buf buf; + + if (!qlog->write) { + return; + } + + ngtcp2_buf_init(&buf, rawbuf, sizeof(rawbuf)); + + *buf.last++ = '\x1e'; + *buf.last++ = '{'; + buf.last = qlog_write_time(qlog, buf.last); + buf.last = write_verbatim( + buf.last, + ",\"name\":\"transport:packet_received\",\"data\":{\"header\":"); + + if (ngtcp2_buf_left(&buf) < + NGTCP2_QLOG_PKT_HD_OVERHEAD + hd->token.len * 2 + + sizeof(",\"retry_token\":{\"data\":\"\"}}}\n") - 1 + + retry->token.len * 2) { + return; + } + + buf.last = write_pkt_hd(buf.last, hd); + buf.last = write_verbatim(buf.last, ",\"retry_token\":{"); + buf.last = + write_pair_hex(buf.last, "data", retry->token.base, retry->token.len); + buf.last = write_verbatim(buf.last, "}}}\n"); + + qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf.pos, + ngtcp2_buf_len(&buf)); +} + +void ngtcp2_qlog_stateless_reset_pkt_received( + ngtcp2_qlog *qlog, const ngtcp2_pkt_stateless_reset *sr) { uint8_t buf[256]; uint8_t *p = buf; + ngtcp2_pkt_hd hd = {0}; if (!qlog->write) { return; } + hd.type = NGTCP2_PKT_STATELESS_RESET; + + *p++ = '\x1e'; *p++ = '{'; p = qlog_write_time(qlog, p); p = write_verbatim( p, ",\"name\":\"transport:packet_received\",\"data\":{\"header\":"); - p = write_pkt_hd(p, hd); + p = write_pkt_hd(p, &hd); + *p++ = ','; + p = write_pair_hex(p, "stateless_reset_token", sr->stateless_reset_token, + NGTCP2_STATELESS_RESET_TOKENLEN); p = write_verbatim(p, "}}\n"); qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf, (size_t)(p - buf)); } + +void ngtcp2_qlog_version_negotiation_pkt_received(ngtcp2_qlog *qlog, + const ngtcp2_pkt_hd *hd, + const uint32_t *sv, + size_t nsv) { + uint8_t rawbuf[512]; + ngtcp2_buf buf; + size_t i; + uint32_t v; + + if (!qlog->write) { + return; + } + + ngtcp2_buf_init(&buf, rawbuf, sizeof(rawbuf)); + + *buf.last++ = '\x1e'; + *buf.last++ = '{'; + buf.last = qlog_write_time(qlog, buf.last); + buf.last = write_verbatim( + buf.last, + ",\"name\":\"transport:packet_received\",\"data\":{\"header\":"); + buf.last = write_pkt_hd(buf.last, hd); + buf.last = write_verbatim(buf.last, ",\"supported_versions\":["); + + if (nsv) { + if (ngtcp2_buf_left(&buf) < + (sizeof("\"xxxxxxxx\",") - 1) * nsv - 1 + sizeof("]}}\n") - 1) { + return; + } + + v = ngtcp2_htonl(sv[0]); + buf.last = write_hex(buf.last, (const uint8_t *)&v, sizeof(v)); + + for (i = 1; i < nsv; ++i) { + *buf.last++ = ','; + v = ngtcp2_htonl(sv[i]); + buf.last = write_hex(buf.last, (const uint8_t *)&v, sizeof(v)); + } + } + + buf.last = write_verbatim(buf.last, "]}}\n"); + + qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf.pos, + ngtcp2_buf_len(&buf)); +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.h index cb3c2063f766c8..b9107c0e5c031a 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.h @@ -139,6 +139,23 @@ void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent); * ngtcp2_qlog_retry_pkt_received writes packet_received event for a * received Retry packet. */ -void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd); +void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, + const ngtcp2_pkt_retry *retry); + +/* + * ngtcp2_qlog_stateless_reset_pkt_received writes packet_received + * event for a received Stateless Reset packet. + */ +void ngtcp2_qlog_stateless_reset_pkt_received( + ngtcp2_qlog *qlog, const ngtcp2_pkt_stateless_reset *sr); + +/* + * ngtcp2_qlog_version_negotiation_pkt_received writes packet_received + * event for a received Version Negotiation packet. + */ +void ngtcp2_qlog_version_negotiation_pkt_received(ngtcp2_qlog *qlog, + const ngtcp2_pkt_hd *hd, + const uint32_t *sv, + size_t nsv); #endif /* NGTCP2_QLOG_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rcvry.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rcvry.h index e392c34ebfedb7..4cb40882192a77 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rcvry.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rcvry.h @@ -31,12 +31,10 @@ #include -/* NGTCP2_PKT_THRESHOLD is kPacketThreshold described in - draft-ietf-quic-recovery-22. */ +/* NGTCP2_PKT_THRESHOLD is kPacketThreshold described in RFC 9002. */ #define NGTCP2_PKT_THRESHOLD 3 -/* NGTCP2_GRANULARITY is kGranularity described in - draft-ietf-quic-recovery-17. */ +/* NGTCP2_GRANULARITY is kGranularity described in RFC 9002. */ #define NGTCP2_GRANULARITY NGTCP2_MILLISECONDS #endif /* NGTCP2_RCVRY_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c index e4deab1ff76b83..a6b3f73e73339c 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c @@ -43,24 +43,30 @@ unsigned int __popcnt(unsigned int x) { int ngtcp2_ringbuf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size, const ngtcp2_mem *mem) { + uint8_t *buf = ngtcp2_mem_malloc(mem, nmemb * size); + if (buf == NULL) { + return NGTCP2_ERR_NOMEM; + } + + ngtcp2_ringbuf_buf_init(rb, nmemb, size, buf, mem); + + return 0; +} + +void ngtcp2_ringbuf_buf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size, + uint8_t *buf, const ngtcp2_mem *mem) { #ifdef WIN32 assert(1 == __popcnt((unsigned int)nmemb)); #else assert(1 == __builtin_popcount((unsigned int)nmemb)); #endif - rb->buf = ngtcp2_mem_malloc(mem, nmemb * size); - if (rb->buf == NULL) { - return NGTCP2_ERR_NOMEM; - } - + rb->buf = buf; rb->mem = mem; rb->nmemb = nmemb; rb->size = size; rb->first = 0; rb->len = 0; - - return 0; } void ngtcp2_ringbuf_free(ngtcp2_ringbuf *rb) { diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h index c6e1421518342b..16635c941032c7 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h @@ -62,6 +62,13 @@ typedef struct ngtcp2_ringbuf { int ngtcp2_ringbuf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size, const ngtcp2_mem *mem); +/* + * ngtcp2_ringbuf_buf_init initializes |rb| with given buffer and + * size. + */ +void ngtcp2_ringbuf_buf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size, + uint8_t *buf, const ngtcp2_mem *mem); + /* * ngtcp2_ringbuf_free frees resources allocated for |rb|. This * function does not free the memory pointed by |rb|. @@ -107,4 +114,19 @@ void *ngtcp2_ringbuf_get(ngtcp2_ringbuf *rb, size_t offset); /* ngtcp2_ringbuf_full returns nonzero if |rb| is full. */ int ngtcp2_ringbuf_full(ngtcp2_ringbuf *rb); +/* ngtcp2_static_ringbuf_def defines ngtcp2_ringbuf struct wrapper + which uses a statically allocated buffer that is suitable for a + usage that does not change buffer size with ngtcp2_ringbuf_resize. + ngtcp2_ringbuf_free should never be called for rb field. */ +#define ngtcp2_static_ringbuf_def(NAME, NMEMB, SIZE) \ + typedef struct ngtcp2_static_ringbuf_##NAME { \ + ngtcp2_ringbuf rb; \ + uint8_t buf[(NMEMB) * (SIZE)]; \ + } ngtcp2_static_ringbuf_##NAME; \ + \ + static inline void ngtcp2_static_ringbuf_##NAME##_init( \ + ngtcp2_static_ringbuf_##NAME *srb) { \ + ngtcp2_ringbuf_buf_init(&srb->rb, (NMEMB), (SIZE), srb->buf, NULL); \ + } + #endif /* NGTCP2_RINGBUF_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c index 499c07ec6be247..9c3d75dc33ae0c 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c @@ -69,11 +69,8 @@ int ngtcp2_rob_init(ngtcp2_rob *rob, size_t chunk, const ngtcp2_mem *mem) { int rv; ngtcp2_rob_gap *g; - rv = ngtcp2_ksl_init(&rob->gapksl, ngtcp2_ksl_range_compar, - sizeof(ngtcp2_range), mem); - if (rv != 0) { - goto fail_gapksl_ksl_init; - } + ngtcp2_ksl_init(&rob->gapksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), + mem); rv = ngtcp2_rob_gap_new(&g, 0, UINT64_MAX, mem); if (rv != 0) { @@ -85,23 +82,18 @@ int ngtcp2_rob_init(ngtcp2_rob *rob, size_t chunk, const ngtcp2_mem *mem) { goto fail_gapksl_ksl_insert; } - rv = ngtcp2_ksl_init(&rob->dataksl, ngtcp2_ksl_range_compar, - sizeof(ngtcp2_range), mem); - if (rv != 0) { - goto fail_dataksl_ksl_init; - } + ngtcp2_ksl_init(&rob->dataksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), + mem); rob->chunk = chunk; rob->mem = mem; return 0; -fail_dataksl_ksl_init: fail_gapksl_ksl_insert: ngtcp2_rob_gap_del(g, mem); fail_rob_gap_new: ngtcp2_ksl_free(&rob->gapksl); -fail_gapksl_ksl_init: return rv; } @@ -185,7 +177,7 @@ int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data, break; } if (ngtcp2_range_eq(&g->range, &m)) { - ngtcp2_ksl_remove(&rob->gapksl, &it, &g->range); + ngtcp2_ksl_remove_hint(&rob->gapksl, &it, &it, &g->range); ngtcp2_rob_gap_del(g, rob->mem); rv = rob_write_data(rob, m.begin, data + (m.begin - offset), (size_t)ngtcp2_range_len(&m)); @@ -244,7 +236,7 @@ int ngtcp2_rob_remove_prefix(ngtcp2_rob *rob, uint64_t offset) { g->range.begin = offset; break; } - ngtcp2_ksl_remove(&rob->gapksl, &it, &g->range); + ngtcp2_ksl_remove_hint(&rob->gapksl, &it, &it, &g->range); ngtcp2_rob_gap_del(g, rob->mem); } @@ -255,7 +247,7 @@ int ngtcp2_rob_remove_prefix(ngtcp2_rob *rob, uint64_t offset) { if (offset < d->range.begin + rob->chunk) { return 0; } - ngtcp2_ksl_remove(&rob->dataksl, &it, &d->range); + ngtcp2_ksl_remove_hint(&rob->dataksl, &it, &it, &d->range); ngtcp2_rob_data_del(d, rob->mem); } @@ -305,7 +297,7 @@ void ngtcp2_rob_pop(ngtcp2_rob *rob, uint64_t offset, size_t len) { return; } - ngtcp2_ksl_remove(&rob->dataksl, NULL, &d->range); + ngtcp2_ksl_remove_hint(&rob->dataksl, NULL, &it, &d->range); ngtcp2_rob_data_del(d, rob->mem); } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c index e546fdf85c623b..7b50f98d41ec7b 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c @@ -23,6 +23,9 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "ngtcp2_rst.h" + +#include + #include "ngtcp2_rtb.h" #include "ngtcp2_cc.h" #include "ngtcp2_macro.h" @@ -32,6 +35,9 @@ void ngtcp2_rs_init(ngtcp2_rs *rs) { rs->delivered = 0; rs->prior_delivered = 0; rs->prior_ts = 0; + rs->tx_in_flight = 0; + rs->lost = 0; + rs->prior_lost = 0; rs->send_elapsed = 0; rs->ack_elapsed = 0; rs->is_app_limited = 0; @@ -39,10 +45,15 @@ void ngtcp2_rs_init(ngtcp2_rs *rs) { void ngtcp2_rst_init(ngtcp2_rst *rst) { ngtcp2_rs_init(&rst->rs); + ngtcp2_window_filter_init(&rst->wf, 12); rst->delivered = 0; rst->delivered_ts = 0; rst->first_sent_ts = 0; rst->app_limited = 0; + rst->next_round_delivered = 0; + rst->round_count = 0; + rst->is_cwnd_limited = 0; + rst->lost = 0; } void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent, @@ -54,15 +65,24 @@ void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent, ent->rst.delivered_ts = rst->delivered_ts; ent->rst.delivered = rst->delivered; ent->rst.is_app_limited = rst->app_limited != 0; + ent->rst.tx_in_flight = cstat->bytes_in_flight + ent->pktlen; + ent->rst.lost = rst->lost; } -int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) { +int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat, + uint64_t pkt_delivered) { ngtcp2_rs *rs = &rst->rs; + uint64_t rate; if (rst->app_limited && rst->delivered > rst->app_limited) { rst->app_limited = 0; } + if (pkt_delivered >= rst->next_round_delivered) { + rst->next_round_delivered = pkt_delivered; + ++rst->round_count; + } + if (rs->prior_ts == 0) { return 0; } @@ -70,14 +90,22 @@ int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) { rs->interval = ngtcp2_max(rs->send_elapsed, rs->ack_elapsed); rs->delivered = rst->delivered - rs->prior_delivered; + rs->lost = rst->lost - rs->prior_lost; if (rs->interval < cstat->min_rtt) { rs->interval = UINT64_MAX; return 0; } - if (rs->interval) { - cstat->delivery_rate_sec = rs->delivered * NGTCP2_SECONDS / rs->interval; + if (!rs->interval) { + return 0; + } + + rate = rs->delivered * NGTCP2_SECONDS / rs->interval; + + if (rate > ngtcp2_window_filter_get_best(&rst->wf) || !rst->app_limited) { + ngtcp2_window_filter_update(&rst->wf, rate, rst->round_count); + cstat->delivery_rate_sec = ngtcp2_window_filter_get_best(&rst->wf); } return 0; @@ -96,6 +124,8 @@ void ngtcp2_rst_update_rate_sample(ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent, rs->is_app_limited = ent->rst.is_app_limited; rs->send_elapsed = ent->ts - ent->rst.first_sent_ts; rs->ack_elapsed = rst->delivered_ts - ent->rst.delivered_ts; + rs->tx_in_flight = ent->rst.tx_in_flight; + rs->prior_lost = ent->rst.lost; rst->first_sent_ts = ent->ts; } } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h index 14aae998ebdf65..488c65575a5589 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h @@ -31,6 +31,8 @@ #include +#include "ngtcp2_window_filter.h" + typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry; /** @@ -43,6 +45,9 @@ typedef struct ngtcp2_rs { uint64_t delivered; uint64_t prior_delivered; ngtcp2_tstamp prior_ts; + uint64_t tx_in_flight; + uint64_t lost; + uint64_t prior_lost; ngtcp2_duration send_elapsed; ngtcp2_duration ack_elapsed; int is_app_limited; @@ -56,17 +61,23 @@ void ngtcp2_rs_init(ngtcp2_rs *rs); */ typedef struct ngtcp2_rst { ngtcp2_rs rs; + ngtcp2_window_filter wf; uint64_t delivered; ngtcp2_tstamp delivered_ts; ngtcp2_tstamp first_sent_ts; uint64_t app_limited; + uint64_t next_round_delivered; + uint64_t round_count; + uint64_t lost; + int is_cwnd_limited; } ngtcp2_rst; void ngtcp2_rst_init(ngtcp2_rst *rst); void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent, const ngtcp2_conn_stat *cstat); -int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat); +int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat, + uint64_t pkt_delivered); void ngtcp2_rst_update_rate_sample(ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent, ngtcp2_tstamp ts); void ngtcp2_rst_update_app_limited(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat); diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c index 1ef67ccbc6af5d..644071400a6eb7 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c @@ -46,6 +46,18 @@ int ngtcp2_frame_chain_new(ngtcp2_frame_chain **pfrc, const ngtcp2_mem *mem) { return 0; } +int ngtcp2_frame_chain_objalloc_new(ngtcp2_frame_chain **pfrc, + ngtcp2_objalloc *objalloc) { + *pfrc = ngtcp2_objalloc_frame_chain_get(objalloc); + if (*pfrc == NULL) { + return NGTCP2_ERR_NOMEM; + } + + ngtcp2_frame_chain_init(*pfrc); + + return 0; +} + int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen, const ngtcp2_mem *mem) { *pfrc = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_frame_chain) + extralen); @@ -58,35 +70,44 @@ int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen, return 0; } -int ngtcp2_frame_chain_stream_datacnt_new(ngtcp2_frame_chain **pfrc, - size_t datacnt, - const ngtcp2_mem *mem) { - size_t need = sizeof(ngtcp2_vec) * (datacnt - 1); - size_t avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_stream); +int ngtcp2_frame_chain_stream_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc, + size_t datacnt, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem) { + size_t need, avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_stream); - if (datacnt > 0 && need > avail) { - return ngtcp2_frame_chain_extralen_new(pfrc, need - avail, mem); + if (datacnt > 1) { + need = sizeof(ngtcp2_vec) * (datacnt - 1); + + if (need > avail) { + return ngtcp2_frame_chain_extralen_new(pfrc, need - avail, mem); + } } - return ngtcp2_frame_chain_new(pfrc, mem); + return ngtcp2_frame_chain_objalloc_new(pfrc, objalloc); } -int ngtcp2_frame_chain_crypto_datacnt_new(ngtcp2_frame_chain **pfrc, - size_t datacnt, - const ngtcp2_mem *mem) { - size_t need = sizeof(ngtcp2_vec) * (datacnt - 1); - size_t avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_crypto); +int ngtcp2_frame_chain_crypto_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc, + size_t datacnt, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem) { + size_t need, avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_crypto); + + if (datacnt > 1) { + need = sizeof(ngtcp2_vec) * (datacnt - 1); - if (datacnt > 0 && need > avail) { - return ngtcp2_frame_chain_extralen_new(pfrc, need - avail, mem); + if (need > avail) { + return ngtcp2_frame_chain_extralen_new(pfrc, need - avail, mem); + } } - return ngtcp2_frame_chain_new(pfrc, mem); + return ngtcp2_frame_chain_objalloc_new(pfrc, objalloc); } -int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc, - const ngtcp2_vec *token, - const ngtcp2_mem *mem) { +int ngtcp2_frame_chain_new_token_objalloc_new(ngtcp2_frame_chain **pfrc, + const ngtcp2_vec *token, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem) { size_t avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_new_token); int rv; uint8_t *p; @@ -95,7 +116,7 @@ int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc, if (token->len > avail) { rv = ngtcp2_frame_chain_extralen_new(pfrc, token->len - avail, mem); } else { - rv = ngtcp2_frame_chain_new(pfrc, mem); + rv = ngtcp2_frame_chain_objalloc_new(pfrc, objalloc); } if (rv != 0) { return rv; @@ -104,7 +125,7 @@ int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc, fr = &(*pfrc)->fr; fr->type = NGTCP2_FRAME_NEW_TOKEN; - p = (uint8_t *)(*pfrc) + sizeof(ngtcp2_new_token); + p = (uint8_t *)fr + sizeof(ngtcp2_new_token); memcpy(p, token->base, token->len); ngtcp2_vec_init(&fr->new_token.token, p, token->len); @@ -127,19 +148,71 @@ void ngtcp2_frame_chain_del(ngtcp2_frame_chain *frc, const ngtcp2_mem *mem) { ngtcp2_mem_free(mem, frc); } +void ngtcp2_frame_chain_objalloc_del(ngtcp2_frame_chain *frc, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem) { + ngtcp2_frame_chain_binder *binder; + + if (frc == NULL) { + return; + } + + switch (frc->fr.type) { + case NGTCP2_FRAME_STREAM: + if (frc->fr.stream.datacnt && + sizeof(ngtcp2_vec) * (frc->fr.stream.datacnt - 1) > + sizeof(ngtcp2_frame) - sizeof(ngtcp2_stream)) { + ngtcp2_frame_chain_del(frc, mem); + + return; + } + + break; + case NGTCP2_FRAME_CRYPTO: + if (frc->fr.crypto.datacnt && + sizeof(ngtcp2_vec) * (frc->fr.crypto.datacnt - 1) > + sizeof(ngtcp2_frame) - sizeof(ngtcp2_crypto)) { + ngtcp2_frame_chain_del(frc, mem); + + return; + } + + break; + case NGTCP2_FRAME_NEW_TOKEN: + if (frc->fr.new_token.token.len > + sizeof(ngtcp2_frame) - sizeof(ngtcp2_new_token)) { + ngtcp2_frame_chain_del(frc, mem); + + return; + } + + break; + } + + binder = frc->binder; + if (binder && --binder->refcount == 0) { + ngtcp2_mem_free(mem, binder); + } + + frc->binder = NULL; + + ngtcp2_objalloc_frame_chain_release(objalloc, frc); +} + void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc) { frc->next = NULL; frc->binder = NULL; } -void ngtcp2_frame_chain_list_del(ngtcp2_frame_chain *frc, - const ngtcp2_mem *mem) { +void ngtcp2_frame_chain_list_objalloc_del(ngtcp2_frame_chain *frc, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem) { ngtcp2_frame_chain *next; - for (; frc;) { + for (; frc; frc = next) { next = frc->next; - ngtcp2_frame_chain_del(frc, mem); - frc = next; + + ngtcp2_frame_chain_objalloc_del(frc, objalloc, mem); } } @@ -176,35 +249,46 @@ int ngtcp2_bind_frame_chains(ngtcp2_frame_chain *a, ngtcp2_frame_chain *b, return 0; } -int ngtcp2_rtb_entry_new(ngtcp2_rtb_entry **pent, const ngtcp2_pkt_hd *hd, - ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, - size_t pktlen, uint8_t flags, const ngtcp2_mem *mem) { - (*pent) = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_rtb_entry)); +static void rtb_entry_init(ngtcp2_rtb_entry *ent, const ngtcp2_pkt_hd *hd, + ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, + size_t pktlen, uint16_t flags) { + memset(ent, 0, sizeof(*ent)); + + ent->hd.pkt_num = hd->pkt_num; + ent->hd.type = hd->type; + ent->hd.flags = hd->flags; + ent->frc = frc; + ent->ts = ts; + ent->lost_ts = UINT64_MAX; + ent->pktlen = pktlen; + ent->flags = flags; + ent->next = NULL; +} + +int ngtcp2_rtb_entry_objalloc_new(ngtcp2_rtb_entry **pent, + const ngtcp2_pkt_hd *hd, + ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, + size_t pktlen, uint16_t flags, + ngtcp2_objalloc *objalloc) { + *pent = ngtcp2_objalloc_rtb_entry_get(objalloc); if (*pent == NULL) { return NGTCP2_ERR_NOMEM; } - (*pent)->hd.pkt_num = hd->pkt_num; - (*pent)->hd.type = hd->type; - (*pent)->hd.flags = hd->flags; - (*pent)->frc = frc; - (*pent)->ts = ts; - (*pent)->lost_ts = UINT64_MAX; - (*pent)->pktlen = pktlen; - (*pent)->flags = flags; - (*pent)->next = NULL; + rtb_entry_init(*pent, hd, frc, ts, pktlen, flags); return 0; } -void ngtcp2_rtb_entry_del(ngtcp2_rtb_entry *ent, const ngtcp2_mem *mem) { - if (ent == NULL) { - return; - } +void ngtcp2_rtb_entry_objalloc_del(ngtcp2_rtb_entry *ent, + ngtcp2_objalloc *objalloc, + ngtcp2_objalloc *frc_objalloc, + const ngtcp2_mem *mem) { + ngtcp2_frame_chain_list_objalloc_del(ent->frc, frc_objalloc, mem); - ngtcp2_frame_chain_list_del(ent->frc, mem); + ent->frc = NULL; - ngtcp2_mem_free(mem, ent); + ngtcp2_objalloc_rtb_entry_release(objalloc, ent); } static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { @@ -214,7 +298,10 @@ static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_pktns_id pktns_id, ngtcp2_strm *crypto, ngtcp2_rst *rst, ngtcp2_cc *cc, ngtcp2_log *log, ngtcp2_qlog *qlog, - const ngtcp2_mem *mem) { + ngtcp2_objalloc *rtb_entry_objalloc, + ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) { + rtb->rtb_entry_objalloc = rtb_entry_objalloc; + rtb->frc_objalloc = frc_objalloc; ngtcp2_ksl_init(&rtb->ents, greater, sizeof(int64_t), mem); rtb->crypto = crypto; rtb->rst = rst; @@ -225,12 +312,14 @@ void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_pktns_id pktns_id, rtb->largest_acked_tx_pkt_num = -1; rtb->num_ack_eliciting = 0; rtb->num_retransmittable = 0; + rtb->num_pto_eliciting = 0; rtb->probe_pkt_left = 0; rtb->pktns_id = pktns_id; rtb->cc_pkt_num = 0; rtb->cc_bytes_in_flight = 0; rtb->persistent_congestion_start_ts = UINT64_MAX; rtb->num_lost_pkts = 0; + rtb->num_lost_pmtud_pkts = 0; } void ngtcp2_rtb_free(ngtcp2_rtb *rtb) { @@ -243,7 +332,9 @@ void ngtcp2_rtb_free(ngtcp2_rtb *rtb) { it = ngtcp2_ksl_begin(&rtb->ents); for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { - ngtcp2_rtb_entry_del(ngtcp2_ksl_it_get(&it), rtb->mem); + ngtcp2_rtb_entry_objalloc_del(ngtcp2_ksl_it_get(&it), + rtb->rtb_entry_objalloc, rtb->frc_objalloc, + rtb->mem); } ngtcp2_ksl_free(&rtb->ents); @@ -266,14 +357,23 @@ static void rtb_on_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) { ++rtb->num_retransmittable; } + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING) { + ++rtb->num_pto_eliciting; + } } -static void rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, - ngtcp2_conn_stat *cstat) { +static size_t rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, + ngtcp2_conn_stat *cstat) { if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) { assert(rtb->num_lost_pkts); --rtb->num_lost_pkts; - return; + + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { + assert(rtb->num_lost_pmtud_pkts); + --rtb->num_lost_pmtud_pkts; + } + + return 0; } if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { @@ -287,22 +387,44 @@ static void rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, --rtb->num_retransmittable; } + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING) { + assert(rtb->num_pto_eliciting); + --rtb->num_pto_eliciting; + } + if (rtb->cc_pkt_num <= ent->hd.pkt_num) { assert(cstat->bytes_in_flight >= ent->pktlen); cstat->bytes_in_flight -= ent->pktlen; assert(rtb->cc_bytes_in_flight >= ent->pktlen); rtb->cc_bytes_in_flight -= ent->pktlen; + + /* If PMTUD packet is lost, we do not report the lost bytes to the + caller in order to ignore loss of PMTUD packet. */ + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { + return 0; + } + + return ent->pktlen; } + + return 0; } +/* NGTCP2_RECLAIM_FLAG_NONE indicates that no flag is set. */ +#define NGTCP2_RECLAIM_FLAG_NONE 0x00u +/* NGTCP2_RECLAIM_FLAG_ON_LOSS indicates that frames are reclaimed + because of the packet loss.*/ +#define NGTCP2_RECLAIM_FLAG_ON_LOSS 0x01u + /* * rtb_reclaim_frame queues unacknowledged frames included in |ent| * for retransmission. The re-queued frames are not deleted from - * |ent|. It returns the number of frames queued. + * |ent|. It returns the number of frames queued. |flags| is bitwise + * OR of 0 or more of NGTCP2_RECLAIM_FLAG_*. */ -static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, - ngtcp2_pktns *pktns, +static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, uint8_t flags, + ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_rtb_entry *ent) { ngtcp2_frame_chain *frc, *nfrc, **pfrc = &pktns->tx.frq; ngtcp2_frame *fr; @@ -310,9 +432,10 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, ngtcp2_range gap, range; size_t num_reclaimed = 0; int rv; + int streamfrq_empty; + + assert(ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE); - /* PADDING only (or PADDING + ACK ) packets will have NULL - ent->frc. */ /* TODO Reconsider the order of pfrc */ for (frc = ent->frc; frc; frc = frc->next) { fr = &frc->fr; @@ -334,13 +457,28 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, range.end = fr->stream.offset + ngtcp2_vec_len(fr->stream.data, fr->stream.datacnt); range = ngtcp2_range_intersect(&range, &gap); - if (ngtcp2_range_len(&range) == 0 && - (!fr->stream.fin || (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED))) { - continue; + if (ngtcp2_range_len(&range) == 0) { + if (!fr->stream.fin) { + /* 0 length STREAM frame with offset == 0 must be + retransmitted if no non-empty data is sent to this stream + and no data in this stream is acknowledged. */ + if (fr->stream.offset != 0 || fr->stream.datacnt != 0 || + strm->tx.offset || (strm->flags & NGTCP2_STRM_FLAG_ANY_ACKED)) { + continue; + } + } else if (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED) { + continue; + } + } + + if ((flags & NGTCP2_RECLAIM_FLAG_ON_LOSS) && + ent->hd.pkt_num != strm->tx.last_lost_pkt_num) { + strm->tx.last_lost_pkt_num = ent->hd.pkt_num; + ++strm->tx.loss_count; } - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, fr->stream.datacnt, - rtb->mem); + rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( + &nfrc, fr->stream.datacnt, rtb->frc_objalloc, rtb->mem); if (rv != 0) { return rv; } @@ -349,9 +487,10 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, ngtcp2_vec_copy(nfrc->fr.stream.data, fr->stream.data, fr->stream.datacnt); + streamfrq_empty = ngtcp2_strm_streamfrq_empty(strm); rv = ngtcp2_strm_streamfrq_push(strm, nfrc); if (rv != 0) { - ngtcp2_frame_chain_del(nfrc, conn->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, rtb->frc_objalloc, rtb->mem); return rv; } if (!ngtcp2_strm_is_tx_queued(strm)) { @@ -361,6 +500,9 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, return rv; } } + if (streamfrq_empty) { + ++conn->tx.strmq_nretrans; + } ++num_reclaimed; @@ -378,8 +520,8 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, continue; } - rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, fr->crypto.datacnt, - rtb->mem); + rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new( + &nfrc, fr->crypto.datacnt, rtb->frc_objalloc, rtb->mem); if (rv != 0) { return rv; } @@ -392,7 +534,7 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, &nfrc->fr.crypto.offset, nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, conn->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, rtb->frc_objalloc, rtb->mem); return rv; } @@ -400,8 +542,8 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, continue; case NGTCP2_FRAME_NEW_TOKEN: - rv = ngtcp2_frame_chain_new_token_new(&nfrc, &fr->new_token.token, - rtb->mem); + rv = ngtcp2_frame_chain_new_token_objalloc_new( + &nfrc, &fr->new_token.token, rtb->frc_objalloc, rtb->mem); if (rv != 0) { return rv; } @@ -412,8 +554,11 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, } break; + case NGTCP2_FRAME_DATAGRAM: + case NGTCP2_FRAME_DATAGRAM_LEN: + continue; default: - rv = ngtcp2_frame_chain_new(&nfrc, rtb->mem); + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, rtb->frc_objalloc); if (rv != 0) { return rv; } @@ -438,11 +583,41 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, return (ngtcp2_ssize)num_reclaimed; } +/* + * conn_process_lost_datagram calls ngtcp2_lost_datagram callback for + * lost DATAGRAM frames. + */ +static int conn_process_lost_datagram(ngtcp2_conn *conn, + ngtcp2_rtb_entry *ent) { + ngtcp2_frame_chain *frc; + int rv; + + for (frc = ent->frc; frc; frc = frc->next) { + switch (frc->fr.type) { + case NGTCP2_FRAME_DATAGRAM: + case NGTCP2_FRAME_DATAGRAM_LEN: + assert(conn->callbacks.lost_datagram); + + rv = conn->callbacks.lost_datagram(conn, frc->fr.datagram.dgram_id, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + break; + } + } + + return 0; +} + static int rtb_on_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it, - ngtcp2_rtb_entry *ent, ngtcp2_conn *conn, - ngtcp2_pktns *pktns, ngtcp2_tstamp ts) { + ngtcp2_rtb_entry *ent, ngtcp2_conn_stat *cstat, + ngtcp2_conn *conn, ngtcp2_pktns *pktns, + ngtcp2_tstamp ts) { int rv; ngtcp2_ssize reclaimed; + ngtcp2_cc *cc = rtb->cc; + ngtcp2_cc_pkt pkt; ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags, ent->ts); @@ -451,55 +626,60 @@ static int rtb_on_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it, ngtcp2_qlog_pkt_lost(rtb->qlog, ent); } - if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE)) { - if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) { - ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, - "pkn=%" PRId64 " has already been reclaimed on PTO", - ent->hd.pkt_num); - assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)); - assert(UINT64_MAX == ent->lost_ts); - - ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED; - ent->lost_ts = ts; - - ++rtb->num_lost_pkts; + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { + ++rtb->num_lost_pmtud_pkts; + } else if (rtb->cc->on_pkt_lost) { + cc->on_pkt_lost(cc, cstat, + ngtcp2_cc_pkt_init(&pkt, ent->hd.pkt_num, ent->pktlen, + rtb->pktns_id, ent->ts, ent->rst.lost, + ent->rst.tx_in_flight, + ent->rst.is_app_limited), + ts); + } - ngtcp2_ksl_it_next(it); + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) { + ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, + "pkn=%" PRId64 " has already been reclaimed on PTO", + ent->hd.pkt_num); + assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)); + assert(UINT64_MAX == ent->lost_ts); - return 0; - } + ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED; + ent->lost_ts = ts; - if (ent->frc) { - assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)); - assert(UINT64_MAX == ent->lost_ts); + ++rtb->num_lost_pkts; - reclaimed = rtb_reclaim_frame(rtb, conn, pktns, ent); - if (reclaimed < 0) { - return (int)reclaimed; - } + ngtcp2_ksl_it_next(it); - if (reclaimed) { - ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED; - ent->lost_ts = ts; + return 0; + } - ++rtb->num_lost_pkts; + if (conn->callbacks.lost_datagram && + (ent->flags & NGTCP2_RTB_ENTRY_FLAG_DATAGRAM)) { + rv = conn_process_lost_datagram(conn, ent); + if (rv != 0) { + return rv; + } + } - ngtcp2_ksl_it_next(it); + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) { + assert(ent->frc); + assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)); + assert(UINT64_MAX == ent->lost_ts); - return 0; - } + reclaimed = + rtb_reclaim_frame(rtb, NGTCP2_RECLAIM_FLAG_ON_LOSS, conn, pktns, ent); + if (reclaimed < 0) { + return (int)reclaimed; } - } else { - ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, - "pkn=%" PRId64 - " is a probe packet, no retransmission is necessary", - ent->hd.pkt_num); } - rv = ngtcp2_ksl_remove(&rtb->ents, it, &ent->hd.pkt_num); - assert(0 == rv); + ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED; + ent->lost_ts = ts; - ngtcp2_rtb_entry_del(ent, rtb->mem); + ++rtb->num_lost_pkts; + + ngtcp2_ksl_it_next(it); return 0; } @@ -526,7 +706,9 @@ static void rtb_remove(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it, ngtcp2_rtb_entry **pent, ngtcp2_rtb_entry *ent, ngtcp2_conn_stat *cstat) { int rv; - rv = ngtcp2_ksl_remove(&rtb->ents, it, &ent->hd.pkt_num); + (void)rv; + + rv = ngtcp2_ksl_remove_hint(&rtb->ents, it, it, &ent->hd.pkt_num); assert(0 == rv); rtb_on_remove(rtb, ent, cstat); @@ -535,6 +717,35 @@ static void rtb_remove(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it, ngtcp2_list_insert(ent, pent); } +static void conn_ack_crypto_data(ngtcp2_conn *conn, ngtcp2_pktns *pktns, + uint64_t datalen) { + ngtcp2_buf_chain **pbufchain, *bufchain; + size_t left; + + for (pbufchain = &pktns->crypto.tx.data; *pbufchain;) { + left = ngtcp2_buf_len(&(*pbufchain)->buf); + if (left > datalen) { + (*pbufchain)->buf.pos += datalen; + return; + } + + bufchain = *pbufchain; + *pbufchain = bufchain->next; + + ngtcp2_mem_free(conn->mem, bufchain); + + datalen -= left; + + if (datalen == 0) { + return; + } + } + + assert(datalen == 0); + + return; +} + static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, ngtcp2_conn *conn) { ngtcp2_frame_chain *frc; @@ -543,7 +754,19 @@ static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, int rv; uint64_t datalen; ngtcp2_strm *crypto = rtb->crypto; - ngtcp2_crypto_level crypto_level; + ngtcp2_pktns *pktns = NULL; + + if ((ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) && conn->pmtud && + conn->pmtud->tx_pkt_num <= ent->hd.pkt_num) { + ngtcp2_pmtud_probe_success(conn->pmtud, ent->pktlen); + + conn->dcid.current.max_udp_payload_size = + ngtcp2_max(conn->dcid.current.max_udp_payload_size, ent->pktlen); + + if (ngtcp2_pmtud_finished(conn->pmtud)) { + ngtcp2_conn_stop_pmtud(conn); + } + } for (frc = ent->frc; frc; frc = frc->next) { if (frc->binder) { @@ -557,6 +780,8 @@ static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, break; } + strm->flags |= NGTCP2_STRM_FLAG_ANY_ACKED; + if (frc->fr.stream.fin) { strm->flags |= NGTCP2_STRM_FLAG_FIN_ACKED; } @@ -584,7 +809,7 @@ static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, } } - rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, NGTCP2_NO_ERROR); + rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm); if (rv != 0) { return rv; } @@ -598,33 +823,28 @@ static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, return rv; } - if (conn->callbacks.acked_crypto_offset) { - stream_offset = ngtcp2_strm_get_acked_offset(crypto); - datalen = stream_offset - prev_stream_offset; - if (datalen == 0) { - break; - } - - switch (rtb->pktns_id) { - case NGTCP2_PKTNS_ID_INITIAL: - crypto_level = NGTCP2_CRYPTO_LEVEL_INITIAL; - break; - case NGTCP2_PKTNS_ID_HANDSHAKE: - crypto_level = NGTCP2_CRYPTO_LEVEL_HANDSHAKE; - break; - case NGTCP2_PKTNS_ID_APPLICATION: - crypto_level = NGTCP2_CRYPTO_LEVEL_APPLICATION; - break; - default: - assert(0); - } + stream_offset = ngtcp2_strm_get_acked_offset(crypto); + datalen = stream_offset - prev_stream_offset; + if (datalen == 0) { + break; + } - rv = conn->callbacks.acked_crypto_offset( - conn, crypto_level, prev_stream_offset, datalen, conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } + switch (rtb->pktns_id) { + case NGTCP2_PKTNS_ID_INITIAL: + pktns = conn->in_pktns; + break; + case NGTCP2_PKTNS_ID_HANDSHAKE: + pktns = conn->hs_pktns; + break; + case NGTCP2_PKTNS_ID_APPLICATION: + pktns = &conn->pktns; + break; + default: + assert(0); } + + conn_ack_crypto_data(conn, pktns, datalen); + break; case NGTCP2_FRAME_RESET_STREAM: strm = ngtcp2_conn_find_stream(conn, frc->fr.reset_stream.stream_id); @@ -632,14 +852,26 @@ static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, break; } strm->flags |= NGTCP2_STRM_FLAG_RST_ACKED; - rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, NGTCP2_NO_ERROR); + rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm); if (rv != 0) { return rv; } break; case NGTCP2_FRAME_RETIRE_CONNECTION_ID: - assert(conn->dcid.num_retire_queued); - --conn->dcid.num_retire_queued; + ngtcp2_conn_untrack_retired_dcid_seq(conn, + frc->fr.retire_connection_id.seq); + break; + case NGTCP2_FRAME_DATAGRAM: + case NGTCP2_FRAME_DATAGRAM_LEN: + if (!conn->callbacks.ack_datagram) { + break; + } + + rv = conn->callbacks.ack_datagram(conn, frc->fr.datagram.dgram_id, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } break; } } @@ -655,7 +887,9 @@ static void rtb_on_pkt_acked(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, cc->on_pkt_acked(cc, cstat, ngtcp2_cc_pkt_init(&pkt, ent->hd.pkt_num, ent->pktlen, - rtb->pktns_id, ent->ts), + rtb->pktns_id, ent->ts, ent->rst.lost, + ent->rst.tx_in_flight, + ent->rst.is_app_limited), ts); if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE) && @@ -705,6 +939,10 @@ static void conn_verify_ecn(ngtcp2_conn *conn, ngtcp2_pktns *pktns, } } +static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost, + ngtcp2_conn *conn, ngtcp2_pktns *pktns, + ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts); + ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, ngtcp2_conn_stat *cstat, ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_tstamp pkt_ts, @@ -723,10 +961,17 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, int ack_eliciting_pkt_acked = 0; size_t ecn_acked = 0; int verify_ecn = 0; + ngtcp2_cc_ack cc_ack = {0}; + size_t num_lost_pkts = rtb->num_lost_pkts - rtb->num_lost_pmtud_pkts; + + cc_ack.prior_bytes_in_flight = cstat->bytes_in_flight; + cc_ack.rtt = UINT64_MAX; if (conn && (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) && + (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR) && largest_ack >= conn->pktns.crypto.tx.ckm->pkt_num) { - conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED; + conn->flags &= (uint32_t) ~(NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED | + NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR); conn->crypto.key_update.confirmed_ts = ts; ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_CRY, "key update confirmed"); @@ -740,7 +985,7 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, /* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */ it = ngtcp2_ksl_lower_bound(&rtb->ents, &largest_ack); if (ngtcp2_ksl_it_end(&it)) { - if (verify_ecn) { + if (conn && verify_ecn) { conn_verify_ecn(conn, pktns, rtb->cc, cstat, fr, ecn_acked, largest_acked_sent_ts, ts); } @@ -800,15 +1045,14 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, } if (largest_pkt_sent_ts != UINT64_MAX && ack_eliciting_pkt_acked) { - ngtcp2_conn_update_rtt(conn, pkt_ts - largest_pkt_sent_ts, - fr->ack_delay_unscaled, ts); - if (cc->new_rtt_sample) { + cc_ack.rtt = pkt_ts - largest_pkt_sent_ts; + + rv = ngtcp2_conn_update_rtt(conn, cc_ack.rtt, fr->ack_delay_unscaled, ts); + if (rv == 0 && cc->new_rtt_sample) { cc->new_rtt_sample(cc, cstat, ts); } } - ngtcp2_rst_on_ack_recv(rtb->rst, cstat); - if (conn) { for (ent = acked_ent; ent; ent = acked_ent) { if (ent->hd.pkt_num >= pktns->tx.ecn.start_pkt_num && @@ -826,9 +1070,17 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, goto fail; } + if (ent->hd.pkt_num >= rtb->cc_pkt_num) { + assert(cc_ack.pkt_delivered <= ent->rst.delivered); + + cc_ack.bytes_delivered += ent->pktlen; + cc_ack.pkt_delivered = ent->rst.delivered; + } + rtb_on_pkt_acked(rtb, ent, cstat, ts); acked_ent = ent->next; - ngtcp2_rtb_entry_del(ent, rtb->mem); + ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, + rtb->frc_objalloc, rtb->mem); } if (verify_ecn) { @@ -840,29 +1092,48 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, for (ent = acked_ent; ent; ent = acked_ent) { rtb_on_pkt_acked(rtb, ent, cstat, ts); acked_ent = ent->next; - ngtcp2_rtb_entry_del(ent, rtb->mem); + ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, + rtb->frc_objalloc, rtb->mem); + } + } + + if (rtb->cc->on_spurious_congestion && num_lost_pkts && + rtb->num_lost_pkts - rtb->num_lost_pmtud_pkts == 0) { + rtb->cc->on_spurious_congestion(cc, cstat, ts); + } + + ngtcp2_rst_on_ack_recv(rtb->rst, cstat, cc_ack.pkt_delivered); + + if (conn && num_acked > 0) { + rv = rtb_detect_lost_pkt(rtb, &cc_ack.bytes_lost, conn, pktns, cstat, ts); + if (rv != 0) { + return rv; } } - cc->on_ack_recv(cc, cstat, ts); + rtb->rst->lost += cc_ack.bytes_lost; + + cc_ack.largest_acked_sent_ts = largest_acked_sent_ts; + cc->on_ack_recv(cc, cstat, &cc_ack, ts); return num_acked; fail: for (ent = acked_ent; ent; ent = acked_ent) { acked_ent = ent->next; - ngtcp2_rtb_entry_del(ent, rtb->mem); + ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, + rtb->frc_objalloc, rtb->mem); } return rv; } static int rtb_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_conn_stat *cstat, - const ngtcp2_rtb_entry *ent, uint64_t loss_delay, - ngtcp2_tstamp lost_send_time, uint64_t pkt_thres) { + const ngtcp2_rtb_entry *ent, ngtcp2_duration loss_delay, + size_t pkt_thres, ngtcp2_tstamp ts) { ngtcp2_tstamp loss_time; - if (ent->ts <= lost_send_time || + if (ent->ts + loss_delay <= ts || rtb->largest_acked_tx_pkt_num >= ent->hd.pkt_num + (int64_t)pkt_thres) { return 1; } @@ -906,12 +1177,11 @@ static int conn_all_ecn_pkt_lost(ngtcp2_conn *conn) { pktns->tx.ecn.validation_pkt_sent == pktns->tx.ecn.validation_pkt_lost; } -int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn, - ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat, - ngtcp2_duration pto, ngtcp2_tstamp ts) { +static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost, + ngtcp2_conn *conn, ngtcp2_pktns *pktns, + ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) { ngtcp2_rtb_entry *ent; ngtcp2_duration loss_delay; - ngtcp2_tstamp lost_send_time; ngtcp2_ksl_it it; ngtcp2_tstamp latest_ts, oldest_ts; int64_t last_lost_pkt_num; @@ -922,11 +1192,14 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn, rtb->cc_bytes_in_flight / cstat->max_udp_payload_size / 2; size_t ecn_pkt_lost = 0; ngtcp2_tstamp start_ts; + ngtcp2_duration pto = ngtcp2_conn_compute_pto(conn, pktns); + uint64_t bytes_lost = 0; + ngtcp2_duration max_ack_delay; pkt_thres = ngtcp2_max(pkt_thres, NGTCP2_PKT_THRESHOLD); + pkt_thres = ngtcp2_min(pkt_thres, 256); cstat->loss_time[rtb->pktns_id] = UINT64_MAX; loss_delay = compute_pkt_loss_delay(cstat); - lost_send_time = ts - loss_delay; it = ngtcp2_ksl_lower_bound(&rtb->ents, &rtb->largest_acked_tx_pkt_num); for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { @@ -936,15 +1209,18 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn, break; } - if (rtb_pkt_lost(rtb, cstat, ent, loss_delay, lost_send_time, pkt_thres)) { + if (rtb_pkt_lost(rtb, cstat, ent, loss_delay, (size_t)pkt_thres, ts)) { /* All entries from ent are considered to be lost. */ latest_ts = oldest_ts = ent->ts; last_lost_pkt_num = ent->hd.pkt_num; + max_ack_delay = conn->remote.transport_params + ? conn->remote.transport_params->max_ack_delay + : 0; - congestion_period = (cstat->smoothed_rtt + - ngtcp2_max(4 * cstat->rttvar, NGTCP2_GRANULARITY) + - conn->remote.transport_params.max_ack_delay) * - NGTCP2_PERSISTENT_CONGESTION_THRESHOLD; + congestion_period = + (cstat->smoothed_rtt + + ngtcp2_max(4 * cstat->rttvar, NGTCP2_GRANULARITY) + max_ack_delay) * + NGTCP2_PERSISTENT_CONGESTION_THRESHOLD; start_ts = ngtcp2_max(rtb->persistent_congestion_start_ts, cstat->first_rtt_sample_ts); @@ -974,13 +1250,19 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn, ++ecn_pkt_lost; } - rtb_on_remove(rtb, ent, cstat); - rv = rtb_on_pkt_lost(rtb, &it, ent, conn, pktns, ts); + bytes_lost += rtb_on_remove(rtb, ent, cstat); + rv = rtb_on_pkt_lost(rtb, &it, ent, cstat, conn, pktns, ts); if (rv != 0) { return rv; } } + /* If only PMTUD packets are lost, do not trigger congestion + event. */ + if (bytes_lost == 0) { + break; + } + switch (conn->tx.ecn.state) { case NGTCP2_ECN_STATE_TESTING: if (conn->tx.ecn.validation_start_ts == UINT64_MAX) { @@ -1038,15 +1320,27 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn, } } - ngtcp2_rtb_remove_excessive_lost_pkt(rtb, pkt_thres); + ngtcp2_rtb_remove_excessive_lost_pkt(rtb, (size_t)pkt_thres); + + if (ppkt_lost) { + *ppkt_lost = bytes_lost; + } return 0; } +int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn, + ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + return rtb_detect_lost_pkt(rtb, /* ppkt_lost = */ NULL, conn, pktns, cstat, + ts); +} + void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n) { ngtcp2_ksl_it it = ngtcp2_ksl_end(&rtb->ents); ngtcp2_rtb_entry *ent; int rv; + (void)rv; for (; rtb->num_lost_pkts > n;) { assert(ngtcp2_ksl_it_end(&it)); @@ -1059,9 +1353,15 @@ void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n) { "removing stale lost pkn=%" PRId64, ent->hd.pkt_num); --rtb->num_lost_pkts; - rv = ngtcp2_ksl_remove(&rtb->ents, &it, &ent->hd.pkt_num); + + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { + --rtb->num_lost_pmtud_pkts; + } + + rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num); assert(0 == rv); - ngtcp2_rtb_entry_del(ent, rtb->mem); + ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, + rtb->frc_objalloc, rtb->mem); } } @@ -1070,6 +1370,7 @@ void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto, ngtcp2_ksl_it it; ngtcp2_rtb_entry *ent; int rv; + (void)rv; if (ngtcp2_ksl_len(&rtb->ents) == 0) { return; @@ -1092,9 +1393,15 @@ void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto, "removing stale lost pkn=%" PRId64, ent->hd.pkt_num); --rtb->num_lost_pkts; - rv = ngtcp2_ksl_remove(&rtb->ents, &it, &ent->hd.pkt_num); + + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { + --rtb->num_lost_pmtud_pkts; + } + + rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num); assert(0 == rv); - ngtcp2_rtb_entry_del(ent, rtb->mem); + ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, + rtb->frc_objalloc, rtb->mem); if (ngtcp2_ksl_len(&rtb->ents) == 0) { return; @@ -1128,6 +1435,7 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, ngtcp2_stream *sfr; ngtcp2_strm *strm; int rv; + int streamfrq_empty; ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags, ent->ts); @@ -1144,19 +1452,21 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, return 0; } - if (!ent->frc) { - /* PADDING only (or PADDING + ACK ) packets will have NULL - ent->frc. */ - assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)); - assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED)); + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { + ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, + "pkn=%" PRId64 + " is a PMTUD probe packet, no retransmission is necessary", + ent->hd.pkt_num); return 0; } if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) { --rtb->num_lost_pkts; - } - if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) { + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { + --rtb->num_lost_pmtud_pkts; + } + ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, "pkn=%" PRId64 " was declared lost and has already been retransmitted", @@ -1171,6 +1481,14 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, return 0; } + if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) && + (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_DATAGRAM) || + !conn->callbacks.lost_datagram)) { + /* PADDING only (or PADDING + ACK ) packets will have NULL + ent->frc. */ + return 0; + } + pfrc = &ent->frc; for (; *pfrc;) { @@ -1184,12 +1502,13 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, strm = ngtcp2_conn_find_stream(conn, sfr->stream_id); if (!strm) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem); break; } + streamfrq_empty = ngtcp2_strm_streamfrq_empty(strm); rv = ngtcp2_strm_streamfrq_push(strm, frc); if (rv != 0) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem); return rv; } if (!ngtcp2_strm_is_tx_queued(strm)) { @@ -1199,6 +1518,9 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, return rv; } } + if (streamfrq_empty) { + ++conn->tx.strmq_nretrans; + } break; case NGTCP2_FRAME_CRYPTO: frc = *pfrc; @@ -1210,10 +1532,26 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, &frc->fr.crypto.offset, frc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem); return rv; } break; + case NGTCP2_FRAME_DATAGRAM: + case NGTCP2_FRAME_DATAGRAM_LEN: + frc = *pfrc; + + if (conn->callbacks.lost_datagram) { + rv = conn->callbacks.lost_datagram(conn, frc->fr.datagram.dgram_id, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + } + + *pfrc = (*pfrc)->next; + + ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem); + break; default: pfrc = &(*pfrc)->next; } @@ -1238,11 +1576,12 @@ int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn, ent = ngtcp2_ksl_it_get(&it); rtb_on_remove(rtb, ent, cstat); - rv = ngtcp2_ksl_remove(&rtb->ents, &it, &ent->hd.pkt_num); + rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num); assert(0 == rv); rv = rtb_on_pkt_lost_resched_move(rtb, conn, pktns, ent); - ngtcp2_rtb_entry_del(ent, rtb->mem); + ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, + rtb->frc_objalloc, rtb->mem); if (rv != 0) { return rv; } @@ -1251,6 +1590,31 @@ int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn, return 0; } +void ngtcp2_rtb_remove_early_data(ngtcp2_rtb *rtb, ngtcp2_conn_stat *cstat) { + ngtcp2_rtb_entry *ent; + ngtcp2_ksl_it it; + int rv; + (void)rv; + + it = ngtcp2_ksl_begin(&rtb->ents); + + for (; !ngtcp2_ksl_it_end(&it);) { + ent = ngtcp2_ksl_it_get(&it); + + if (ent->hd.type != NGTCP2_PKT_0RTT) { + ngtcp2_ksl_it_next(&it); + continue; + } + + rtb_on_remove(rtb, ent, cstat); + rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num); + assert(0 == rv); + + ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, + rtb->frc_objalloc, rtb->mem); + } +} + int ngtcp2_rtb_empty(ngtcp2_rtb *rtb) { return ngtcp2_ksl_len(&rtb->ents) == 0; } @@ -1280,7 +1644,8 @@ ngtcp2_ssize ngtcp2_rtb_reclaim_on_pto(ngtcp2_rtb *rtb, ngtcp2_conn *conn, assert(ent->frc); - reclaimed = rtb_reclaim_frame(rtb, conn, pktns, ent); + reclaimed = + rtb_reclaim_frame(rtb, NGTCP2_RECLAIM_FLAG_NONE, conn, pktns, ent); if (reclaimed < 0) { return reclaimed; } @@ -1292,6 +1657,12 @@ ngtcp2_ssize ngtcp2_rtb_reclaim_on_pto(ngtcp2_rtb *rtb, ngtcp2_conn *conn, assert(rtb->num_retransmittable); --rtb->num_retransmittable; + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING) { + ent->flags &= (uint16_t)~NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING; + assert(rtb->num_pto_eliciting); + --rtb->num_pto_eliciting; + } + if (reclaimed) { --num_pkts; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h index 70f43ffd92416a..a97805dbaf3bc3 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h @@ -34,6 +34,7 @@ #include "ngtcp2_pkt.h" #include "ngtcp2_ksl.h" #include "ngtcp2_pq.h" +#include "ngtcp2_objalloc.h" typedef struct ngtcp2_conn ngtcp2_conn; typedef struct ngtcp2_pktns ngtcp2_pktns; @@ -41,13 +42,14 @@ typedef struct ngtcp2_log ngtcp2_log; typedef struct ngtcp2_qlog ngtcp2_qlog; typedef struct ngtcp2_strm ngtcp2_strm; typedef struct ngtcp2_rst ngtcp2_rst; +typedef struct ngtcp2_cc ngtcp2_cc; /* NGTCP2_FRAME_CHAIN_BINDER_FLAG_NONE indicates that no flag is set. */ -#define NGTCP2_FRAME_CHAIN_BINDER_FLAG_NONE 0x00 +#define NGTCP2_FRAME_CHAIN_BINDER_FLAG_NONE 0x00u /* NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK indicates that an information which a frame carries has been acknowledged. */ -#define NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK 0x01 +#define NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK 0x01u /* * ngtcp2_frame_chain_binder binds 2 or more of ngtcp2_frame_chain to @@ -71,11 +73,19 @@ typedef struct ngtcp2_frame_chain ngtcp2_frame_chain; * ngtcp2_frame_chain chains frames in a single packet. */ struct ngtcp2_frame_chain { - ngtcp2_frame_chain *next; - ngtcp2_frame_chain_binder *binder; - ngtcp2_frame fr; + union { + struct { + ngtcp2_frame_chain *next; + ngtcp2_frame_chain_binder *binder; + ngtcp2_frame fr; + }; + + ngtcp2_opl_entry oplent; + }; }; +ngtcp2_objalloc_def(frame_chain, ngtcp2_frame_chain, oplent); + /* * ngtcp2_bind_frame_chains binds two frame chains |a| and |b| using * new or existing ngtcp2_frame_chain_binder. |a| might have non-NULL @@ -110,6 +120,13 @@ int ngtcp2_bind_frame_chains(ngtcp2_frame_chain *a, ngtcp2_frame_chain *b, */ int ngtcp2_frame_chain_new(ngtcp2_frame_chain **pfrc, const ngtcp2_mem *mem); +/* + * ngtcp2_frame_chain_objalloc_new behaves like + * ngtcp2_frame_chain_new, but it uses |objalloc| to allocate the object. + */ +int ngtcp2_frame_chain_objalloc_new(ngtcp2_frame_chain **pfrc, + ngtcp2_objalloc *objalloc); + /* * ngtcp2_frame_chain_extralen_new works like ngtcp2_frame_chain_new, * but it allocates extra memory |extralen| in order to extend @@ -119,30 +136,33 @@ int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen, const ngtcp2_mem *mem); /* - * ngtcp2_frame_chain_stream_datacnt_new works like + * ngtcp2_frame_chain_stream_datacnt_objalloc_new works like * ngtcp2_frame_chain_new, but it allocates enough data to store * additional |datacnt| - 1 ngtcp2_vec object after ngtcp2_stream - * object. If |datacnt| equals to 1, ngtcp2_frame_chain_new is called - * internally. + * object. If no additional space is required, + * ngtcp2_frame_chain_objalloc_new is called internally. */ -int ngtcp2_frame_chain_stream_datacnt_new(ngtcp2_frame_chain **pfrc, - size_t datacnt, - const ngtcp2_mem *mem); +int ngtcp2_frame_chain_stream_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc, + size_t datacnt, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem); /* - * ngtcp2_frame_chain_crypto_datacnt_new works like + * ngtcp2_frame_chain_crypto_datacnt_objalloc_new works like * ngtcp2_frame_chain_new, but it allocates enough data to store * additional |datacnt| - 1 ngtcp2_vec object after ngtcp2_crypto - * object. If |datacnt| equals to 1, ngtcp2_frame_chain_new is called - * internally. + * object. If no additional space is required, + * ngtcp2_frame_chain_objalloc_new is called internally. */ -int ngtcp2_frame_chain_crypto_datacnt_new(ngtcp2_frame_chain **pfrc, - size_t datacnt, - const ngtcp2_mem *mem); +int ngtcp2_frame_chain_crypto_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc, + size_t datacnt, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem); -int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc, - const ngtcp2_vec *token, - const ngtcp2_mem *mem); +int ngtcp2_frame_chain_new_token_objalloc_new(ngtcp2_frame_chain **pfrc, + const ngtcp2_vec *token, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem); /* * ngtcp2_frame_chain_del deallocates |frc|. It also deallocates the @@ -150,43 +170,62 @@ int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc, */ void ngtcp2_frame_chain_del(ngtcp2_frame_chain *frc, const ngtcp2_mem *mem); +/* + * ngtcp2_frame_chain_objalloc_del adds |frc| to |objalloc| for reuse. + * It might just delete |frc| depending on the frame type and the size + * of |frc|. + */ +void ngtcp2_frame_chain_objalloc_del(ngtcp2_frame_chain *frc, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem); + /* * ngtcp2_frame_chain_init initializes |frc|. */ void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc); /* - * ngtcp2_frame_chain_list_del deletes |frc|, and all objects - * connected by next field. + * ngtcp2_frame_chain_list_objalloc_del adds all ngtcp2_frame_chain + * linked from |frc| to |objalloc| for reuse. Depending on the frame type + * and its size, ngtcp2_frame_chain might be deleted instead. */ -void ngtcp2_frame_chain_list_del(ngtcp2_frame_chain *frc, - const ngtcp2_mem *mem); +void ngtcp2_frame_chain_list_objalloc_del(ngtcp2_frame_chain *frc, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem); /* NGTCP2_RTB_ENTRY_FLAG_NONE indicates that no flag is set. */ -#define NGTCP2_RTB_ENTRY_FLAG_NONE 0x00 +#define NGTCP2_RTB_ENTRY_FLAG_NONE 0x00u /* NGTCP2_RTB_ENTRY_FLAG_PROBE indicates that the entry includes a probe packet. */ -#define NGTCP2_RTB_ENTRY_FLAG_PROBE 0x01 +#define NGTCP2_RTB_ENTRY_FLAG_PROBE 0x01u /* NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE indicates that the entry includes a frame which must be retransmitted until it is acknowledged. In most cases, this flag is used along with - NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING. We have these 2 flags because - NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE triggers PTO, but just - NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING does not. */ -#define NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE 0x02 + NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING and + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING. */ +#define NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE 0x02u /* NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING indicates that the entry elicits acknowledgement. */ -#define NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING 0x04 +#define NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING 0x04u /* NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED indicates that the packet has been reclaimed on PTO. It is not marked lost yet and still consumes congestion window. */ -#define NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED 0x08 +#define NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED 0x08u /* NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED indicates that the entry - has been marked lost and scheduled to retransmit. */ -#define NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED 0x10 + has been marked lost and, optionally, scheduled to retransmit. */ +#define NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED 0x10u /* NGTCP2_RTB_ENTRY_FLAG_ECN indicates that the entry is included in a UDP datagram with ECN marking. */ -#define NGTCP2_RTB_ENTRY_FLAG_ECN 0x20 +#define NGTCP2_RTB_ENTRY_FLAG_ECN 0x20u +/* NGTCP2_RTB_ENTRY_FLAG_DATAGRAM indicates that the entry includes + DATAGRAM frame. */ +#define NGTCP2_RTB_ENTRY_FLAG_DATAGRAM 0x40u +/* NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE indicates that the entry includes + a PMTUD probe packet. */ +#define NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE 0x80u +/* NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING indicates that the entry + includes a packet which elicits PTO probe packets. */ +#define NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING 0x100u typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry; @@ -195,58 +234,69 @@ typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry; * to the one packet which is waiting for its ACK. */ struct ngtcp2_rtb_entry { - ngtcp2_rtb_entry *next; - - struct { - int64_t pkt_num; - uint8_t type; - uint8_t flags; - } hd; - ngtcp2_frame_chain *frc; - /* ts is the time point when a packet included in this entry is sent - to a peer. */ - ngtcp2_tstamp ts; - /* lost_ts is the time when this entry is marked lost. */ - ngtcp2_tstamp lost_ts; - /* pktlen is the length of QUIC packet */ - size_t pktlen; - struct { - uint64_t delivered; - ngtcp2_tstamp delivered_ts; - ngtcp2_tstamp first_sent_ts; - int is_app_limited; - } rst; - /* flags is bitwise-OR of zero or more of - NGTCP2_RTB_ENTRY_FLAG_*. */ - uint8_t flags; + union { + struct { + ngtcp2_rtb_entry *next; + + struct { + int64_t pkt_num; + uint8_t type; + uint8_t flags; + } hd; + ngtcp2_frame_chain *frc; + /* ts is the time point when a packet included in this entry is sent + to a peer. */ + ngtcp2_tstamp ts; + /* lost_ts is the time when this entry is marked lost. */ + ngtcp2_tstamp lost_ts; + /* pktlen is the length of QUIC packet */ + size_t pktlen; + struct { + uint64_t delivered; + ngtcp2_tstamp delivered_ts; + ngtcp2_tstamp first_sent_ts; + uint64_t tx_in_flight; + uint64_t lost; + int is_app_limited; + } rst; + /* flags is bitwise-OR of zero or more of + NGTCP2_RTB_ENTRY_FLAG_*. */ + uint16_t flags; + }; + + ngtcp2_opl_entry oplent; + }; }; +ngtcp2_objalloc_def(rtb_entry, ngtcp2_rtb_entry, oplent); + /* * ngtcp2_rtb_entry_new allocates ngtcp2_rtb_entry object, and assigns - * its pointer to |*pent|. On success, |*pent| takes ownership of - * |frc|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. + * its pointer to |*pent|. */ -int ngtcp2_rtb_entry_new(ngtcp2_rtb_entry **pent, const ngtcp2_pkt_hd *hd, - ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, - size_t pktlen, uint8_t flags, const ngtcp2_mem *mem); +int ngtcp2_rtb_entry_objalloc_new(ngtcp2_rtb_entry **pent, + const ngtcp2_pkt_hd *hd, + ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, + size_t pktlen, uint16_t flags, + ngtcp2_objalloc *objalloc); /* - * ngtcp2_rtb_entry_del deallocates |ent|. It also frees memory - * pointed by |ent|. + * ngtcp2_rtb_entry_objalloc_del adds |ent| to |objalloc| for reuse. + * ngtcp2_frame_chain linked from ent->frc are also added to + * |frc_objalloc| depending on their frame type and size. */ -void ngtcp2_rtb_entry_del(ngtcp2_rtb_entry *ent, const ngtcp2_mem *mem); +void ngtcp2_rtb_entry_objalloc_del(ngtcp2_rtb_entry *ent, + ngtcp2_objalloc *objalloc, + ngtcp2_objalloc *frc_objalloc, + const ngtcp2_mem *mem); /* * ngtcp2_rtb tracks sent packets, and its ACK timeout for * retransmission. */ typedef struct ngtcp2_rtb { + ngtcp2_objalloc *frc_objalloc; + ngtcp2_objalloc *rtb_entry_objalloc; /* ents includes ngtcp2_rtb_entry sorted by decreasing order of packet number. */ ngtcp2_ksl ents; @@ -265,6 +315,9 @@ typedef struct ngtcp2_rtb { /* num_retransmittable is the number of packets which contain frames that must be retransmitted on loss. */ size_t num_retransmittable; + /* num_pto_eliciting is the number of packets that elicit PTO probe + packets. */ + size_t num_pto_eliciting; /* probe_pkt_left is the number of probe packet to send */ size_t probe_pkt_left; /* pktns_id is the identifier of packet number space. */ @@ -283,6 +336,10 @@ typedef struct ngtcp2_rtb { /* num_lost_pkts is the number entries in ents which has NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED flag set. */ size_t num_lost_pkts; + /* num_lost_pmtud_pkts is the number of entries in ents which have + both NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED and + NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE flags set. */ + size_t num_lost_pmtud_pkts; } ngtcp2_rtb; /* @@ -290,7 +347,9 @@ typedef struct ngtcp2_rtb { */ void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_pktns_id pktns_id, ngtcp2_strm *crypto, ngtcp2_rst *rst, ngtcp2_cc *cc, - ngtcp2_log *log, ngtcp2_qlog *qlog, const ngtcp2_mem *mem); + ngtcp2_log *log, ngtcp2_qlog *qlog, + ngtcp2_objalloc *rtb_entry_objalloc, + ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem); /* * ngtcp2_rtb_free deallocates resources allocated for |rtb|. @@ -344,7 +403,7 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, */ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat, - ngtcp2_duration pto, ngtcp2_tstamp ts); + ngtcp2_tstamp ts); /* * ngtcp2_rtb_remove_expired_lost_pkt removes expired lost packet. @@ -367,6 +426,11 @@ ngtcp2_tstamp ngtcp2_rtb_lost_pkt_ts(ngtcp2_rtb *rtb); int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat); +/* + * ngtcp2_rtb_remove_early_data removes all entries for 0RTT packets. + */ +void ngtcp2_rtb_remove_early_data(ngtcp2_rtb *rtb, ngtcp2_conn_stat *cstat); + /* * ngtcp2_rtb_empty returns nonzero if |rtb| have no entry. */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.c index 3118955b248902..c1ce64a2e57ac4 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.c @@ -216,20 +216,6 @@ uint8_t *ngtcp2_encode_ipv6(uint8_t *dest, const uint8_t *addr) { return dest; } -int ngtcp2_verify_stateless_reset_token(const uint8_t *want, - const uint8_t *got) { - return !ngtcp2_check_invalid_stateless_reset_token(got) && - ngtcp2_cmemeq(want, got, NGTCP2_STATELESS_RESET_TOKENLEN) - ? 0 - : NGTCP2_ERR_INVALID_ARGUMENT; -} - -int ngtcp2_check_invalid_stateless_reset_token(const uint8_t *token) { - static uint8_t invalid_token[NGTCP2_STATELESS_RESET_TOKENLEN] = {0}; - - return 0 == memcmp(invalid_token, token, NGTCP2_STATELESS_RESET_TOKENLEN); -} - int ngtcp2_cmemeq(const uint8_t *a, const uint8_t *b, size_t n) { size_t i; int rv = 0; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.h index bd0145747c8f54..04735d6dec5c63 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.h @@ -77,25 +77,6 @@ uint8_t *ngtcp2_encode_ipv6(uint8_t *dest, const uint8_t *addr); char *ngtcp2_encode_printable_ascii(char *dest, const uint8_t *data, size_t len); -/* - * ngtcp2_verify_stateless_reset_token verifies stateless reset token - * |want| and |got|. This function returns 0 if |want| equals |got| - * and |got| is not all zero, or one of the following negative error - * codes: - * - * NGTCP2_ERR_INVALID_ARGUMENT - * Token does not match; or token is all zero. - */ -int ngtcp2_verify_stateless_reset_token(const uint8_t *want, - const uint8_t *got); - -/* - * ngtcp2_check_invalid_stateless_reset_token returns nonzero if - * |token| is invalid stateless reset token. Currently, token which - * consists of all zeros is considered invalid. - */ -int ngtcp2_check_invalid_stateless_reset_token(const uint8_t *token); - /* * ngtcp2_cmemeq returns nonzero if the first |n| bytes of the buffers * pointed by |a| and |b| are equal. The comparison is done in a diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c index 8e8eef0c9c9c93..6f20e866ad51c0 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c @@ -35,9 +35,11 @@ static int offset_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { return *(int64_t *)lhs < *(int64_t *)rhs; } -int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags, - uint64_t max_rx_offset, uint64_t max_tx_offset, - void *stream_user_data, const ngtcp2_mem *mem) { +void ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags, + uint64_t max_rx_offset, uint64_t max_tx_offset, + void *stream_user_data, ngtcp2_objalloc *frc_objalloc, + const ngtcp2_mem *mem) { + strm->frc_objalloc = frc_objalloc; strm->cycle = 0; strm->tx.acked_offset = NULL; strm->tx.cont_acked_offset = 0; @@ -45,6 +47,8 @@ int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags, strm->tx.offset = 0; strm->tx.max_offset = max_tx_offset; strm->tx.last_max_stream_data_ts = UINT64_MAX; + strm->tx.loss_count = 0; + strm->tx.last_lost_pkt_num = -1; strm->rx.rob = NULL; strm->rx.cont_offset = 0; strm->rx.last_offset = 0; @@ -53,12 +57,9 @@ int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags, strm->stream_user_data = stream_user_data; strm->rx.window = strm->rx.max_offset = strm->rx.unsent_max_offset = max_rx_offset; - strm->me.key = (uint64_t)stream_id; strm->pe.index = NGTCP2_PQ_BAD_INDEX; strm->mem = mem; strm->app_error_code = 0; - - return 0; } void ngtcp2_strm_free(ngtcp2_strm *strm) { @@ -71,17 +72,23 @@ void ngtcp2_strm_free(ngtcp2_strm *strm) { if (strm->tx.streamfrq) { for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { - ngtcp2_frame_chain_del(ngtcp2_ksl_it_get(&it), strm->mem); + ngtcp2_frame_chain_objalloc_del(ngtcp2_ksl_it_get(&it), + strm->frc_objalloc, strm->mem); } ngtcp2_ksl_free(strm->tx.streamfrq); ngtcp2_mem_free(strm->mem, strm->tx.streamfrq); } - ngtcp2_rob_free(strm->rx.rob); - ngtcp2_mem_free(strm->mem, strm->rx.rob); - ngtcp2_gaptr_free(strm->tx.acked_offset); - ngtcp2_mem_free(strm->mem, strm->tx.acked_offset); + if (strm->rx.rob) { + ngtcp2_rob_free(strm->rx.rob); + ngtcp2_mem_free(strm->mem, strm->rx.rob); + } + + if (strm->tx.acked_offset) { + ngtcp2_gaptr_free(strm->tx.acked_offset); + ngtcp2_mem_free(strm->mem, strm->tx.acked_offset); + } } static int strm_rob_init(ngtcp2_strm *strm) { @@ -155,17 +162,12 @@ void ngtcp2_strm_shutdown(ngtcp2_strm *strm, uint32_t flags) { } static int strm_streamfrq_init(ngtcp2_strm *strm) { - int rv; ngtcp2_ksl *streamfrq = ngtcp2_mem_malloc(strm->mem, sizeof(*streamfrq)); if (streamfrq == NULL) { return NGTCP2_ERR_NOMEM; } - rv = ngtcp2_ksl_init(streamfrq, offset_less, sizeof(uint64_t), strm->mem); - if (rv != 0) { - ngtcp2_mem_free(strm->mem, streamfrq); - return rv; - } + ngtcp2_ksl_init(streamfrq, offset_less, sizeof(uint64_t), strm->mem); strm->tx.streamfrq = streamfrq; @@ -210,7 +212,7 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm, frc = ngtcp2_ksl_it_get(&it); fr = &frc->fr.stream; - ngtcp2_ksl_remove(strm->tx.streamfrq, &it, &fr->offset); + ngtcp2_ksl_remove_hint(strm->tx.streamfrq, &it, &it, &fr->offset); idx = 0; offset = fr->offset; @@ -234,19 +236,27 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm, if (idx == fr->datacnt) { if (fr->fin) { if (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED) { - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); assert(ngtcp2_ksl_len(strm->tx.streamfrq) == 0); return 0; } - fr->offset = fr->offset + ngtcp2_vec_len(fr->data, fr->datacnt); + fr->offset += ngtcp2_vec_len(fr->data, fr->datacnt); fr->datacnt = 0; *pfrc = frc; return 0; } - ngtcp2_frame_chain_del(frc, strm->mem); + + if (fr->offset == 0 && fr->datacnt == 0 && strm->tx.offset == 0 && + !(strm->flags & NGTCP2_STRM_FLAG_ANY_ACKED)) { + *pfrc = frc; + + return 0; + } + + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); continue; } @@ -285,10 +295,10 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm, return 0; } - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, fr->datacnt - end_idx, - strm->mem); + rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( + &nfrc, fr->datacnt - end_idx, strm->frc_objalloc, strm->mem); if (rv != 0) { - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } @@ -310,8 +320,8 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm, rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, strm->mem); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } @@ -346,7 +356,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, ngtcp2_frame_chain *frc, *nfrc; int rv; size_t nmerged; - size_t datalen; + uint64_t datalen; ngtcp2_vec a[NGTCP2_MAX_STREAM_DATACNT]; ngtcp2_vec b[NGTCP2_MAX_STREAM_DATACNT]; size_t acnt, bcnt; @@ -375,7 +385,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &fr->offset, frc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } *pfrc = NULL; @@ -393,10 +403,11 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, assert(acnt > 0); assert(bcnt > 0); - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, bcnt, strm->mem); + rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( + &nfrc, bcnt, strm->frc_objalloc, strm->mem); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } @@ -412,15 +423,16 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, strm->mem); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, acnt, strm->mem); + rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( + &nfrc, acnt, strm->frc_objalloc, strm->mem); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } @@ -430,14 +442,14 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, nfr->datacnt = acnt; ngtcp2_vec_copy(nfr->data, a, acnt); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); *pfrc = nfrc; return 0; } - left -= datalen; + left -= (size_t)datalen; ngtcp2_vec_copy(a, fr->data, fr->datacnt); acnt = fr->datacnt; @@ -452,7 +464,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, rv = strm_streamfrq_unacked_pop(strm, &nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } if (nfrc == NULL) { @@ -463,7 +475,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, if (nfr->fin && nfr->datacnt == 0) { fr->fin = 1; - ngtcp2_frame_chain_del(nfrc, strm->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); break; } @@ -473,8 +485,8 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, strm->mem); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } break; @@ -485,7 +497,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, if (nfr->datacnt == 0) { fr->fin = nfr->fin; - ngtcp2_frame_chain_del(nfrc, strm->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); continue; } @@ -493,8 +505,8 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); if (rv != 0) { - ngtcp2_frame_chain_del(nfrc, strm->mem); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } @@ -512,9 +524,10 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, assert(acnt > fr->datacnt); - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, acnt, strm->mem); + rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( + &nfrc, acnt, strm->frc_objalloc, strm->mem); if (rv != 0) { - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } @@ -523,7 +536,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, nfr->datacnt = acnt; ngtcp2_vec_copy(nfr->data, a, acnt); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); *pfrc = nfrc; @@ -535,7 +548,7 @@ uint64_t ngtcp2_strm_streamfrq_unacked_offset(ngtcp2_strm *strm) { ngtcp2_stream *fr; ngtcp2_range gap; ngtcp2_ksl_it it; - size_t datalen; + uint64_t datalen; assert(strm->tx.streamfrq); assert(ngtcp2_ksl_len(strm->tx.streamfrq)); @@ -589,7 +602,7 @@ void ngtcp2_strm_streamfrq_clear(ngtcp2_strm *strm) { for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { frc = ngtcp2_ksl_it_get(&it); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); } ngtcp2_ksl_clear(strm->tx.streamfrq); } @@ -607,9 +620,13 @@ int ngtcp2_strm_is_all_tx_data_acked(ngtcp2_strm *strm) { strm->tx.offset; } +int ngtcp2_strm_is_all_tx_data_fin_acked(ngtcp2_strm *strm) { + return (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED) && + ngtcp2_strm_is_all_tx_data_acked(strm); +} + ngtcp2_range ngtcp2_strm_get_unacked_range_after(ngtcp2_strm *strm, uint64_t offset) { - ngtcp2_ksl_it gapit; ngtcp2_range gap; if (strm->tx.acked_offset == NULL) { @@ -618,8 +635,7 @@ ngtcp2_range ngtcp2_strm_get_unacked_range_after(ngtcp2_strm *strm, return gap; } - gapit = ngtcp2_gaptr_get_first_gap_after(strm->tx.acked_offset, offset); - return *(ngtcp2_range *)ngtcp2_ksl_it_key(&gapit); + return ngtcp2_gaptr_get_first_gap_after(strm->tx.acked_offset, offset); } uint64_t ngtcp2_strm_get_acked_offset(ngtcp2_strm *strm) { @@ -631,7 +647,6 @@ uint64_t ngtcp2_strm_get_acked_offset(ngtcp2_strm *strm) { } static int strm_acked_offset_init(ngtcp2_strm *strm) { - int rv; ngtcp2_gaptr *acked_offset = ngtcp2_mem_malloc(strm->mem, sizeof(*acked_offset)); @@ -639,11 +654,7 @@ static int strm_acked_offset_init(ngtcp2_strm *strm) { return NGTCP2_ERR_NOMEM; } - rv = ngtcp2_gaptr_init(acked_offset, strm->mem); - if (rv != 0) { - ngtcp2_mem_free(strm->mem, acked_offset); - return rv; - } + ngtcp2_gaptr_init(acked_offset, strm->mem); strm->tx.acked_offset = acked_offset; @@ -673,3 +684,15 @@ int ngtcp2_strm_ack_data(ngtcp2_strm *strm, uint64_t offset, uint64_t len) { return ngtcp2_gaptr_push(strm->tx.acked_offset, offset, len); } + +void ngtcp2_strm_set_app_error_code(ngtcp2_strm *strm, + uint64_t app_error_code) { + if (strm->flags & NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET) { + return; + } + + assert(0 == strm->app_error_code); + + strm->flags |= NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET; + strm->app_error_code = app_error_code; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.h index 6b7418706c760e..8e3cfe83543509 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.h @@ -40,110 +40,138 @@ typedef struct ngtcp2_frame_chain ngtcp2_frame_chain; /* NGTCP2_STRM_FLAG_NONE indicates that no flag is set. */ -#define NGTCP2_STRM_FLAG_NONE 0x00 +#define NGTCP2_STRM_FLAG_NONE 0x00u /* NGTCP2_STRM_FLAG_SHUT_RD indicates that further reception of stream data is not allowed. */ -#define NGTCP2_STRM_FLAG_SHUT_RD 0x01 +#define NGTCP2_STRM_FLAG_SHUT_RD 0x01u /* NGTCP2_STRM_FLAG_SHUT_WR indicates that further transmission of stream data is not allowed. */ -#define NGTCP2_STRM_FLAG_SHUT_WR 0x02 +#define NGTCP2_STRM_FLAG_SHUT_WR 0x02u #define NGTCP2_STRM_FLAG_SHUT_RDWR \ (NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_SHUT_WR) /* NGTCP2_STRM_FLAG_SENT_RST indicates that RST_STREAM is sent from the local endpoint. In this case, NGTCP2_STRM_FLAG_SHUT_WR is also set. */ -#define NGTCP2_STRM_FLAG_SENT_RST 0x04 +#define NGTCP2_STRM_FLAG_SENT_RST 0x04u /* NGTCP2_STRM_FLAG_SENT_RST indicates that RST_STREAM is received from the remote endpoint. In this case, NGTCP2_STRM_FLAG_SHUT_RD is also set. */ -#define NGTCP2_STRM_FLAG_RECV_RST 0x08 +#define NGTCP2_STRM_FLAG_RECV_RST 0x08u /* NGTCP2_STRM_FLAG_STOP_SENDING indicates that STOP_SENDING is sent from the local endpoint. */ -#define NGTCP2_STRM_FLAG_STOP_SENDING 0x10 +#define NGTCP2_STRM_FLAG_STOP_SENDING 0x10u /* NGTCP2_STRM_FLAG_RST_ACKED indicates that the outgoing RST_STREAM is acknowledged by peer. */ -#define NGTCP2_STRM_FLAG_RST_ACKED 0x20 +#define NGTCP2_STRM_FLAG_RST_ACKED 0x20u /* NGTCP2_STRM_FLAG_FIN_ACKED indicates that a STREAM with FIN bit set is acknowledged by a remote endpoint. */ -#define NGTCP2_STRM_FLAG_FIN_ACKED 0x40 +#define NGTCP2_STRM_FLAG_FIN_ACKED 0x40u +/* NGTCP2_STRM_FLAG_ANY_ACKED indicates that any portion of stream + data, including 0 length segment, is acknowledged. */ +#define NGTCP2_STRM_FLAG_ANY_ACKED 0x80u +/* NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET indicates that app_error_code + field is set. This resolves the ambiguity that the initial + app_error_code value 0 might be a proper application error code. + In this case, without this flag, we are unable to distinguish + assigned value from unassigned one. */ +#define NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET 0x100u +/* NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED is set when + stream_stop_sending callback is called. */ +#define NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED 0x200u typedef struct ngtcp2_strm ngtcp2_strm; struct ngtcp2_strm { - ngtcp2_map_entry me; - ngtcp2_pq_entry pe; - uint64_t cycle; + union { + struct { + ngtcp2_pq_entry pe; + uint64_t cycle; + ngtcp2_objalloc *frc_objalloc; - struct { - /* acked_offset tracks acknowledged outgoing data. */ - ngtcp2_gaptr *acked_offset; - /* cont_acked_offset is the offset that all data up to this offset - is acknowledged by a remote endpoint. It is used until the - remote endpoint acknowledges data in out-of-order. After that, - acked_offset is used instead. */ - uint64_t cont_acked_offset; - /* streamfrq contains STREAM frame for retransmission. The flow - control credits have been paid when they are transmitted first - time. There are no restriction regarding flow control for - retransmission. */ - ngtcp2_ksl *streamfrq; - /* offset is the next offset of outgoing data. In other words, it - is the number of bytes sent in this stream without - duplication. */ - uint64_t offset; - /* max_tx_offset is the maximum offset that local endpoint can - send for this stream. */ - uint64_t max_offset; - /* last_max_stream_data_ts is the timestamp when last - MAX_STREAM_DATA frame is sent. */ - ngtcp2_tstamp last_max_stream_data_ts; - } tx; + struct { + /* acked_offset tracks acknowledged outgoing data. */ + ngtcp2_gaptr *acked_offset; + /* cont_acked_offset is the offset that all data up to this offset + is acknowledged by a remote endpoint. It is used until the + remote endpoint acknowledges data in out-of-order. After that, + acked_offset is used instead. */ + uint64_t cont_acked_offset; + /* streamfrq contains STREAM frame for retransmission. The flow + control credits have been paid when they are transmitted first + time. There are no restriction regarding flow control for + retransmission. */ + ngtcp2_ksl *streamfrq; + /* offset is the next offset of outgoing data. In other words, it + is the number of bytes sent in this stream without + duplication. */ + uint64_t offset; + /* max_tx_offset is the maximum offset that local endpoint can + send for this stream. */ + uint64_t max_offset; + /* last_max_stream_data_ts is the timestamp when last + MAX_STREAM_DATA frame is sent. */ + ngtcp2_tstamp last_max_stream_data_ts; + /* loss_count is the number of packets that contain STREAM + frame for this stream and are declared to be lost. It may + include the spurious losses. It does not include a packet + whose contents have been reclaimed for PTO and which is + later declared to be lost. Those data are not blocked by + the flow control and will be sent immediately if no other + restrictions are applied. */ + size_t loss_count; + /* last_lost_pkt_num is the packet number of the packet that + is counted to loss_count. It is used to avoid to count + multiple STREAM frames in one lost packet. */ + int64_t last_lost_pkt_num; + } tx; - struct { - /* rob is the reorder buffer for incoming stream data. The data - received in out of order is buffered and sorted by its offset - in this object. */ - ngtcp2_rob *rob; - /* cont_offset is the largest offset of consecutive data. It is - used until the endpoint receives out-of-order data. After - that, rob is used to track the offset and data. */ - uint64_t cont_offset; - /* last_offset is the largest offset of stream data received for - this stream. */ - uint64_t last_offset; - /* max_offset is the maximum offset that remote endpoint can send - to this stream. */ - uint64_t max_offset; - /* unsent_max_offset is the maximum offset that remote endpoint - can send to this stream, and it is not notified to the remote - endpoint. unsent_max_offset >= max_offset must be hold. */ - uint64_t unsent_max_offset; - /* window is the stream-level flow control window size. */ - uint64_t window; - } rx; + struct { + /* rob is the reorder buffer for incoming stream data. The data + received in out of order is buffered and sorted by its offset + in this object. */ + ngtcp2_rob *rob; + /* cont_offset is the largest offset of consecutive data. It is + used until the endpoint receives out-of-order data. After + that, rob is used to track the offset and data. */ + uint64_t cont_offset; + /* last_offset is the largest offset of stream data received for + this stream. */ + uint64_t last_offset; + /* max_offset is the maximum offset that remote endpoint can send + to this stream. */ + uint64_t max_offset; + /* unsent_max_offset is the maximum offset that remote endpoint + can send to this stream, and it is not notified to the remote + endpoint. unsent_max_offset >= max_offset must be hold. */ + uint64_t unsent_max_offset; + /* window is the stream-level flow control window size. */ + uint64_t window; + } rx; - const ngtcp2_mem *mem; - int64_t stream_id; - void *stream_user_data; - /* flags is bit-wise OR of zero or more of NGTCP2_STRM_FLAG_*. */ - uint32_t flags; - /* app_error_code is an error code the local endpoint sent in - RST_STREAM or STOP_SENDING. */ - uint64_t app_error_code; + const ngtcp2_mem *mem; + int64_t stream_id; + void *stream_user_data; + /* flags is bit-wise OR of zero or more of NGTCP2_STRM_FLAG_*. */ + uint32_t flags; + /* app_error_code is an error code the local endpoint sent in + RESET_STREAM or STOP_SENDING, or received from a remote endpoint + in RESET_STREAM or STOP_SENDING. First application error code is + chosen and when set, NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET flag is + set in flags field. */ + uint64_t app_error_code; + }; + + ngtcp2_opl_entry oplent; + }; }; /* * ngtcp2_strm_init initializes |strm|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory */ -int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags, - uint64_t max_rx_offset, uint64_t max_tx_offset, - void *stream_user_data, const ngtcp2_mem *mem); +void ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags, + uint64_t max_rx_offset, uint64_t max_tx_offset, + void *stream_user_data, ngtcp2_objalloc *frc_objalloc, + const ngtcp2_mem *mem); /* * ngtcp2_strm_free deallocates memory allocated for |strm|. This @@ -245,6 +273,13 @@ int ngtcp2_strm_is_tx_queued(ngtcp2_strm *strm); */ int ngtcp2_strm_is_all_tx_data_acked(ngtcp2_strm *strm); +/* + * ngtcp2_strm_is_all_tx_data_fin_acked behaves like + * ngtcp2_strm_is_all_tx_data_acked, but it also requires that STREAM + * frame with fin bit set is acknowledged. + */ +int ngtcp2_strm_is_all_tx_data_fin_acked(ngtcp2_strm *strm); + /* * ngtcp2_strm_get_unacked_range_after returns the range that is not * acknowledged yet and intersects or comes after |offset|. @@ -265,4 +300,11 @@ uint64_t ngtcp2_strm_get_acked_offset(ngtcp2_strm *strm); */ int ngtcp2_strm_ack_data(ngtcp2_strm *strm, uint64_t offset, uint64_t len); +/* + * ngtcp2_strm_set_app_error_code sets |app_error_code| to |strm| and + * set NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET flag. If the flag is + * already set, this function does nothing. + */ +void ngtcp2_strm_set_app_error_code(ngtcp2_strm *strm, uint64_t app_error_code); + #endif /* NGTCP2_STRM_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c index 7a6f8afa051f20..257332e27a2abe 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c @@ -61,7 +61,7 @@ void ngtcp2_vec_del(ngtcp2_vec *vec, const ngtcp2_mem *mem) { ngtcp2_mem_free(mem, vec); } -size_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n) { +uint64_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n) { size_t i; size_t res = 0; @@ -72,6 +72,23 @@ size_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n) { return res; } +int64_t ngtcp2_vec_len_varint(const ngtcp2_vec *vec, size_t n) { + uint64_t res = 0; + size_t len; + size_t i; + + for (i = 0; i < n; ++i) { + len = vec[i].len; + if (len > NGTCP2_MAX_VARINT - res) { + return -1; + } + + res += len; + } + + return (int64_t)res; +} + ngtcp2_ssize ngtcp2_vec_split(ngtcp2_vec *src, size_t *psrccnt, ngtcp2_vec *dst, size_t *pdstcnt, size_t left, size_t maxcnt) { size_t i; @@ -198,13 +215,10 @@ size_t ngtcp2_vec_merge(ngtcp2_vec *dst, size_t *pdstcnt, ngtcp2_vec *src, return orig_left - left; } -size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t *pnwritten, - size_t dstcnt, const ngtcp2_vec *src, - size_t srccnt, size_t left) { +size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t dstcnt, + const ngtcp2_vec *src, size_t srccnt, + size_t left) { size_t i, j; - size_t len = left; - - *pnwritten = 0; for (i = 0, j = 0; left > 0 && i < srccnt && j < dstcnt;) { if (src[i].len == 0) { @@ -214,7 +228,6 @@ size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t *pnwritten, dst[j] = src[i]; if (dst[j].len > left) { dst[j].len = left; - *pnwritten = len; return j + 1; } left -= dst[j].len; @@ -222,8 +235,6 @@ size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t *pnwritten, ++j; } - *pnwritten = len - left; - return j; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h index 077820a9efed23..a39c4392fd2627 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h @@ -65,7 +65,13 @@ void ngtcp2_vec_del(ngtcp2_vec *vec, const ngtcp2_mem *mem); /* * ngtcp2_vec_len returns the sum of length in |vec| of |n| elements. */ -size_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n); +uint64_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n); + +/* + * ngtcp2_vec_len_varint is similar to ngtcp2_vec_len, but it returns + * -1 if the sum of the length exceeds NGTCP2_MAX_VARINT. + */ +int64_t ngtcp2_vec_len_varint(const ngtcp2_vec *vec, size_t n); /* * ngtcp2_vec_split splits |src| to |dst| so that the sum of the @@ -97,13 +103,13 @@ size_t ngtcp2_vec_merge(ngtcp2_vec *dst, size_t *pdstcnt, ngtcp2_vec *src, /* * ngtcp2_vec_copy_at_most copies |src| of length |srccnt| to |dst| of * length |dstcnt|. The total number of bytes which the copied - * ngtcp2_vec refers to is at most |left| and is assigned to - * |*pnwritten|. The empty elements in |src| are ignored. This - * function returns the number of elements copied. + * ngtcp2_vec refers to is at most |left|. The empty elements in + * |src| are ignored. This function returns the number of elements + * copied. */ -size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t *pnwritten, - size_t dstcnt, const ngtcp2_vec *src, - size_t srccnt, size_t left); +size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t dstcnt, + const ngtcp2_vec *src, size_t srccnt, + size_t left); /* * ngtcp2_vec_copy copies |src| of length |cnt| to |dst|. |dst| must diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_version.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_version.c index 40f3ae3f9eade4..b31162c3ebe0d7 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_version.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_version.c @@ -31,7 +31,7 @@ static ngtcp2_info version = {NGTCP2_VERSION_AGE, NGTCP2_VERSION_NUM, NGTCP2_VERSION}; -ngtcp2_info *ngtcp2_version(int least_version) { +const ngtcp2_info *ngtcp2_version(int least_version) { if (least_version > NGTCP2_VERSION_NUM) { return NULL; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.c new file mode 100644 index 00000000000000..71c816e4d3d815 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.c @@ -0,0 +1,99 @@ +/* + * ngtcp2 + * + * Copyright (c) 2021 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * Translated to C from the original C++ code + * https://quiche.googlesource.com/quiche/+/5be974e29f7e71a196e726d6e2272676d33ab77d/quic/core/congestion_control/windowed_filter.h + * with the following license: + * + * // Copyright (c) 2016 The Chromium Authors. All rights reserved. + * // Use of this source code is governed by a BSD-style license that can be + * // found in the LICENSE file. + */ +#include "ngtcp2_window_filter.h" + +#include + +void ngtcp2_window_filter_init(ngtcp2_window_filter *wf, + uint64_t window_length) { + wf->window_length = window_length; + memset(wf->estimates, 0, sizeof(wf->estimates)); +} + +void ngtcp2_window_filter_update(ngtcp2_window_filter *wf, uint64_t new_sample, + uint64_t new_time) { + if (wf->estimates[0].sample == 0 || new_sample > wf->estimates[0].sample || + new_time - wf->estimates[2].time > wf->window_length) { + ngtcp2_window_filter_reset(wf, new_sample, new_time); + return; + } + + if (new_sample > wf->estimates[1].sample) { + wf->estimates[1].sample = new_sample; + wf->estimates[1].time = new_time; + wf->estimates[2] = wf->estimates[1]; + } else if (new_sample > wf->estimates[2].sample) { + wf->estimates[2].sample = new_sample; + wf->estimates[2].time = new_time; + } + + if (new_time - wf->estimates[0].time > wf->window_length) { + wf->estimates[0] = wf->estimates[1]; + wf->estimates[1] = wf->estimates[2]; + wf->estimates[2].sample = new_sample; + wf->estimates[2].time = new_time; + + if (new_time - wf->estimates[0].time > wf->window_length) { + wf->estimates[0] = wf->estimates[1]; + wf->estimates[1] = wf->estimates[2]; + } + return; + } + + if (wf->estimates[1].sample == wf->estimates[0].sample && + new_time - wf->estimates[1].time > wf->window_length >> 2) { + wf->estimates[2].sample = new_sample; + wf->estimates[2].time = new_time; + wf->estimates[1] = wf->estimates[2]; + return; + } + + if (wf->estimates[2].sample == wf->estimates[1].sample && + new_time - wf->estimates[2].time > wf->window_length >> 1) { + wf->estimates[2].sample = new_sample; + wf->estimates[2].time = new_time; + } +} + +void ngtcp2_window_filter_reset(ngtcp2_window_filter *wf, uint64_t new_sample, + uint64_t new_time) { + wf->estimates[0].sample = new_sample; + wf->estimates[0].time = new_time; + wf->estimates[1] = wf->estimates[2] = wf->estimates[0]; +} + +uint64_t ngtcp2_window_filter_get_best(ngtcp2_window_filter *wf) { + return wf->estimates[0].sample; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.h new file mode 100644 index 00000000000000..50415f10b8c37b --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.h @@ -0,0 +1,65 @@ +/* + * ngtcp2 + * + * Copyright (c) 2021 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * Translated to C from the original C++ code + * https://quiche.googlesource.com/quiche/+/5be974e29f7e71a196e726d6e2272676d33ab77d/quic/core/congestion_control/windowed_filter.h + * with the following license: + * + * // Copyright (c) 2016 The Chromium Authors. All rights reserved. + * // Use of this source code is governed by a BSD-style license that can be + * // found in the LICENSE file. + */ +#ifndef NGTCP2_WINDOW_FILTER_H +#define NGTCP2_WINDOW_FILTER_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +typedef struct ngtcp2_window_filter_sample { + uint64_t sample; + uint64_t time; +} ngtcp2_window_filter_sample; + +typedef struct ngtcp2_window_filter { + uint64_t window_length; + ngtcp2_window_filter_sample estimates[3]; +} ngtcp2_window_filter; + +void ngtcp2_window_filter_init(ngtcp2_window_filter *wf, + uint64_t window_length); + +void ngtcp2_window_filter_update(ngtcp2_window_filter *wf, uint64_t new_sample, + uint64_t new_time); + +void ngtcp2_window_filter_reset(ngtcp2_window_filter *wf, uint64_t new_sample, + uint64_t new_time); + +uint64_t ngtcp2_window_filter_get_best(ngtcp2_window_filter *wf); + +#endif /* NGTCP2_WINDOW_FILTER_H */ diff --git a/deps/npm/docs/output/commands/npm-ls.html b/deps/npm/docs/output/commands/npm-ls.html index ffe3861d165a9b..3733fe5747ca73 100644 --- a/deps/npm/docs/output/commands/npm-ls.html +++ b/deps/npm/docs/output/commands/npm-ls.html @@ -166,7 +166,7 @@

Description

the results to only the paths to the packages named. Note that nested packages will also show the paths to the specified packages. For example, running npm ls promzard in npm's source tree will show:

-
npm@8.19.1 /path/to/npm
+
npm@8.19.2 /path/to/npm
 └─┬ init-package-json@0.0.4
   └── promzard@0.1.5
 
diff --git a/deps/npm/docs/output/commands/npm.html b/deps/npm/docs/output/commands/npm.html index 0abece728a13da..1be4ac89284580 100644 --- a/deps/npm/docs/output/commands/npm.html +++ b/deps/npm/docs/output/commands/npm.html @@ -149,7 +149,7 @@

Table of contents

Version

-

8.19.1

+

8.19.2

Description

npm is the package manager for the Node JavaScript platform. It puts modules in place so that node can find them, and manages dependency diff --git a/deps/npm/man/man1/npm-ls.1 b/deps/npm/man/man1/npm-ls.1 index c09aabc06eae3e..a728587ff9bb4c 100644 --- a/deps/npm/man/man1/npm-ls.1 +++ b/deps/npm/man/man1/npm-ls.1 @@ -26,7 +26,7 @@ example, running \fBnpm ls promzard\fP in npm's source tree will show: .P .RS 2 .nf -npm@8\.19\.1 /path/to/npm +npm@8\.19\.2 /path/to/npm └─┬ init\-package\-json@0\.0\.4 └── promzard@0\.1\.5 .fi diff --git a/deps/npm/man/man1/npm.1 b/deps/npm/man/man1/npm.1 index 693ff495fbb5f3..1f9f49691b1d5f 100644 --- a/deps/npm/man/man1/npm.1 +++ b/deps/npm/man/man1/npm.1 @@ -4,7 +4,7 @@ .SS Synopsis .SS Version .P -8\.19\.1 +8\.19\.2 .SS Description .P npm is the package manager for the Node JavaScript platform\. It puts diff --git a/deps/npm/node_modules/@npmcli/arborist/lib/add-rm-pkg-deps.js b/deps/npm/node_modules/@npmcli/arborist/lib/add-rm-pkg-deps.js index 7b43c38e2492b2..59d5e32547c29e 100644 --- a/deps/npm/node_modules/@npmcli/arborist/lib/add-rm-pkg-deps.js +++ b/deps/npm/node_modules/@npmcli/arborist/lib/add-rm-pkg-deps.js @@ -5,23 +5,24 @@ const localeCompare = require('@isaacs/string-locale-compare')('en') const add = ({ pkg, add, saveBundle, saveType }) => { for (const { name, rawSpec } of add) { + let addSaveType = saveType // if the user does not give us a type, we infer which type(s) // to keep based on the same order of priority we do when // building the tree as defined in the _loadDeps method of // the node class. - if (!saveType) { - saveType = inferSaveType(pkg, name) + if (!addSaveType) { + addSaveType = inferSaveType(pkg, name) } - if (saveType === 'prod') { + if (addSaveType === 'prod') { // a production dependency can only exist as production (rpj ensures it // doesn't coexist w/ optional) deleteSubKey(pkg, 'devDependencies', name, 'dependencies') deleteSubKey(pkg, 'peerDependencies', name, 'dependencies') - } else if (saveType === 'dev') { + } else if (addSaveType === 'dev') { // a dev dependency may co-exist as peer, or optional, but not production deleteSubKey(pkg, 'dependencies', name, 'devDependencies') - } else if (saveType === 'optional') { + } else if (addSaveType === 'optional') { // an optional dependency may co-exist as dev (rpj ensures it doesn't // coexist w/ prod) deleteSubKey(pkg, 'peerDependencies', name, 'optionalDependencies') @@ -31,23 +32,23 @@ const add = ({ pkg, add, saveBundle, saveType }) => { deleteSubKey(pkg, 'optionalDependencies', name, 'peerDependencies') } - const depType = saveTypeMap.get(saveType) + const depType = saveTypeMap.get(addSaveType) pkg[depType] = pkg[depType] || {} if (rawSpec !== '' || pkg[depType][name] === undefined) { pkg[depType][name] = rawSpec || '*' } - if (saveType === 'optional') { + if (addSaveType === 'optional') { // Affordance for previous npm versions that require this behaviour pkg.dependencies = pkg.dependencies || {} pkg.dependencies[name] = pkg.optionalDependencies[name] } - if (saveType === 'peer' || saveType === 'peerOptional') { + if (addSaveType === 'peer' || addSaveType === 'peerOptional') { const pdm = pkg.peerDependenciesMeta || {} - if (saveType === 'peer' && pdm[name] && pdm[name].optional) { + if (addSaveType === 'peer' && pdm[name] && pdm[name].optional) { pdm[name].optional = false - } else if (saveType === 'peerOptional') { + } else if (addSaveType === 'peerOptional') { pdm[name] = pdm[name] || {} pdm[name].optional = true pkg.peerDependenciesMeta = pdm @@ -59,7 +60,7 @@ const add = ({ pkg, add, saveBundle, saveType }) => { } } - if (saveBundle && saveType !== 'peer' && saveType !== 'peerOptional') { + if (saveBundle && addSaveType !== 'peer' && addSaveType !== 'peerOptional') { // keep it sorted, keep it unique const bd = new Set(pkg.bundleDependencies || []) bd.add(name) diff --git a/deps/npm/node_modules/@npmcli/arborist/package.json b/deps/npm/node_modules/@npmcli/arborist/package.json index b7cd92ba4cb11c..7f1b266305ef29 100644 --- a/deps/npm/node_modules/@npmcli/arborist/package.json +++ b/deps/npm/node_modules/@npmcli/arborist/package.json @@ -1,6 +1,6 @@ { "name": "@npmcli/arborist", - "version": "5.6.1", + "version": "5.6.2", "description": "Manage node_modules trees", "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", @@ -42,7 +42,7 @@ }, "devDependencies": { "@npmcli/eslint-config": "^3.1.0", - "@npmcli/template-oss": "3.8.1", + "@npmcli/template-oss": "4.1.1", "benchmark": "^2.1.4", "chalk": "^4.1.0", "minify-registry-metadata": "^2.1.0", @@ -100,6 +100,6 @@ }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "3.8.1" + "version": "4.1.1" } } diff --git a/deps/npm/node_modules/libnpmaccess/package.json b/deps/npm/node_modules/libnpmaccess/package.json index 94c688e07e58ef..01092346e71f61 100644 --- a/deps/npm/node_modules/libnpmaccess/package.json +++ b/deps/npm/node_modules/libnpmaccess/package.json @@ -17,7 +17,7 @@ }, "devDependencies": { "@npmcli/eslint-config": "^3.1.0", - "@npmcli/template-oss": "3.8.1", + "@npmcli/template-oss": "4.1.1", "nock": "^13.2.4", "tap": "^16.0.1" }, @@ -43,6 +43,6 @@ ], "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "3.8.1" + "version": "4.1.1" } } diff --git a/deps/npm/node_modules/libnpmdiff/package.json b/deps/npm/node_modules/libnpmdiff/package.json index 9f61ee8f55cc6c..b1da1aa1bb9699 100644 --- a/deps/npm/node_modules/libnpmdiff/package.json +++ b/deps/npm/node_modules/libnpmdiff/package.json @@ -43,7 +43,7 @@ }, "devDependencies": { "@npmcli/eslint-config": "^3.1.0", - "@npmcli/template-oss": "3.8.1", + "@npmcli/template-oss": "4.1.1", "tap": "^16.0.1" }, "dependencies": { @@ -58,6 +58,6 @@ }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "3.8.1" + "version": "4.1.1" } } diff --git a/deps/npm/node_modules/libnpmexec/package.json b/deps/npm/node_modules/libnpmexec/package.json index 7f7b8eac456b99..7542bce982e427 100644 --- a/deps/npm/node_modules/libnpmexec/package.json +++ b/deps/npm/node_modules/libnpmexec/package.json @@ -1,6 +1,6 @@ { "name": "libnpmexec", - "version": "4.0.12", + "version": "4.0.13", "files": [ "bin/", "lib/" @@ -47,14 +47,14 @@ }, "devDependencies": { "@npmcli/eslint-config": "^3.1.0", - "@npmcli/template-oss": "3.8.1", + "@npmcli/template-oss": "4.1.1", "bin-links": "^3.0.3", "minify-registry-metadata": "^2.2.0", "mkdirp": "^1.0.4", "tap": "^16.0.1" }, "dependencies": { - "@npmcli/arborist": "^5.6.1", + "@npmcli/arborist": "^5.6.2", "@npmcli/ci-detect": "^2.0.0", "@npmcli/fs": "^2.1.1", "@npmcli/run-script": "^4.2.0", @@ -71,6 +71,6 @@ }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "3.8.1" + "version": "4.1.1" } } diff --git a/deps/npm/node_modules/libnpmfund/package.json b/deps/npm/node_modules/libnpmfund/package.json index 68db1d647326a2..562a973e2dec1f 100644 --- a/deps/npm/node_modules/libnpmfund/package.json +++ b/deps/npm/node_modules/libnpmfund/package.json @@ -1,6 +1,6 @@ { "name": "libnpmfund", - "version": "3.0.3", + "version": "3.0.4", "main": "lib/index.js", "files": [ "bin/", @@ -42,17 +42,17 @@ }, "devDependencies": { "@npmcli/eslint-config": "^3.1.0", - "@npmcli/template-oss": "3.8.1", + "@npmcli/template-oss": "4.1.1", "tap": "^16.0.1" }, "dependencies": { - "@npmcli/arborist": "^5.6.1" + "@npmcli/arborist": "^5.6.2" }, "engines": { "node": "^12.13.0 || ^14.15.0 || >=16.0.0" }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "3.8.1" + "version": "4.1.1" } } diff --git a/deps/npm/node_modules/libnpmhook/package.json b/deps/npm/node_modules/libnpmhook/package.json index 446170777ff260..48dac38b2a8b3d 100644 --- a/deps/npm/node_modules/libnpmhook/package.json +++ b/deps/npm/node_modules/libnpmhook/package.json @@ -37,7 +37,7 @@ }, "devDependencies": { "@npmcli/eslint-config": "^3.1.0", - "@npmcli/template-oss": "3.8.1", + "@npmcli/template-oss": "4.1.1", "nock": "^13.2.4", "tap": "^16.0.1" }, @@ -46,6 +46,6 @@ }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "3.8.1" + "version": "4.1.1" } } diff --git a/deps/npm/node_modules/libnpmorg/package.json b/deps/npm/node_modules/libnpmorg/package.json index b5ecf40cbf3788..3374aa34288eae 100644 --- a/deps/npm/node_modules/libnpmorg/package.json +++ b/deps/npm/node_modules/libnpmorg/package.json @@ -28,7 +28,7 @@ ], "devDependencies": { "@npmcli/eslint-config": "^3.1.0", - "@npmcli/template-oss": "3.8.1", + "@npmcli/template-oss": "4.1.1", "minipass": "^3.1.1", "nock": "^13.2.4", "tap": "^16.0.1" @@ -49,6 +49,6 @@ }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "3.8.1" + "version": "4.1.1" } } diff --git a/deps/npm/node_modules/libnpmpack/package.json b/deps/npm/node_modules/libnpmpack/package.json index e808c7b8e3b6bb..701e00eb1ad9cd 100644 --- a/deps/npm/node_modules/libnpmpack/package.json +++ b/deps/npm/node_modules/libnpmpack/package.json @@ -23,7 +23,7 @@ }, "devDependencies": { "@npmcli/eslint-config": "^3.1.0", - "@npmcli/template-oss": "3.8.1", + "@npmcli/template-oss": "4.1.1", "nock": "^13.0.7", "tap": "^16.0.1" }, @@ -44,6 +44,6 @@ }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "3.8.1" + "version": "4.1.1" } } diff --git a/deps/npm/node_modules/libnpmpublish/package.json b/deps/npm/node_modules/libnpmpublish/package.json index dd88dd5460dbe5..dda1477201b5b7 100644 --- a/deps/npm/node_modules/libnpmpublish/package.json +++ b/deps/npm/node_modules/libnpmpublish/package.json @@ -25,7 +25,7 @@ }, "devDependencies": { "@npmcli/eslint-config": "^3.1.0", - "@npmcli/template-oss": "3.8.1", + "@npmcli/template-oss": "4.1.1", "libnpmpack": "^4.1.3", "lodash.clonedeep": "^4.5.0", "nock": "^13.2.4", @@ -50,6 +50,6 @@ }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "3.8.1" + "version": "4.1.1" } } diff --git a/deps/npm/node_modules/libnpmsearch/package.json b/deps/npm/node_modules/libnpmsearch/package.json index f9b8cdded0c104..9cd26196d1a94c 100644 --- a/deps/npm/node_modules/libnpmsearch/package.json +++ b/deps/npm/node_modules/libnpmsearch/package.json @@ -26,7 +26,7 @@ }, "devDependencies": { "@npmcli/eslint-config": "^3.1.0", - "@npmcli/template-oss": "3.8.1", + "@npmcli/template-oss": "4.1.1", "nock": "^13.2.4", "tap": "^16.0.1" }, @@ -45,6 +45,6 @@ }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "3.8.1" + "version": "4.1.1" } } diff --git a/deps/npm/node_modules/libnpmteam/package.json b/deps/npm/node_modules/libnpmteam/package.json index 2d5a91b5e57c7e..be4fd45527636b 100644 --- a/deps/npm/node_modules/libnpmteam/package.json +++ b/deps/npm/node_modules/libnpmteam/package.json @@ -16,7 +16,7 @@ }, "devDependencies": { "@npmcli/eslint-config": "^3.1.0", - "@npmcli/template-oss": "3.8.1", + "@npmcli/template-oss": "4.1.1", "nock": "^13.2.4", "tap": "^16.0.1" }, @@ -39,6 +39,6 @@ }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "3.8.1" + "version": "4.1.1" } } diff --git a/deps/npm/node_modules/libnpmversion/package.json b/deps/npm/node_modules/libnpmversion/package.json index ff6415afc862bc..aab8131607ec07 100644 --- a/deps/npm/node_modules/libnpmversion/package.json +++ b/deps/npm/node_modules/libnpmversion/package.json @@ -28,7 +28,7 @@ }, "devDependencies": { "@npmcli/eslint-config": "^3.1.0", - "@npmcli/template-oss": "3.8.1", + "@npmcli/template-oss": "4.1.1", "require-inject": "^1.4.4", "tap": "^16.0.1" }, @@ -44,6 +44,6 @@ }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "3.8.1" + "version": "4.1.1" } } diff --git a/deps/npm/package.json b/deps/npm/package.json index 458952142ff2b2..573e5d06e73a77 100644 --- a/deps/npm/package.json +++ b/deps/npm/package.json @@ -1,5 +1,5 @@ { - "version": "8.19.1", + "version": "8.19.2", "name": "npm", "description": "a package manager for JavaScript", "workspaces": [ @@ -56,7 +56,7 @@ }, "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^5.6.1", + "@npmcli/arborist": "^5.6.2", "@npmcli/ci-detect": "^2.0.0", "@npmcli/config": "^4.2.1", "@npmcli/fs": "^2.1.0", @@ -81,8 +81,8 @@ "json-parse-even-better-errors": "^2.3.1", "libnpmaccess": "^6.0.4", "libnpmdiff": "^4.0.5", - "libnpmexec": "^4.0.12", - "libnpmfund": "^3.0.3", + "libnpmexec": "^4.0.13", + "libnpmfund": "^3.0.4", "libnpmhook": "^8.0.4", "libnpmorg": "^4.0.4", "libnpmpack": "^4.1.3", @@ -206,7 +206,7 @@ "devDependencies": { "@npmcli/eslint-config": "^3.1.0", "@npmcli/promise-spawn": "^3.0.0", - "@npmcli/template-oss": "3.8.1", + "@npmcli/template-oss": "4.1.1", "fs-minipass": "^2.1.0", "licensee": "^8.2.0", "minimatch": "^5.1.0", @@ -251,8 +251,19 @@ "templateOSS": { "rootRepo": false, "rootModule": false, - "version": "3.8.1", - "releaseTest": "release.yml" + "version": "4.1.1", + "releaseTest": "release.yml", + "ciVersions": [ + "12.13.0", + "12.x", + "14.15.0", + "14.x", + "16.0.0", + "16.x" + ], + "releaseBranches": [ + "v8" + ] }, "license": "Artistic-2.0", "engines": { diff --git a/deps/uvwasi/include/uvwasi.h b/deps/uvwasi/include/uvwasi.h index 28d38568612607..b45f80b1908ee4 100644 --- a/deps/uvwasi/include/uvwasi.h +++ b/deps/uvwasi/include/uvwasi.h @@ -10,7 +10,7 @@ extern "C" { #define UVWASI_VERSION_MAJOR 0 #define UVWASI_VERSION_MINOR 0 -#define UVWASI_VERSION_PATCH 12 +#define UVWASI_VERSION_PATCH 13 #define UVWASI_VERSION_HEX ((UVWASI_VERSION_MAJOR << 16) | \ (UVWASI_VERSION_MINOR << 8) | \ (UVWASI_VERSION_PATCH)) diff --git a/deps/uvwasi/src/uvwasi.c b/deps/uvwasi/src/uvwasi.c index 25ce4ab846ef86..18885ee25961a6 100644 --- a/deps/uvwasi/src/uvwasi.c +++ b/deps/uvwasi/src/uvwasi.c @@ -1384,8 +1384,14 @@ uvwasi_errno_t uvwasi_fd_readdir(uvwasi_t* uvwasi, } /* Write dirent to the buffer if it will fit. */ - if (UVWASI_SERDES_SIZE_dirent_t + *bufused > buf_len) + if (UVWASI_SERDES_SIZE_dirent_t + *bufused > buf_len) { + /* If there are more entries to be written to the buffer we set + * bufused, which is the return value, to the length of the buffer + * which indicates that there are more entries to be read. + */ + *bufused = buf_len; break; + } uvwasi_serdes_write_dirent_t(buf, *bufused, &dirent); *bufused += UVWASI_SERDES_SIZE_dirent_t; diff --git a/doc/abi_version_registry.json b/doc/abi_version_registry.json index b2778a83bf954c..64577159c7ff9c 100644 --- a/doc/abi_version_registry.json +++ b/doc/abi_version_registry.json @@ -1,5 +1,6 @@ { "NODE_MODULE_VERSION": [ + { "modules": 110,"runtime": "electron", "variant": "electron", "versions": "22" }, { "modules": 109,"runtime": "electron", "variant": "electron", "versions": "21" }, { "modules": 108,"runtime": "node", "variant": "v8_10.1", "versions": "18.0.0" }, { "modules": 107,"runtime": "electron", "variant": "electron", "versions": "20" }, diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 385dff2b808ded..5257083b9595bf 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -1175,25 +1175,30 @@ or `setPrivateKey()` methods. ```mjs const { createDiffieHellmanGroup } = await import('node:crypto'); -const dh = createDiffieHellmanGroup('modp1'); +const dh = createDiffieHellmanGroup('modp16'); ``` ```cjs const { createDiffieHellmanGroup } = require('node:crypto'); -const dh = createDiffieHellmanGroup('modp1'); +const dh = createDiffieHellmanGroup('modp16'); ``` The following groups are supported: -* `'modp1'` (768 bits, [RFC 2409][] Section 6.1) -* `'modp2'` (1024 bits, [RFC 2409][] Section 6.2) -* `'modp5'` (1536 bits, [RFC 3526][] Section 2) * `'modp14'` (2048 bits, [RFC 3526][] Section 3) * `'modp15'` (3072 bits, [RFC 3526][] Section 4) * `'modp16'` (4096 bits, [RFC 3526][] Section 5) * `'modp17'` (6144 bits, [RFC 3526][] Section 6) * `'modp18'` (8192 bits, [RFC 3526][] Section 7) +The following groups are still supported but deprecated (see [Caveats][]): + +* `'modp1'` (768 bits, [RFC 2409][] Section 6.1) +* `'modp2'` (1024 bits, [RFC 2409][] Section 6.2) +* `'modp5'` (1536 bits, [RFC 3526][] Section 2) + +These deprecated groups might be removed in future versions of Node.js. + ## Class: `ECDH` -Type: Runtime +Type: End-of-Life The `process.config` property provides access to Node.js compile-time settings. However, the property is mutable and therefore subject to tampering. The ability @@ -3057,7 +3060,9 @@ const w = new Writable({ @@ -3090,7 +3095,9 @@ changes: - version: v18.0.0 pr-url: https://github.com/nodejs/node/pull/41896 description: Runtime deprecation. - - version: v17.6.0 + - version: + - v17.6.0 + - v16.15.0 pr-url: https://github.com/nodejs/node/pull/41872 description: Documentation-only deprecation. --> @@ -3104,7 +3111,9 @@ which diminished its usefulness. @@ -3122,15 +3131,20 @@ resources and not the actual references. -Type: Runtime +Type: End-of-Life Implicit coercion of objects with own `toString` property, passed as second parameter in [`fs.write()`][], [`fs.writeFile()`][], [`fs.appendFile()`][], @@ -3141,7 +3155,9 @@ Convert them to primitive strings. @@ -3155,19 +3171,27 @@ Use [`diagnostics_channel.subscribe(name, onMessage)`][] or [`diagnostics_channel.unsubscribe(name, onMessage)`][] which does the same thing instead. -### DEP0164: `process.exit([code])` coercion to integer +### DEP0164: `process.exit(code)`, `process.exitCode` coercion to integer Type: Documentation-only -`code` values other than `undefined`, `null`, integer numbers and integer -strings (e.g., '1') are deprecated as parameter in [`process.exit()`][]. +Values other than `undefined`, `null`, integer numbers, and integer strings +(e.g., `'1'`) are deprecated as value for the `code` parameter in +[`process.exit()`][] and as value to assign to [`process.exitCode`][]. ### DEP0165: `--trace-atomics-wait` @@ -3182,11 +3206,72 @@ Type: Documentation-only The [`--trace-atomics-wait`][] flag is deprecated. +### DEP0166: Double slashes in imports and exports targets + + + +Type: Runtime + +Package imports and exports targets mapping into paths including a double slash +(of _"/"_ or _"\\"_) are deprecated and will fail with a resolution validation +error in a future release. This same deprecation also applies to pattern matches +starting or ending in a slash. + +### DEP0167: Weak `DiffieHellmanGroup` instances (`modp1`, `modp2`, `modp5`) + + + +Type: Documentation-only + +The well-known MODP groups `modp1`, `modp2`, and `modp5` are deprecated because +they are not secure against practical attacks. See [RFC 8247 Section 2.4][] for +details. + +These groups might be removed in future versions of Node.js. Applications that +rely on these groups should evaluate using stronger MODP groups instead. + +### DEP0168: Unhandled exception in Node-API callbacks + + + +Type: Runtime. + +The implicit suppression of uncaught exceptions in Node-API callbacks is now +deprecated. + +Set the flag [`--force-node-api-uncaught-exceptions-policy`][] to force Node.js +to emit an [`'uncaughtException'`][] event if the exception is not handled in +Node-API callbacks. + [Legacy URL API]: url.md#legacy-url-api [NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf [RFC 6066]: https://tools.ietf.org/html/rfc6066#section-3 +[RFC 8247 Section 2.4]: https://www.rfc-editor.org/rfc/rfc8247#section-2.4 [WHATWG URL API]: url.md#the-whatwg-url-api [`"exports"` or `"main"` entry]: packages.md#main-entry-point-export +[`'uncaughtException'`]: process.md#event-uncaughtexception +[`--force-node-api-uncaught-exceptions-policy`]: cli.md#--force-node-api-uncaught-exceptions-policy [`--pending-deprecation`]: cli.md#--pending-deprecation [`--throw-deprecation`]: cli.md#--throw-deprecation [`--trace-atomics-wait`]: cli.md#--trace-atomics-wait @@ -3262,6 +3347,7 @@ The [`--trace-atomics-wait`][] flag is deprecated. [`os.tmpdir()`]: os.md#ostmpdir [`process.env`]: process.md#processenv [`process.exit()`]: process.md#processexitcode +[`process.exitCode`]: process.md#processexitcode_1 [`process.getActiveResourcesInfo()`]: process.md#processgetactiveresourcesinfo [`process.mainModule`]: process.md#processmainmodule [`punycode`]: punycode.md diff --git a/doc/api/errors.md b/doc/api/errors.md index 4c544ef734776e..2c2618b42b6030 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -1325,6 +1325,12 @@ When using [`fs.cp()`][], `src` or `dest` pointed to an invalid path. +### `ERR_HTTP_CONTENT_LENGTH_MISMATCH` + +Response body size doesn't match with the specified content-length header value. + + + ### `ERR_FS_CP_FIFO_PIPE` -This function is not available in [worker threads][]. +Deactivate the inspector. Blocks until there are no active connections. ## `inspector.console` @@ -262,4 +268,3 @@ session.post('HeapProfiler.takeHeapSnapshot', null, (err, r) => { [`'Debugger.paused'`]: https://chromedevtools.github.io/devtools-protocol/v8/Debugger#event-paused [`session.connect()`]: #sessionconnect [security warning]: cli.md#warning-binding-inspector-to-a-public-ipport-combination-is-insecure -[worker threads]: worker_threads.md diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 352daceb218d17..056634cbe6aece 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -4731,8 +4731,8 @@ napi_status napi_get_cb_info(napi_env env, provided than claimed, the rest of `argv` is filled with `napi_value` values that represent `undefined`. `argv` can optionally be ignored by passing `NULL`. -* `[out] this`: Receives the JavaScript `this` argument for the call. `this` - can optionally be ignored by passing `NULL`. +* `[out] thisArg`: Receives the JavaScript `this` argument for the call. + `thisArg` can optionally be ignored by passing `NULL`. * `[out] data`: Receives the data pointer for the callback. `data` can optionally be ignored by passing `NULL`. diff --git a/doc/api/permissions.md b/doc/api/permissions.md new file mode 100644 index 00000000000000..a4be1a7c399b4e --- /dev/null +++ b/doc/api/permissions.md @@ -0,0 +1,446 @@ +# Permissions + +Permissions can be used to control what system resources the +Node.js process has access to or what actions the process can take +with those resources. Permissions can also control what modules can +be accessed by other modules. + +* [Module-based permissions](#module-based-permissions) control which files + or URLs are available to other modules during application execution. + This can be used to control what modules can be accessed by third-party + dependencies, for example. + +If you find a potential security vulnerability, please refer to our +[Security Policy][]. + +## Module-based permissions + +### Policies + + + + + +> Stability: 1 - Experimental + + + +Node.js contains experimental support for creating policies on loading code. + +Policies are a security feature intended to allow guarantees +about what code Node.js is able to load. The use of policies assumes +safe practices for the policy files such as ensuring that policy +files cannot be overwritten by the Node.js application by using +file permissions. + +A best practice would be to ensure that the policy manifest is read-only for +the running Node.js application and that the file cannot be changed +by the running Node.js application in any way. A typical setup would be to +create the policy file as a different user id than the one running Node.js +and granting read permissions to the user id running Node.js. + +#### Enabling + + + +The `--experimental-policy` flag can be used to enable features for policies +when loading modules. + +Once this has been set, all modules must conform to a policy manifest file +passed to the flag: + +```bash +node --experimental-policy=policy.json app.js +``` + +The policy manifest will be used to enforce constraints on code loaded by +Node.js. + +To mitigate tampering with policy files on disk, an integrity for +the policy file itself may be provided via `--policy-integrity`. +This allows running `node` and asserting the policy file contents +even if the file is changed on disk. + +```bash +node --experimental-policy=policy.json --policy-integrity="sha384-SggXRQHwCG8g+DktYYzxkXRIkTiEYWBHqev0xnpCxYlqMBufKZHAHQM3/boDaI/0" app.js +``` + +#### Features + +##### Error behavior + +When a policy check fails, Node.js by default will throw an error. +It is possible to change the error behavior to one of a few possibilities +by defining an "onerror" field in a policy manifest. The following values are +available to change the behavior: + +* `"exit"`: will exit the process immediately. + No cleanup code will be allowed to run. +* `"log"`: will log the error at the site of the failure. +* `"throw"`: will throw a JS error at the site of the failure. This is the + default. + +```json +{ + "onerror": "log", + "resources": { + "./app/checked.js": { + "integrity": "sha384-SggXRQHwCG8g+DktYYzxkXRIkTiEYWBHqev0xnpCxYlqMBufKZHAHQM3/boDaI/0" + } + } +} +``` + +##### Integrity checks + +Policy files must use integrity checks with Subresource Integrity strings +compatible with the browser +[integrity attribute](https://www.w3.org/TR/SRI/#the-integrity-attribute) +associated with absolute URLs. + +When using `require()` or `import` all resources involved in loading are checked +for integrity if a policy manifest has been specified. If a resource does not +match the integrity listed in the manifest, an error will be thrown. + +An example policy file that would allow loading a file `checked.js`: + +```json +{ + "resources": { + "./app/checked.js": { + "integrity": "sha384-SggXRQHwCG8g+DktYYzxkXRIkTiEYWBHqev0xnpCxYlqMBufKZHAHQM3/boDaI/0" + } + } +} +``` + +Each resource listed in the policy manifest can be of one the following +formats to determine its location: + +1. A [relative-URL string][] to a resource from the manifest such as `./resource.js`, `../resource.js`, or `/resource.js`. +2. A complete URL string to a resource such as `file:///resource.js`. + +When loading resources the entire URL must match including search parameters +and hash fragment. `./a.js?b` will not be used when attempting to load +`./a.js` and vice versa. + +To generate integrity strings, a script such as +`node -e 'process.stdout.write("sha256-");process.stdin.pipe(crypto.createHash("sha256").setEncoding("base64")).pipe(process.stdout)' < FILE` +can be used. + +Integrity can be specified as the boolean value `true` to accept any +body for the resource which can be useful for local development. It is not +recommended in production since it would allow unexpected alteration of +resources to be considered valid. + +##### Dependency redirection + +An application may need to ship patched versions of modules or to prevent +modules from allowing all modules access to all other modules. Redirection +can be used by intercepting attempts to load the modules wishing to be +replaced. + +```json +{ + "resources": { + "./app/checked.js": { + "dependencies": { + "fs": true, + "os": "./app/node_modules/alt-os", + "http": { "import": true } + } + } + } +} +``` + +The dependencies are keyed by the requested specifier string and have values +of either `true`, `null`, a string pointing to a module to be resolved, +or a conditions object. + +The specifier string does not perform any searching and must match exactly what +is provided to the `require()` or `import` except for a canonicalization step. +Therefore, multiple specifiers may be needed in the policy if it uses multiple +different strings to point to the same module (such as excluding the extension). + +Specifier strings are canonicalized but not resolved prior to be used for +matching in order to have some compatibility with import maps, for example if a +resource `file:///C:/app/server.js` was given the following redirection from a +policy located at `file:///C:/app/policy.json`: + +```json +{ + "resources": { + "file:///C:/app/utils.js": { + "dependencies": { + "./utils.js": "./utils-v2.js" + } + } + } +} +``` + +Any specifier used to load `file:///C:/app/utils.js` would then be intercepted +and redirected to `file:///C:/app/utils-v2.js` instead regardless of using an +absolute or relative specifier. However, if a specifier that is not an absolute +or relative URL string is used, it would not be intercepted. So, if an import +such as `import('#utils')` was used, it would not be intercepted. + +If the value of the redirection is `true`, a "dependencies" field at the top of +the policy file will be used. If that field at the top of the policy file is +`true` the default node searching algorithms are used to find the module. + +If the value of the redirection is a string, it is resolved relative to +the manifest and then immediately used without searching. + +Any specifier string for which resolution is attempted and that is not listed in +the dependencies results in an error according to the policy. + +Redirection does not prevent access to APIs through means such as direct access +to `require.cache` or through `module.constructor` which allow access to +loading modules. Policy redirection only affects specifiers to `require()` and +`import`. Other means, such as to prevent undesired access to APIs through +variables, are necessary to lock down that path of loading modules. + +A boolean value of `true` for the dependencies map can be specified to allow a +module to load any specifier without redirection. This can be useful for local +development and may have some valid usage in production, but should be used +only with care after auditing a module to ensure its behavior is valid. + +Similar to `"exports"` in `package.json`, dependencies can also be specified to +be objects containing conditions which branch how dependencies are loaded. In +the preceding example, `"http"` is allowed when the `"import"` condition is +part of loading it. + +A value of `null` for the resolved value causes the resolution to fail. This +can be used to ensure some kinds of dynamic access are explicitly prevented. + +Unknown values for the resolved module location cause failures but are +not guaranteed to be forward compatible. + +##### Example: Patched dependency + +Redirected dependencies can provide attenuated or modified functionality as fits +the application. For example, log data about timing of function durations by +wrapping the original: + +```js +const original = require('fn'); +module.exports = function fn(...args) { + console.time(); + try { + return new.target ? + Reflect.construct(original, args) : + Reflect.apply(original, this, args); + } finally { + console.timeEnd(); + } +}; +``` + +#### Scopes + +Use the `"scopes"` field of a manifest to set configuration for many resources +at once. The `"scopes"` field works by matching resources by their segments. +If a scope or resource includes `"cascade": true`, unknown specifiers will +be searched for in their containing scope. The containing scope for cascading +is found by recursively reducing the resource URL by removing segments for +[special schemes][], keeping trailing `"/"` suffixes, and removing the query and +hash fragment. This leads to the eventual reduction of the URL to its origin. +If the URL is non-special the scope will be located by the URL's origin. If no +scope is found for the origin or in the case of opaque origins, a protocol +string can be used as a scope. If no scope is found for the URL's protocol, a +final empty string `""` scope will be used. + +Note, `blob:` URLs adopt their origin from the path they contain, and so a scope +of `"blob:https://nodejs.org"` will have no effect since no URL can have an +origin of `blob:https://nodejs.org`; URLs starting with +`blob:https://nodejs.org/` will use `https://nodejs.org` for its origin and +thus `https:` for its protocol scope. For opaque origin `blob:` URLs they will +have `blob:` for their protocol scope since they do not adopt origins. + +##### Example + +```json +{ + "scopes": { + "file:///C:/app/": {}, + "file:": {}, + "": {} + } +} +``` + +Given a file located at `file:///C:/app/bin/main.js`, the following scopes would +be checked in order: + +1. `"file:///C:/app/bin/"` + +This determines the policy for all file based resources within +`"file:///C:/app/bin/"`. This is not in the `"scopes"` field of the policy and +would be skipped. Adding this scope to the policy would cause it to be used +prior to the `"file:///C:/app/"` scope. + +2. `"file:///C:/app/"` + +This determines the policy for all file based resources within +`"file:///C:/app/"`. This is in the `"scopes"` field of the policy and it would +determine the policy for the resource at `file:///C:/app/bin/main.js`. If the +scope has `"cascade": true`, any unsatisfied queries about the resource would +delegate to the next relevant scope for `file:///C:/app/bin/main.js`, `"file:"`. + +3. `"file:///C:/"` + +This determines the policy for all file based resources within `"file:///C:/"`. +This is not in the `"scopes"` field of the policy and would be skipped. It would +not be used for `file:///C:/app/bin/main.js` unless `"file:///"` is set to +cascade or is not in the `"scopes"` of the policy. + +4. `"file:///"` + +This determines the policy for all file based resources on the `localhost`. This +is not in the `"scopes"` field of the policy and would be skipped. It would not +be used for `file:///C:/app/bin/main.js` unless `"file:///"` is set to cascade +or is not in the `"scopes"` of the policy. + +5. `"file:"` + +This determines the policy for all file based resources. It would not be used +for `file:///C:/app/bin/main.js` unless `"file:///"` is set to cascade or is not +in the `"scopes"` of the policy. + +6. `""` + +This determines the policy for all resources. It would not be used for +`file:///C:/app/bin/main.js` unless `"file:"` is set to cascade. + +##### Integrity using scopes + +Setting an integrity to `true` on a scope will set the integrity for any +resource not found in the manifest to `true`. + +Setting an integrity to `null` on a scope will set the integrity for any +resource not found in the manifest to fail matching. + +Not including an integrity is the same as setting the integrity to `null`. + +`"cascade"` for integrity checks will be ignored if `"integrity"` is explicitly +set. + +The following example allows loading any file: + +```json +{ + "scopes": { + "file:": { + "integrity": true + } + } +} +``` + +##### Dependency redirection using scopes + +The following example, would allow access to `fs` for all resources within +`./app/`: + +```json +{ + "resources": { + "./app/checked.js": { + "cascade": true, + "integrity": true + } + }, + "scopes": { + "./app/": { + "dependencies": { + "fs": true + } + } + } +} +``` + +The following example, would allow access to `fs` for all `data:` resources: + +```json +{ + "resources": { + "data:text/javascript,import('node:fs');": { + "cascade": true, + "integrity": true + } + }, + "scopes": { + "data:": { + "dependencies": { + "fs": true + } + } + } +} +``` + +##### Example: [import maps][] emulation + +Given an import map: + +```json +{ + "imports": { + "react": "./app/node_modules/react/index.js" + }, + "scopes": { + "./ssr/": { + "react": "./app/node_modules/server-side-react/index.js" + } + } +} +``` + +```json +{ + "dependencies": true, + "scopes": { + "": { + "cascade": true, + "dependencies": { + "react": "./app/node_modules/react/index.js" + } + }, + "./ssr/": { + "cascade": true, + "dependencies": { + "react": "./app/node_modules/server-side-react/index.js" + } + } + } +} +``` + +Import maps assume you can get any resource by default. This means +`"dependencies"` at the top level of the policy should be set to `true`. +Policies require this to be opt-in since it enables all resources of the +application cross linkage which doesn't make sense for many scenarios. They also +assume any given scope has access to any scope above its allowed dependencies; +all scopes emulating import maps must set `"cascade": true`. + +Import maps only have a single top level scope for their "imports". So for +emulating `"imports"` use the `""` scope. For emulating `"scopes"` use the +`"scopes"` in a similar manner to how `"scopes"` works in import maps. + +Caveats: Policies do not use string matching for various finding of scope. They +do URL traversals. This means things like `blob:` and `data:` URLs might not be +entirely interoperable between the two systems. For example import maps can +partially match a `data:` or `blob:` URL by partitioning the URL on a `/` +character, policies intentionally cannot. For `blob:` URLs import map scopes do +not adopt the origin of the `blob:` URL. + +Additionally, import maps only work on `import` so it may be desirable to add a +`"import"` condition to all dependency mappings. + +[Security Policy]: https://github.com/nodejs/node/blob/main/SECURITY.md +[import maps]: https://url.spec.whatwg.org/#relative-url-with-fragment-string +[relative-url string]: https://url.spec.whatwg.org/#relative-url-with-fragment-string +[special schemes]: https://url.spec.whatwg.org/#special-scheme diff --git a/doc/api/policy.md b/doc/api/policy.md index 233d6c94640790..cf2ff88b7ffdaf 100644 --- a/doc/api/policy.md +++ b/doc/api/policy.md @@ -6,423 +6,6 @@ > Stability: 1 - Experimental - +The former Policies documentation is now at [Permissions documentation][] -Node.js contains experimental support for creating policies on loading code. - -Policies are a security feature intended to allow guarantees -about what code Node.js is able to load. The use of policies assumes -safe practices for the policy files such as ensuring that policy -files cannot be overwritten by the Node.js application by using -file permissions. - -A best practice would be to ensure that the policy manifest is read-only for -the running Node.js application and that the file cannot be changed -by the running Node.js application in any way. A typical setup would be to -create the policy file as a different user id than the one running Node.js -and granting read permissions to the user id running Node.js. - -## Enabling - - - -The `--experimental-policy` flag can be used to enable features for policies -when loading modules. - -Once this has been set, all modules must conform to a policy manifest file -passed to the flag: - -```bash -node --experimental-policy=policy.json app.js -``` - -The policy manifest will be used to enforce constraints on code loaded by -Node.js. - -To mitigate tampering with policy files on disk, an integrity for -the policy file itself may be provided via `--policy-integrity`. -This allows running `node` and asserting the policy file contents -even if the file is changed on disk. - -```bash -node --experimental-policy=policy.json --policy-integrity="sha384-SggXRQHwCG8g+DktYYzxkXRIkTiEYWBHqev0xnpCxYlqMBufKZHAHQM3/boDaI/0" app.js -``` - -## Features - -### Error behavior - -When a policy check fails, Node.js by default will throw an error. -It is possible to change the error behavior to one of a few possibilities -by defining an "onerror" field in a policy manifest. The following values are -available to change the behavior: - -* `"exit"`: will exit the process immediately. - No cleanup code will be allowed to run. -* `"log"`: will log the error at the site of the failure. -* `"throw"`: will throw a JS error at the site of the failure. This is the - default. - -```json -{ - "onerror": "log", - "resources": { - "./app/checked.js": { - "integrity": "sha384-SggXRQHwCG8g+DktYYzxkXRIkTiEYWBHqev0xnpCxYlqMBufKZHAHQM3/boDaI/0" - } - } -} -``` - -### Integrity checks - -Policy files must use integrity checks with Subresource Integrity strings -compatible with the browser -[integrity attribute](https://www.w3.org/TR/SRI/#the-integrity-attribute) -associated with absolute URLs. - -When using `require()` or `import` all resources involved in loading are checked -for integrity if a policy manifest has been specified. If a resource does not -match the integrity listed in the manifest, an error will be thrown. - -An example policy file that would allow loading a file `checked.js`: - -```json -{ - "resources": { - "./app/checked.js": { - "integrity": "sha384-SggXRQHwCG8g+DktYYzxkXRIkTiEYWBHqev0xnpCxYlqMBufKZHAHQM3/boDaI/0" - } - } -} -``` - -Each resource listed in the policy manifest can be of one the following -formats to determine its location: - -1. A [relative-URL string][] to a resource from the manifest such as `./resource.js`, `../resource.js`, or `/resource.js`. -2. A complete URL string to a resource such as `file:///resource.js`. - -When loading resources the entire URL must match including search parameters -and hash fragment. `./a.js?b` will not be used when attempting to load -`./a.js` and vice versa. - -To generate integrity strings, a script such as -`node -e 'process.stdout.write("sha256-");process.stdin.pipe(crypto.createHash("sha256").setEncoding("base64")).pipe(process.stdout)' < FILE` -can be used. - -Integrity can be specified as the boolean value `true` to accept any -body for the resource which can be useful for local development. It is not -recommended in production since it would allow unexpected alteration of -resources to be considered valid. - -### Dependency redirection - -An application may need to ship patched versions of modules or to prevent -modules from allowing all modules access to all other modules. Redirection -can be used by intercepting attempts to load the modules wishing to be -replaced. - -```json -{ - "resources": { - "./app/checked.js": { - "dependencies": { - "fs": true, - "os": "./app/node_modules/alt-os", - "http": { "import": true } - } - } - } -} -``` - -The dependencies are keyed by the requested specifier string and have values -of either `true`, `null`, a string pointing to a module to be resolved, -or a conditions object. - -The specifier string does not perform any searching and must match exactly what -is provided to the `require()` or `import` except for a canonicalization step. -Therefore, multiple specifiers may be needed in the policy if it uses multiple -different strings to point to the same module (such as excluding the extension). - -Specifier strings are canonicalized but not resolved prior to be used for -matching in order to have some compatibility with import maps, for example if a -resource `file:///C:/app/server.js` was given the following redirection from a -policy located at `file:///C:/app/policy.json`: - -```json -{ - "resources": { - "file:///C:/app/utils.js": { - "dependencies": { - "./utils.js": "./utils-v2.js" - } - } - } -} -``` - -Any specifier used to load `file:///C:/app/utils.js` would then be intercepted -and redirected to `file:///C:/app/utils-v2.js` instead regardless of using an -absolute or relative specifier. However, if a specifier that is not an absolute -or relative URL string is used, it would not be intercepted. So, if an import -such as `import('#utils')` was used, it would not be intercepted. - -If the value of the redirection is `true`, a "dependencies" field at the top of -the policy file will be used. If that field at the top of the policy file is -`true` the default node searching algorithms are used to find the module. - -If the value of the redirection is a string, it is resolved relative to -the manifest and then immediately used without searching. - -Any specifier string for which resolution is attempted and that is not listed in -the dependencies results in an error according to the policy. - -Redirection does not prevent access to APIs through means such as direct access -to `require.cache` or through `module.constructor` which allow access to -loading modules. Policy redirection only affects specifiers to `require()` and -`import`. Other means, such as to prevent undesired access to APIs through -variables, are necessary to lock down that path of loading modules. - -A boolean value of `true` for the dependencies map can be specified to allow a -module to load any specifier without redirection. This can be useful for local -development and may have some valid usage in production, but should be used -only with care after auditing a module to ensure its behavior is valid. - -Similar to `"exports"` in `package.json`, dependencies can also be specified to -be objects containing conditions which branch how dependencies are loaded. In -the preceding example, `"http"` is allowed when the `"import"` condition is -part of loading it. - -A value of `null` for the resolved value causes the resolution to fail. This -can be used to ensure some kinds of dynamic access are explicitly prevented. - -Unknown values for the resolved module location cause failures but are -not guaranteed to be forward compatible. - -#### Example: Patched dependency - -Redirected dependencies can provide attenuated or modified functionality as fits -the application. For example, log data about timing of function durations by -wrapping the original: - -```js -const original = require('fn'); -module.exports = function fn(...args) { - console.time(); - try { - return new.target ? - Reflect.construct(original, args) : - Reflect.apply(original, this, args); - } finally { - console.timeEnd(); - } -}; -``` - -### Scopes - -Use the `"scopes"` field of a manifest to set configuration for many resources -at once. The `"scopes"` field works by matching resources by their segments. -If a scope or resource includes `"cascade": true`, unknown specifiers will -be searched for in their containing scope. The containing scope for cascading -is found by recursively reducing the resource URL by removing segments for -[special schemes][], keeping trailing `"/"` suffixes, and removing the query and -hash fragment. This leads to the eventual reduction of the URL to its origin. -If the URL is non-special the scope will be located by the URL's origin. If no -scope is found for the origin or in the case of opaque origins, a protocol -string can be used as a scope. If no scope is found for the URL's protocol, a -final empty string `""` scope will be used. - -Note, `blob:` URLs adopt their origin from the path they contain, and so a scope -of `"blob:https://nodejs.org"` will have no effect since no URL can have an -origin of `blob:https://nodejs.org`; URLs starting with -`blob:https://nodejs.org/` will use `https://nodejs.org` for its origin and -thus `https:` for its protocol scope. For opaque origin `blob:` URLs they will -have `blob:` for their protocol scope since they do not adopt origins. - -#### Example - -```json -{ - "scopes": { - "file:///C:/app/": {}, - "file:": {}, - "": {} - } -} -``` - -Given a file located at `file:///C:/app/bin/main.js`, the following scopes would -be checked in order: - -1. `"file:///C:/app/bin/"` - -This determines the policy for all file based resources within -`"file:///C:/app/bin/"`. This is not in the `"scopes"` field of the policy and -would be skipped. Adding this scope to the policy would cause it to be used -prior to the `"file:///C:/app/"` scope. - -2. `"file:///C:/app/"` - -This determines the policy for all file based resources within -`"file:///C:/app/"`. This is in the `"scopes"` field of the policy and it would -determine the policy for the resource at `file:///C:/app/bin/main.js`. If the -scope has `"cascade": true`, any unsatisfied queries about the resource would -delegate to the next relevant scope for `file:///C:/app/bin/main.js`, `"file:"`. - -3. `"file:///C:/"` - -This determines the policy for all file based resources within `"file:///C:/"`. -This is not in the `"scopes"` field of the policy and would be skipped. It would -not be used for `file:///C:/app/bin/main.js` unless `"file:///"` is set to -cascade or is not in the `"scopes"` of the policy. - -4. `"file:///"` - -This determines the policy for all file based resources on the `localhost`. This -is not in the `"scopes"` field of the policy and would be skipped. It would not -be used for `file:///C:/app/bin/main.js` unless `"file:///"` is set to cascade -or is not in the `"scopes"` of the policy. - -5. `"file:"` - -This determines the policy for all file based resources. It would not be used -for `file:///C:/app/bin/main.js` unless `"file:///"` is set to cascade or is not -in the `"scopes"` of the policy. - -6. `""` - -This determines the policy for all resources. It would not be used for -`file:///C:/app/bin/main.js` unless `"file:"` is set to cascade. - -#### Integrity using scopes - -Setting an integrity to `true` on a scope will set the integrity for any -resource not found in the manifest to `true`. - -Setting an integrity to `null` on a scope will set the integrity for any -resource not found in the manifest to fail matching. - -Not including an integrity is the same as setting the integrity to `null`. - -`"cascade"` for integrity checks will be ignored if `"integrity"` is explicitly -set. - -The following example allows loading any file: - -```json -{ - "scopes": { - "file:": { - "integrity": true - } - } -} -``` - -#### Dependency redirection using scopes - -The following example, would allow access to `fs` for all resources within -`./app/`: - -```json -{ - "resources": { - "./app/checked.js": { - "cascade": true, - "integrity": true - } - }, - "scopes": { - "./app/": { - "dependencies": { - "fs": true - } - } - } -} -``` - -The following example, would allow access to `fs` for all `data:` resources: - -```json -{ - "resources": { - "data:text/javascript,import('node:fs');": { - "cascade": true, - "integrity": true - } - }, - "scopes": { - "data:": { - "dependencies": { - "fs": true - } - } - } -} -``` - -#### Example: [import maps][] emulation - -Given an import map: - -```json -{ - "imports": { - "react": "./app/node_modules/react/index.js" - }, - "scopes": { - "./ssr/": { - "react": "./app/node_modules/server-side-react/index.js" - } - } -} -``` - -```json -{ - "dependencies": true, - "scopes": { - "": { - "cascade": true, - "dependencies": { - "react": "./app/node_modules/react/index.js" - } - }, - "./ssr/": { - "cascade": true, - "dependencies": { - "react": "./app/node_modules/server-side-react/index.js" - } - } - } -} -``` - -Import maps assume you can get any resource by default. This means -`"dependencies"` at the top level of the policy should be set to `true`. -Policies require this to be opt-in since it enables all resources of the -application cross linkage which doesn't make sense for many scenarios. They also -assume any given scope has access to any scope above its allowed dependencies; -all scopes emulating import maps must set `"cascade": true`. - -Import maps only have a single top level scope for their "imports". So for -emulating `"imports"` use the `""` scope. For emulating `"scopes"` use the -`"scopes"` in a similar manner to how `"scopes"` works in import maps. - -Caveats: Policies do not use string matching for various finding of scope. They -do URL traversals. This means things like `blob:` and `data:` URLs might not be -entirely interoperable between the two systems. For example import maps can -partially match a `data:` or `blob:` URL by partitioning the URL on a `/` -character, policies intentionally cannot. For `blob:` URLs import map scopes do -not adopt the origin of the `blob:` URL. - -Additionally, import maps only work on `import` so it may be desirable to add a -`"import"` condition to all dependency mappings. - -[import maps]: https://url.spec.whatwg.org/#relative-url-with-fragment-string -[relative-url string]: https://url.spec.whatwg.org/#relative-url-with-fragment-string -[special schemes]: https://url.spec.whatwg.org/#special-scheme +[Permissions documentation]: permissions.md#policies diff --git a/doc/api/process.md b/doc/api/process.md index f8481065d26429..05a22a8d34b7d0 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -3648,7 +3648,6 @@ changes: - v12.19.0 pr-url: https://github.com/nodejs/node/pull/32499 description: Calling `process.umask()` with no arguments is deprecated. - --> > Stability: 0 - Deprecated. Calling `process.umask()` with no argument causes @@ -3874,7 +3873,7 @@ cases: [`process.config`]: #processconfig [`process.execPath`]: #processexecpath [`process.exit()`]: #processexitcode -[`process.exitCode`]: #processexitcode +[`process.exitCode`]: #processexitcode_1 [`process.hrtime()`]: #processhrtimetime [`process.hrtime.bigint()`]: #processhrtimebigint [`process.kill()`]: #processkillpid-signal diff --git a/doc/api/report.md b/doc/api/report.md index ecfd666775506c..6d5341a59a2f4b 100644 --- a/doc/api/report.md +++ b/doc/api/report.md @@ -10,7 +10,7 @@ Delivers a JSON-formatted diagnostic summary, written to a file. -The report is intended for development, test and production use, to capture +The report is intended for development, test, and production use, to capture and preserve information for problem determination. It includes JavaScript and native stack traces, heap statistics, platform information, resource usage etc. With the report option enabled, diagnostic reports can be triggered @@ -508,9 +508,9 @@ the application, in expectation of self-adjusting the resource consumption, load balancing, monitoring etc. The content of the report consists of a header section containing the event -type, date, time, PID and Node.js version, sections containing JavaScript and +type, date, time, PID, and Node.js version, sections containing JavaScript and native stack traces, a section containing V8 heap information, a section -containing `libuv` handle information and an OS platform information section +containing `libuv` handle information, and an OS platform information section showing CPU and memory usage and system limits. An example report can be triggered using the Node.js REPL: diff --git a/doc/api/stream.md b/doc/api/stream.md index e8bdab43bdee43..9bd9877326fd01 100644 --- a/doc/api/stream.md +++ b/doc/api/stream.md @@ -594,7 +594,7 @@ added: v11.4.0 * {boolean} Is `true` if it is safe to call [`writable.write()`][stream-write], which means -the stream has not been destroyed, errored or ended. +the stream has not been destroyed, errored, or ended. ##### `writable.writableAborted` @@ -1545,7 +1545,7 @@ changes: * `chunk` {Buffer|Uint8Array|string|null|any} Chunk of data to unshift onto the read queue. For streams not operating in object mode, `chunk` must be a - string, `Buffer`, `Uint8Array` or `null`. For object mode streams, `chunk` + string, `Buffer`, `Uint8Array`, or `null`. For object mode streams, `chunk` may be any JavaScript value. * `encoding` {string} Encoding of string chunks. Must be a valid `Buffer` encoding, such as `'utf8'` or `'ascii'`. @@ -3113,7 +3113,7 @@ added: v1.2.0 For many simple cases, it is possible to create a stream without relying on inheritance. This can be accomplished by directly creating instances of the -`stream.Writable`, `stream.Readable`, `stream.Duplex` or `stream.Transform` +`stream.Writable`, `stream.Readable`, `stream.Duplex`, or `stream.Transform` objects and passing appropriate methods as constructor options. ```js @@ -3741,7 +3741,7 @@ changes: * Returns: {boolean} `true` if additional chunks of data may continue to be pushed; `false` otherwise. -When `chunk` is a `Buffer`, `Uint8Array` or `string`, the `chunk` of data will +When `chunk` is a `Buffer`, `Uint8Array`, or `string`, the `chunk` of data will be added to the internal queue for users of the stream to consume. Passing `chunk` as `null` signals the end of the stream (EOF), after which no more data can be written. @@ -4416,7 +4416,7 @@ situations within Node.js where this is done, particularly in the Use of `readable.push('')` is not recommended. -Pushing a zero-byte string, `Buffer` or `Uint8Array` to a stream that is not in +Pushing a zero-byte string, `Buffer`, or `Uint8Array` to a stream that is not in object mode has an interesting side effect. Because it _is_ a call to [`readable.push()`][stream-push], the call will end the reading process. However, because the argument is an empty string, no data is added to the diff --git a/doc/api/test.md b/doc/api/test.md index 5f2eba4fa5e57a..12e9b251956b80 100644 --- a/doc/api/test.md +++ b/doc/api/test.md @@ -475,7 +475,7 @@ same as [`it([name], { skip: true }[, fn])`][it options]. Shorthand for marking a test as `TODO`, same as [`it([name], { todo: true }[, fn])`][it options]. -### `before([, fn][, options])` +## `before([, fn][, options])` + +> Stability: 1 - Experimental + +* `limit` {integer} + +The API is a no-op if `--heapsnapshot-near-heap-limit` is already set from the +command line or the API is called more than once. `limit` must be a positive +integer. See [`--heapsnapshot-near-heap-limit`][] for more information. + ## Serialization API The serialization API provides means of serializing JavaScript values in a way @@ -1010,6 +1024,7 @@ Returns true if the Node.js instance is run to build a snapshot. [HTML structured clone algorithm]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm [Hook Callbacks]: #hook-callbacks [V8]: https://developers.google.com/v8/ +[`--heapsnapshot-near-heap-limit`]: cli.md#--heapsnapshot-near-heap-limitmax_count [`AsyncLocalStorage`]: async_context.md#class-asynclocalstorage [`Buffer`]: buffer.md [`DefaultDeserializer`]: #class-v8defaultdeserializer diff --git a/doc/api/vm.md b/doc/api/vm.md index aa99c8430767a0..f99f4966db82b6 100644 --- a/doc/api/vm.md +++ b/doc/api/vm.md @@ -114,6 +114,18 @@ Creating a new `vm.Script` object compiles `code` but does not run it. The compiled `vm.Script` can be run later multiple times. The `code` is not bound to any global object; rather, it is bound before each run, just for that run. +### `script.cachedDataRejected` + + + +* {boolean|undefined} + +When `cachedData` is supplied to create the `vm.Script`, this value will be set +to either `true` or `false` depending on acceptance of the data by V8. +Otherwise the value is `undefined`. + ### `script.createCachedData()` * Returns: {ReadableStream\[]} @@ -647,6 +651,10 @@ Signals an error that causes the {ReadableStream} to error and close. Every {ReadableStream} has a controller that is responsible for @@ -974,10 +982,6 @@ changes: The `WritableStreamDefaultController` manage's the {WritableStream}'s internal state. -#### `writableStreamDefaultController.abortReason` - -* Type: {any} The `reason` value passed to `writableStream.abort()`. - #### `writableStreamDefaultController.error(error)`