Merge branch 'master' of github.com:cloudflare/quiche into update
Change-Id: Icf03bc27789bc8c620f3a6b49b75233f3c0c6393
diff --git a/.gitignore b/.gitignore
index 6936990..fc3e671 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,3 @@
-/target
+**/target
**/*.rs.bk
-Cargo.lock
+**/Cargo.lock
diff --git a/.gitmodules b/.gitmodules
index 4ea390f..c3033aa 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,4 +1,4 @@
[submodule "boringssl"]
path = deps/boringssl
- url = https://boringssl.googlesource.com/boringssl
+ url = https://github.com/google/boringssl.git
ignore = dirty
diff --git a/.travis.yml b/.travis.yml
index 4d7dd21..d2363f5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,15 +1,29 @@
-language: rust
+env:
+ global:
+ - secure: "Aa+7RLfRyfdH2ENH0fyjI7Waq7/9XSk1LP6sDwG0zG1uhmus/z0QaiaCd027mFY3V25Kzfd6TVzL92dp3U63+3Qn3hHle3oNdu1jqaSBHB1L7R+IS6tLW0Bny5Zd7mMRNbVO642qvmuZo8A5nuu/WHp3w91WI4rp1rhULm/uhQ/+7Sx7mDSR1vRJyIeB7mETzRpckz0cUiZU656AB1hRsMVzkm+no9urHcnEP6AcPukVlWGgsdcBloMoczwp8M/cnvrGg8SuroEnu82i3eY5TwmXBhtmFa06WUaiIafT8PcB+JVADwunS1nJ7tTGCjInVQY5qNogPJxdr97E9vuHJGMDpdCkDuvTFaF2FdnXuvli3nUr2w7mP3t991ocWQW9PMRUTJe+/pFJ935puy8zD797UKUR3d2GwZRHIpwbzmcP+QQ1wG3odoFk4i/tEUAHtKirh6VglQyp9BFIaIX7TuuqIyRqxcqHVsVfDVVouHBIYuy7uRDd7YjmoF3IqqIKsHEFBnMT9GSQeifjJAPYEOoPXfzoa1Ya8PbmSdYtVR9nyjIHHLv/xDYxG3ulBgmz1ieGe9pGNgqLmi6GSiyJ/Vj+5L+1bJGrVMDELHQR/bVdZMmoAp6y8f397hace2qHKDBVN4AVfDWeCiTs1NUtgrJVIpj3Xt788RrnIfvuhF4="
+ - secure: "CE2MWJZ+EtmaOheaGBd5XNgV7A/AGLpQ1Lct2R4v8BG3pXFFAM9uJEvpyTXBFx40gzD8HTBHbNRw1Ae+hP/Hz3M8o96jrdVsPlojKaShaOqoalYCghnfxDSd6WsstJdTYUk1iKhU3bSin/PHhDHe5i/dpwTay4DqSCX4MWaJPpItRIwiPpd8ekVGrHb/DkuTpEWY35Dg+9oao9KnB5c/D1oHLvInRMCivtpPpKRxNWkznOGprQALGIPmnHupBQ0zu6H7+K7TE/DT/HmocQd1h6HEba6DGkKqXU79NgS3uz2EFfgdhnIvTlxWTZFtkET9lym5mqjyuvwkIDsEDfGmk7dGjT0MFe2/9RRMxeNjINA1dlat4riUZ+a5dWqj/7uSvcOgTv0lqT5qitHbtqI0Id/TMaZlVE9r1x+vSTy/7ISzwhVPOwFa6SZJFwrHVBqnPZKP8gajpCcDJMKdFjfuLNZMF/NlDhj78S9Hyr0CUUoCKcPYiCxyCPfV7LzNMtlaynKJG2A+z0vLqZ8YSlTrqBfjuVzrq8EDJyJWMRuik3rkNe/GOFzUgQ+nBxgOzPT0Y5CPlEQNYW991pRz2r5gXhR+MSpjF3ytSrMCYYfIwymwqxb9oX4EvPCHCrWCqT4lebQyfPO8Pp8tDeV3XeRBM5a6KjOD0WfZ5pRlZ/REWrM="
+
+cache: cargo
addons:
apt:
- packages:
- - libev-dev
- - protobuf-compiler
- - uthash-dev
+ packages: &linux_deps
+ - libev-dev
+ - uthash-dev
+ - protobuf-compiler
matrix:
include:
- - rust: stable
+ - name: "stable Linux x86_64/x86"
+ language: rust
+ rust: stable
+ env:
+ DEPLOY_BUILD=yes
+ TARGET_32=i686-unknown-linux-gnu
+ addons:
+ apt:
+ packages:
+ - [*linux_deps, gcc-multilib, g++-multilib]
before_install:
# Install and use the current stable release of Go
- gimme --list
@@ -17,8 +31,8 @@
- gimme --list
install:
- rustup component add clippy
+ - rustup target add $TARGET_32
script:
- - RUSTFLAGS="-D warnings" cargo build --release --verbose
- RUSTFLAGS="-D warnings" cargo test --verbose
- RUSTFLAGS="-D warnings" cargo package --verbose --allow-dirty
- cargo clippy --examples -- -D warnings
@@ -27,10 +41,21 @@
# http3_test
- RUSTFLAGS="-D warnings" cargo test --no-run --verbose --manifest-path tools/http3_test/Cargo.toml
- cargo clippy --manifest-path tools/http3_test/Cargo.toml -- -D warnings
- # quic-trace-log
- - RUSTFLAGS="-D warnings" cargo build --release --verbose --manifest-path tools/quic-trace-log/Cargo.toml
- - cargo clippy --manifest-path tools/quic-trace-log/Cargo.toml -- -D warnings
- - rust: nightly
+ # qlog
+ - RUSTFLAGS="-D warnings" cargo test --verbose --manifest-path tools/qlog/Cargo.toml
+ - cargo clippy --manifest-path tools/qlog/Cargo.toml -- -D warnings
+ # quiche-apps
+ - RUSTFLAGS="-D warnings" cargo build --verbose --manifest-path tools/apps/Cargo.toml
+ - cargo clippy --manifest-path tools/apps/Cargo.toml -- -D warnings
+ # x86 cross build
+ - RUSTFLAGS="-D warnings" cargo build --target=$TARGET_32
+ - name: "nightly Linux x86_64"
+ language: rust
+ rust: nightly
+ addons:
+ apt:
+ packages:
+ - [*linux_deps]
before_install:
# Install and use the current stable release of Go
- gimme --list
@@ -40,7 +65,6 @@
- rustup component add rustfmt
- cargo install cargo-fuzz
script:
- - RUSTFLAGS="-D warnings" cargo build --release --verbose
- RUSTFLAGS="-D warnings" cargo test --verbose
- RUSTFLAGS="-D warnings" cargo package --verbose --allow-dirty
- cargo fmt -- --check
@@ -54,38 +78,128 @@
# http3_test
- RUSTFLAGS="-D warnings" cargo test --no-run --verbose --manifest-path tools/http3_test/Cargo.toml
- cargo fmt --manifest-path tools/http3_test/Cargo.toml -- --check
- # quic-trace-log
- - RUSTFLAGS="-D warnings" cargo build --release --verbose --manifest-path tools/quic-trace-log/Cargo.toml
- - cargo fmt --manifest-path tools/quic-trace-log/Cargo.toml -- --check
- - rust: stable
- os: osx
- script:
- - RUSTFLAGS="-D warnings" cargo build --release --verbose
- - RUSTFLAGS="-D warnings" cargo test --verbose
- - rust: stable
+ # qlog
+ - RUSTFLAGS="-D warnings" cargo test --verbose --manifest-path tools/qlog/Cargo.toml
+ - cargo fmt --manifest-path tools/qlog/Cargo.toml -- --check
+ # quiche-apps
+ - RUSTFLAGS="-D warnings" cargo build --verbose --manifest-path tools/apps/Cargo.toml
+ - cargo fmt --manifest-path tools/apps/Cargo.toml -- --check
+ - name: "stable macOS + iOS"
+ language: rust
+ rust: stable
os: osx
osx_image: xcode11.2
install:
- rustup target add aarch64-apple-ios x86_64-apple-ios
- - cargo install cargo-lipo
script:
- - RUSTFLAGS="-D warnings" cargo lipo --release --verbose
- - rust: stable
+ # macOS
+ - RUSTFLAGS="-D warnings" cargo build --verbose
+ - RUSTFLAGS="-D warnings" cargo test --verbose
+ # iOS
+ - cargo install --force cargo-lipo
+ - RUSTFLAGS="-D warnings" cargo lipo --verbose
+ - name: "stable Windows x86_64/x86"
+ language: rust
+ rust: stable
os: windows
+ env:
+ TARGET_64=x86_64-pc-windows-msvc
+ TARGET_32=i686-pc-windows-msvc
before_install:
- choco install golang nasm
# Update $PATH
- export PATH="$(powershell -Command '("Process", "Machine" | % { [Environment]::GetEnvironmentVariable("PATH", $_) -Split ";" -Replace "\\$", "" } | Select -Unique | % { cygpath $_ }) -Join ":"')"
+ install:
+ - rustup target add $TARGET_32 $TARGET_64
script:
- - RUSTFLAGS="-D warnings" cargo build --release --verbose
- - RUSTFLAGS="-D warnings" cargo test --verbose
+ - RUSTFLAGS="-D warnings" cargo build --verbose --target=$TARGET_64
+ - RUSTFLAGS="-D warnings" cargo test --verbose --target=$TARGET_64
+ - RUSTFLAGS="-D warnings" cargo build --verbose --target=$TARGET_32
+ - RUSTFLAGS="-D warnings" cargo test --verbose --target=$TARGET_32
+ - name: "stable Android"
+ language: android
+ dist: trusty
+ env:
+ NDK_VER_OLD=r13b
+ NDK_VER=r21
+ CMAKE_VER=3.6.4111459
+ android:
+ components:
+ - build-tools-26.0.1
+ # Minimum API level supported
+ - android-21
+ install:
+ # Install rust manually
+ - curl https://build.travis-ci.org/files/rustup-init.sh -sSf | sh -s -- -y --default-toolchain stable
+ - export PATH=$HOME/.cargo/bin:$PATH
+ - rustup default stable
+ - rustup target add aarch64-linux-android arm-linux-androideabi armv7-linux-androideabi i686-linux-android
+ # Additional Android components
+ - echo y | sdkmanager "cmake;$CMAKE_VER"
+ - export PATH=$ANDROID_HOME/cmake/$CMAKE_VER/bin/:$PATH
+ script:
+ #
+ # Old NDK. Here we use 13b
+ #
+ - NDK_URL=https://dl.google.com/android/repository/android-ndk-%s-linux-x86_64.zip
+ - curl -ondk.zip -q $(printf $NDK_URL $NDK_VER_OLD)
+ - unzip -q ndk.zip -d $HOME
+ - export ANDROID_NDK_HOME=$HOME/android-ndk-$NDK_VER_OLD
+ # Setup android toolchain
+ - export TOOLCHAIN_DIR=$(pwd)/toolchain
+ - mkdir -p $TOOLCHAIN_DIR
+ - tools/setup_android.sh
+ - tools/build_android.sh --verbose --features ndk-old-gcc
+ - rm -fr $TOOLCHAIN_DIR && rm -f .cargo/config
+ #
+ # NDK 19 or higher. Here we use 21 (long term support)
+ #
+ - curl -ondk.zip -q $(printf $NDK_URL $NDK_VER)
+ - unzip -q ndk.zip -d $HOME
+ - export ANDROID_NDK_HOME=$HOME/android-ndk-$NDK_VER
+ - cargo install cargo-ndk
+ - cargo clean
+ - tools/build_android_ndk19.sh --verbose
+ - name: "NGINX"
+ language: rust
+ rust: stable
+ env:
+ NGINX_VER=1.16.1
+ addons:
+ apt:
+ packages:
+ - [*linux_deps]
+ before_install:
+ # Install and use the current stable release of Go
+ - gimme --list
+ - eval "$(gimme stable)"
+ - gimme --list
+ script:
+ - curl -O https://nginx.org/download/nginx-$NGINX_VER.tar.gz
+ - tar xzf nginx-$NGINX_VER.tar.gz
+ - |
+ cd nginx-$NGINX_VER &&
+ patch -p01 < ../extras/nginx/nginx-1.16.patch &&
+ ./configure --with-http_ssl_module --with-http_v2_module --with-http_v3_module --with-openssl="../deps/boringssl" --with-quiche=".." --with-debug &&
+ make -j`nproc`
+ - objs/nginx -V
deploy:
- provider: pages
- fqdn: docs.quic.tech
- local-dir: target/doc
- skip-cleanup: true
- github-token: $GITHUB_TOKEN
- on:
- branch: master
- condition: $TRAVIS_RUST_VERSION = stable && $TRAVIS_OS_NAME = linux
+ # publish docs
+ - provider: pages
+ fqdn: docs.quic.tech
+ local-dir: target/doc
+ skip-cleanup: true
+ github-token: $GITHUB_TOKEN
+ on:
+ branch: master
+ condition: $DEPLOY_BUILD = yes
+ # publish Docker images
+ - provider: script
+ skip-cleanup: true
+ script: >-
+ echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin &&
+ make -C extras/docker all publish
+ on:
+ branch: master
+ condition: $DEPLOY_BUILD = yes
diff --git a/COPYING b/COPYING
index 606b083..c044775 100644
--- a/COPYING
+++ b/COPYING
@@ -1,5 +1,4 @@
-Copyright (C) 2018, Cloudflare, Inc.
-Copyright (C) 2018, Alessandro Ghedini
+Copyright (C) 2018-2019, Cloudflare, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/Cargo.toml b/Cargo.toml
index 0e46b0d..1cd1561 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "quiche"
-version = "0.1.0"
+version = "0.2.0"
authors = ["Alessandro Ghedini <alessandro@ghedini.me>"]
edition = "2018"
build = "src/build.rs"
@@ -43,6 +43,13 @@
# Equivalent to "--cfg fuzzing", but can also be checked in build.rs.
fuzzing = []
+# For building with Android NDK < 18 and GCC.
+ndk-old-gcc = []
+
+# Build benchmarks. This is intended to make benchmark-specific dependencies
+# optional, so we don't have to build them for other targets.
+bench = ["criterion"]
+
[package.metadata.docs.rs]
default-features = false
@@ -53,6 +60,7 @@
log = { version = "0.4", features = ["std"] }
libc = "0.2"
ring = "0.16"
+criterion = { version = "0.3", optional = true }
lazy_static = "1"
[target."cfg(windows)".dependencies]
@@ -61,9 +69,6 @@
[dev-dependencies]
mio = "0.6"
url = "1"
-docopt = "1"
-criterion = "0.3"
-env_logger = "0.6"
[profile.bench]
debug = true
@@ -77,3 +82,4 @@
[[bench]]
name = "benches"
harness = false
+required-features = ["bench"]
diff --git a/README.md b/README.md
index 08e7fc6..1b7d328 100644
--- a/README.md
+++ b/README.md
@@ -10,8 +10,9 @@
and handling connection state. The application is responsible for providing I/O
(e.g. sockets handling) as well as an event loop with support for timers.
-A live QUIC server based on quiche is available at ``https://quic.tech:4433/``
-to be used for experimentation.
+A live QUIC server based on quiche is available at ``https://quic.tech:4433/``,
+and an HTTP/3 one at ``https://quic.tech:8443/``, that can be used for
+experimentation.
For more information on how quiche came about and some insights into its design
you can read a [post] on Cloudflare's (where this library is used in production)
@@ -206,7 +207,7 @@
Building
--------
-quiche requires Rust 1.38 or later to build. The latest stable Rust release can
+quiche requires Rust 1.39 or later to build. The latest stable Rust release can
be installed using [rustup](https://rustup.rs/).
Once the Rust build environment is setup, the quiche source code can be fetched
@@ -249,20 +250,53 @@
To build quiche for Android, you need the following:
-- Install Android NDK (13b or higher), using Android Studio or directly.
-- Set `ANDROID_NDK_HOME` environment variable to NDK path, e.g. using bash:
+- Install the [Android NDK] (13b or higher), using Android Studio or directly.
+- Set `ANDROID_NDK_HOME` environment variable to NDK path, e.g.
```bash
$ export ANDROID_NDK_HOME=/usr/local/share/android-ndk
```
-- Install the Rust toolchain for Android architectures:
+- Install the Rust toolchain for Android architectures needed:
```bash
- $ rustup target add aarch64-linux-android arm-linux-androideabi armv7-linux-androideabi i686-linux-android
+ $ rustup target add aarch64-linux-android arm-linux-androideabi armv7-linux-androideabi i686-linux-android x86_64-linux-android
```
-Then, to prepare the cross-compiling toolchain, run the following command:
+Note that the minimum API level is 21 for all target architectures.
+
+Depending on the NDK version used, you can take one of the following procedures:
+
+[Android NDK]: https://developer.android.com/ndk
+
+#### NDK version >= 19
+
+For NDK version 19 or higher (21 recommended), you can build in a simpler
+way using [cargo-ndk]. You need to install [cargo-ndk] first.
+
+```bash
+ $ cargo install cargo-ndk
+```
+
+You can build the quiche library using the following procedure. Note that
+`--target` and `--android-platform` are mandatory.
+
+```bash
+ $ cargo ndk --target aarch64-linux-android --android-platform 21 -- build
+```
+
+See [build_android_ndk19.sh] for more information.
+
+Note that building with NDK version 18 appears to be broken.
+
+[cargo-ndk]: https://docs.rs/crate/cargo-ndk
+[build_android_ndk19.sh]: https://github.com/cloudflare/quiche/blob/master/tools/build_android_ndk19.sh
+
+#### NDK version < 18
+
+If you need to use NDK version < 18 (gcc), you can build quiche in the following way.
+
+To prepare the cross-compiling toolchain, run the following command:
```bash
$ tools/setup_android.sh
@@ -270,13 +304,12 @@
It will create a standalone toolchain for arm64/arm/x86 architectures under the
`$TOOLCHAIN_DIR/arch` directory. If you didn't set `TOOLCHAIN_DIR` environment
-variable, the current directory will be used. Note that the minimum API level is
-21 for all target architectures.
+variable, the current directory will be used.
After it run successfully, run the following script to build libquiche:
```bash
- $ tools/build_android.sh
+ $ tools/build_android.sh --features ndk-old-gcc
```
It will build binaries for aarch64, armv7 and i686. You can pass parameters to
@@ -284,7 +317,7 @@
with verbose logs, do the following:
```bash
- $ tools/build_android.sh --release -vv
+ $ tools/build_android.sh --features ndk-old-gcc --release -vv
```
### Building for iOS
@@ -325,9 +358,7 @@
Copyright
---------
-Copyright (C) 2018, Cloudflare, Inc.
-
-Copyright (C) 2018, Alessandro Ghedini
+Copyright (C) 2018-2019, Cloudflare, Inc.
See [COPYING] for the license.
diff --git a/benches/benches.rs b/benches/benches.rs
index 039affe..bbff101 100755
--- a/benches/benches.rs
+++ b/benches/benches.rs
@@ -1,4 +1,4 @@
-// Copyright (C) 2018, Cloudflare, Inc.
+// Copyright (C) 2018-2019, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -93,7 +93,7 @@
let mut config = make_bench_config();
- let h3_config = quiche::h3::Config::new().unwrap();
+ let mut h3_config = quiche::h3::Config::new().unwrap();
let mut s =
quiche::h3::testing::Session::with_configs(&mut config, &mut h3_config)
@@ -132,7 +132,7 @@
s.pipe.flush_client(&mut buf).unwrap();
match s.server.poll(&mut s.pipe.server) {
- Ok((stream, quiche::h3::Event::Headers(_))) => {
+ Ok((stream, quiche::h3::Event::Headers { .. })) => {
s.server
.send_response(
&mut s.pipe.server,
diff --git a/deps/boringssl b/deps/boringssl
index 8fe1584..f50a8a7 160000
--- a/deps/boringssl
+++ b/deps/boringssl
@@ -1 +1 @@
-Subproject commit 8fe1584023006c844816dbf63fb776b7f84b88b3
+Subproject commit f50a8a77bdab244267f3f70420ed7ef0eaa6d69e
diff --git a/examples/Dockerfile b/examples/Dockerfile
deleted file mode 100644
index 5a992c4..0000000
--- a/examples/Dockerfile
+++ /dev/null
@@ -1,25 +0,0 @@
-FROM rust:1.35 as build
-
-WORKDIR /usr/src/
-
-RUN apt-get update && apt-get install -y \
- git \
- cmake \
- golang \
- && rm -rf /var/lib/apt/lists/*
-
-RUN git clone --recurse-submodules --depth 1 https://github.com/cloudflare/quiche
-
-RUN cd quiche && cargo build --release --examples
-
-FROM debian:latest
-RUN apt-get update && apt-get install -y \
- ca-certificates \
- && rm -rf /var/lib/apt/lists/*
-RUN update-ca-certificates
-COPY --from=build /usr/src/quiche/target/release/examples/http3-client /usr/local/quiche/bin/
-COPY --from=build /usr/src/quiche/target/release/examples/http3-server /usr/local/quiche/bin/
-COPY --from=build /usr/src/quiche/target/release/examples/client /usr/local/quiche/bin/
-COPY --from=build /usr/src/quiche/target/release/examples/server /usr/local/quiche/bin/
-ENV PATH="/usr/local/quiche/bin/:${PATH}"
-ENV RUST_LOG=info
diff --git a/examples/client.c b/examples/client.c
index 2f24cc1..e79c8f7 100644
--- a/examples/client.c
+++ b/examples/client.c
@@ -1,5 +1,4 @@
-// Copyright (C) 2018, Cloudflare, Inc.
-// Copyright (C) 2018, Alessandro Ghedini
+// Copyright (C) 2018-2019, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -244,9 +243,9 @@
}
quiche_config_set_application_protos(config,
- (uint8_t *) "\x05hq-23\x08http/0.9", 15);
+ (uint8_t *) "\x05hq-25\x05hq-24\x05hq-23\x08http/0.9", 15);
- quiche_config_set_idle_timeout(config, 5000);
+ quiche_config_set_max_idle_timeout(config, 5000);
quiche_config_set_max_packet_size(config, MAX_DATAGRAM_SIZE);
quiche_config_set_initial_max_data(config, 10000000);
quiche_config_set_initial_max_stream_data_bidi_local(config, 1000000);
diff --git a/examples/client.rs b/examples/client.rs
index d1de843..da96e95 100644
--- a/examples/client.rs
+++ b/examples/client.rs
@@ -1,5 +1,4 @@
-// Copyright (C) 2018, Cloudflare, Inc.
-// Copyright (C) 2018, Alessandro Ghedini
+// Copyright (C) 2018-2019, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -30,55 +29,27 @@
use std::net::ToSocketAddrs;
-use std::io::prelude::*;
-
use ring::rand::*;
const MAX_DATAGRAM_SIZE: usize = 1350;
const HTTP_REQ_STREAM_ID: u64 = 4;
-const USAGE: &str = "Usage:
- client [options] URL
- client -h | --help
-
-Options:
- --max-data BYTES Connection-wide flow control limit [default: 10000000].
- --max-stream-data BYTES Per-stream flow control limit [default: 1000000].
- --wire-version VERSION The version number to send to the server [default: babababa].
- --dump-packets PATH Dump the incoming packets as files in the given directory.
- --no-verify Don't verify server's certificate.
- -h --help Show this screen.
-";
-
fn main() {
let mut buf = [0; 65535];
let mut out = [0; MAX_DATAGRAM_SIZE];
- env_logger::builder()
- .default_format_timestamp_nanos(true)
- .init();
+ let mut args = std::env::args();
- let args = docopt::Docopt::new(USAGE)
- .and_then(|dopt| dopt.parse())
- .unwrap_or_else(|e| e.exit());
+ let cmd = &args.next().unwrap();
- let max_data = args.get_str("--max-data");
- let max_data = u64::from_str_radix(max_data, 10).unwrap();
+ if args.len() != 1 {
+ println!("Usage: {} URL", cmd);
+ println!("\nSee tools/apps/ for more complete implementations.");
+ return;
+ }
- let max_stream_data = args.get_str("--max-stream-data");
- let max_stream_data = u64::from_str_radix(max_stream_data, 10).unwrap();
-
- let version = args.get_str("--wire-version");
- let version = u32::from_str_radix(version, 16).unwrap();
-
- let dump_path = if args.get_str("--dump-packets") != "" {
- Some(args.get_str("--dump-packets"))
- } else {
- None
- };
-
- let url = url::Url::parse(args.get_str("URL")).unwrap();
+ let url = url::Url::parse(&args.next().unwrap()).unwrap();
// Setup the event loop.
let poll = mio::Poll::new().unwrap();
@@ -110,31 +81,24 @@
.unwrap();
// Create the configuration for the QUIC connection.
- let mut config = quiche::Config::new(version).unwrap();
+ let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
- config.verify_peer(true);
+ // *CAUTION*: this should not be set to `false` in production!!!
+ config.verify_peer(false);
config
- .set_application_protos(b"\x05hq-23\x08http/0.9")
+ .set_application_protos(b"\x05hq-25\x05hq-24\x05hq-23\x08http/0.9")
.unwrap();
- config.set_idle_timeout(5000);
+ config.set_max_idle_timeout(5000);
config.set_max_packet_size(MAX_DATAGRAM_SIZE as u64);
- config.set_initial_max_data(max_data);
- config.set_initial_max_stream_data_bidi_local(max_stream_data);
- config.set_initial_max_stream_data_bidi_remote(max_stream_data);
+ config.set_initial_max_data(10_000_000);
+ config.set_initial_max_stream_data_bidi_local(1_000_000);
+ config.set_initial_max_stream_data_bidi_remote(1_000_000);
config.set_initial_max_streams_bidi(100);
config.set_initial_max_streams_uni(100);
config.set_disable_active_migration(true);
- if args.get_bool("--no-verify") {
- config.verify_peer(false);
- }
-
- if std::env::var_os("SSLKEYLOGFILE").is_some() {
- config.log_keys();
- }
-
// Generate a random source connection ID for the connection.
let mut scid = [0; quiche::MAX_CONN_ID_LEN];
SystemRandom::new().fill(&mut scid[..]).unwrap();
@@ -166,8 +130,6 @@
let mut req_sent = false;
- let mut pkt_count = 0;
-
loop {
poll.poll(&mut events, conn.timeout()).unwrap();
@@ -201,17 +163,6 @@
debug!("got {} bytes", len);
- if let Some(target_path) = dump_path {
- let path = format!("{}/{}.pkt", target_path, pkt_count);
-
- if let Ok(f) = std::fs::File::create(&path) {
- let mut f = std::io::BufWriter::new(f);
- f.write_all(&buf[..len]).ok();
- }
- }
-
- pkt_count += 1;
-
// Process potentially coalesced packets.
let read = match conn.recv(&mut buf[..len]) {
Ok(v) => v,
diff --git a/examples/http3-client.c b/examples/http3-client.c
index 1d65d82..23c866e 100644
--- a/examples/http3-client.c
+++ b/examples/http3-client.c
@@ -332,7 +332,7 @@
(uint8_t *) QUICHE_H3_APPLICATION_PROTOCOL,
sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1);
- quiche_config_set_idle_timeout(config, 5000);
+ quiche_config_set_max_idle_timeout(config, 5000);
quiche_config_set_max_packet_size(config, MAX_DATAGRAM_SIZE);
quiche_config_set_initial_max_data(config, 10000000);
quiche_config_set_initial_max_stream_data_bidi_local(config, 1000000);
diff --git a/examples/http3-client.rs b/examples/http3-client.rs
index dc50ece..d38ed60 100644
--- a/examples/http3-client.rs
+++ b/examples/http3-client.rs
@@ -33,53 +33,21 @@
const MAX_DATAGRAM_SIZE: usize = 1350;
-const USAGE: &str = "Usage:
- http3-client [options] URL
- http3-client -h | --help
-
-Options:
- --method METHOD Use the given HTTP request method [default: GET].
- --body FILE Send the given file as request body.
- --max-data BYTES Connection-wide flow control limit [default: 10000000].
- --max-stream-data BYTES Per-stream flow control limit [default: 1000000].
- --wire-version VERSION The version number to send to the server [default: babababa].
- --no-verify Don't verify server's certificate.
- --no-grease Don't send GREASE.
- -H --header HEADER ... Add a request header.
- -n --requests REQUESTS Send the given number of identical requests [default: 1].
- -h --help Show this screen.
-";
-
fn main() {
let mut buf = [0; 65535];
let mut out = [0; MAX_DATAGRAM_SIZE];
- env_logger::builder()
- .default_format_timestamp_nanos(true)
- .init();
+ let mut args = std::env::args();
- let args = docopt::Docopt::new(USAGE)
- .and_then(|dopt| dopt.parse())
- .unwrap_or_else(|e| e.exit());
+ let cmd = &args.next().unwrap();
- let max_data = args.get_str("--max-data");
- let max_data = u64::from_str_radix(max_data, 10).unwrap();
+ if args.len() != 1 {
+ println!("Usage: {} URL", cmd);
+ println!("\nSee tools/apps/ for more complete implementations.");
+ return;
+ }
- let max_stream_data = args.get_str("--max-stream-data");
- let max_stream_data = u64::from_str_radix(max_stream_data, 10).unwrap();
-
- let version = args.get_str("--wire-version");
- let version = u32::from_str_radix(version, 16).unwrap();
-
- let url = url::Url::parse(args.get_str("URL")).unwrap();
-
- // Request headers (can be multiple).
- let req_headers = args.get_vec("--header");
-
- let reqs_count = args.get_str("--requests");
- let reqs_count = u64::from_str_radix(reqs_count, 10).unwrap();
-
- let mut reqs_complete = 0;
+ let url = url::Url::parse(&args.next().unwrap()).unwrap();
// Setup the event loop.
let poll = mio::Poll::new().unwrap();
@@ -111,38 +79,27 @@
.unwrap();
// Create the configuration for the QUIC connection.
- let mut config = quiche::Config::new(version).unwrap();
+ let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
- config.verify_peer(true);
+ // *CAUTION*: this should not be set to `false` in production!!!
+ config.verify_peer(false);
config
.set_application_protos(quiche::h3::APPLICATION_PROTOCOL)
.unwrap();
- config.set_idle_timeout(5000);
+ config.set_max_idle_timeout(5000);
config.set_max_packet_size(MAX_DATAGRAM_SIZE as u64);
- config.set_initial_max_data(max_data);
- config.set_initial_max_stream_data_bidi_local(max_stream_data);
- config.set_initial_max_stream_data_bidi_remote(max_stream_data);
- config.set_initial_max_stream_data_uni(max_stream_data);
+ config.set_initial_max_data(10_000_000);
+ config.set_initial_max_stream_data_bidi_local(1_000_000);
+ config.set_initial_max_stream_data_bidi_remote(1_000_000);
+ config.set_initial_max_stream_data_uni(1_000_000);
config.set_initial_max_streams_bidi(100);
config.set_initial_max_streams_uni(100);
config.set_disable_active_migration(true);
let mut http3_conn = None;
- if args.get_bool("--no-verify") {
- config.verify_peer(false);
- }
-
- if args.get_bool("--no-grease") {
- config.grease(false);
- }
-
- if std::env::var_os("SSLKEYLOGFILE").is_some() {
- config.log_keys();
- }
-
// Generate a random source connection ID for the connection.
let mut scid = [0; quiche::MAX_CONN_ID_LEN];
SystemRandom::new().fill(&mut scid[..]).unwrap();
@@ -180,41 +137,18 @@
path.push_str(query);
}
- let mut req = vec![
- quiche::h3::Header::new(":method", args.get_str("--method")),
+ let req = vec![
+ quiche::h3::Header::new(":method", "GET"),
quiche::h3::Header::new(":scheme", url.scheme()),
quiche::h3::Header::new(":authority", url.host_str().unwrap()),
quiche::h3::Header::new(":path", &path),
quiche::h3::Header::new("user-agent", "quiche"),
];
- // Add custom headers to the request.
- for header in &req_headers {
- let header_split: Vec<&str> = header.splitn(2, ": ").collect();
- if header_split.len() != 2 {
- panic!("malformed header provided - \"{}\"", header);
- }
-
- req.push(quiche::h3::Header::new(header_split[0], header_split[1]));
- }
-
- let body = if args.get_bool("--body") {
- std::fs::read(args.get_str("--body")).ok()
- } else {
- None
- };
-
- if body.is_some() {
- req.push(quiche::h3::Header::new(
- "content-length",
- &body.as_ref().unwrap().len().to_string(),
- ));
- }
-
- let mut reqs_sent = 0;
-
let req_start = std::time::Instant::now();
+ let mut req_sent = false;
+
loop {
poll.poll(&mut events, conn.timeout()).unwrap();
@@ -269,12 +203,6 @@
if conn.is_closed() {
info!("connection closed, {:?}", conn.stats());
-
- if reqs_complete != reqs_count {
- error!("connection timed out after {:?} and only completed {}/{} requests",
- req_start.elapsed(), reqs_complete, reqs_count);
- }
-
break;
}
@@ -289,49 +217,23 @@
// Send HTTP requests once the QUIC connection is established, and until
// all requests have been sent.
if let Some(h3_conn) = &mut http3_conn {
- let mut reqs_done = 0;
-
- for _ in reqs_sent..reqs_count {
+ if !req_sent {
info!("sending HTTP request {:?}", req);
- let s =
- match h3_conn.send_request(&mut conn, &req, body.is_none()) {
- Ok(v) => v,
+ h3_conn.send_request(&mut conn, &req, true).unwrap();
- Err(quiche::h3::Error::TransportError(
- quiche::Error::StreamLimit,
- )) => {
- debug!("not enough stream credits, retry later...");
- break;
- },
-
- Err(e) => {
- error!("failed to send request {:?}", e);
- break;
- },
- };
-
- if let Some(body) = &body {
- if let Err(e) = h3_conn.send_body(&mut conn, s, body, true) {
- error!("failed to send request body {:?}", e);
- break;
- }
- }
-
- reqs_done += 1;
+ req_sent = true;
}
-
- reqs_sent += reqs_done;
}
if let Some(http3_conn) = &mut http3_conn {
// Process HTTP/3 events.
loop {
match http3_conn.poll(&mut conn) {
- Ok((stream_id, quiche::h3::Event::Headers(headers))) => {
+ Ok((stream_id, quiche::h3::Event::Headers { list, .. })) => {
info!(
"got response headers {:?} on stream id {}",
- headers, stream_id
+ list, stream_id
);
},
@@ -351,30 +253,12 @@
},
Ok((_stream_id, quiche::h3::Event::Finished)) => {
- reqs_complete += 1;
-
- debug!(
- "{}/{} responses received",
- reqs_complete, reqs_count
+ info!(
+ "response received in {:?}, closing...",
+ req_start.elapsed()
);
- if reqs_complete == reqs_count {
- info!(
- "{}/{} response(s) received in {:?}, closing...",
- reqs_complete,
- reqs_count,
- req_start.elapsed()
- );
-
- match conn.close(true, 0x00, b"kthxbye") {
- // Already closed.
- Ok(_) | Err(quiche::Error::Done) => (),
-
- Err(e) => panic!("error closing conn: {:?}", e),
- }
-
- break;
- }
+ conn.close(true, 0x00, b"kthxbye").unwrap();
},
Err(quiche::h3::Error::Done) => {
@@ -423,12 +307,6 @@
if conn.is_closed() {
info!("connection closed, {:?}", conn.stats());
-
- if reqs_complete != reqs_count {
- error!("connection timed out after {:?} and only completed {}/{} requests",
- req_start.elapsed(), reqs_complete, reqs_count);
- }
-
break;
}
}
diff --git a/examples/http3-server.c b/examples/http3-server.c
index 4bb7a39..ef34e67 100644
--- a/examples/http3-server.c
+++ b/examples/http3-server.c
@@ -1,5 +1,4 @@
-// Copyright (C) 2018, Cloudflare, Inc.
-// Copyright (C) 2018, Alessandro Ghedini
+// Copyright (C) 2018-2019, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -25,6 +24,7 @@
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
@@ -255,7 +255,7 @@
HASH_FIND(hh, conns->h, dcid, dcid_len, conn_io);
if (conn_io == NULL) {
- if (version != QUICHE_PROTOCOL_VERSION) {
+ if (!quiche_version_is_supported(version)) {
fprintf(stderr, "version negotiation\n");
ssize_t written = quiche_negotiate_version(scid, scid_len,
@@ -423,9 +423,16 @@
flush_egress(loop, conn_io);
if (quiche_conn_is_closed(conn_io->conn)) {
+ quiche_stats stats;
+
+ quiche_conn_stats(conn_io->conn, &stats);
+ fprintf(stderr, "connection closed, recv=%zu sent=%zu lost=%zu rtt=%" PRIu64 "ns cwnd=%zu\n",
+ stats.recv, stats.sent, stats.lost, stats.rtt, stats.cwnd);
+
HASH_DELETE(hh, conns->h, conn_io);
ev_timer_stop(loop, &conn_io->timer);
+
quiche_conn_free(conn_io->conn);
free(conn_io);
}
@@ -441,7 +448,11 @@
flush_egress(loop, conn_io);
if (quiche_conn_is_closed(conn_io->conn)) {
- fprintf(stderr, "connection closed\n");
+ quiche_stats stats;
+
+ quiche_conn_stats(conn_io->conn, &stats);
+ fprintf(stderr, "connection closed, recv=%zu sent=%zu lost=%zu rtt=%" PRIu64 "ns cwnd=%zu\n",
+ stats.recv, stats.sent, stats.lost, stats.rtt, stats.cwnd);
HASH_DELETE(hh, conns->h, conn_io);
@@ -500,7 +511,7 @@
(uint8_t *) QUICHE_H3_APPLICATION_PROTOCOL,
sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1);
- quiche_config_set_idle_timeout(config, 5000);
+ quiche_config_set_max_idle_timeout(config, 5000);
quiche_config_set_max_packet_size(config, MAX_DATAGRAM_SIZE);
quiche_config_set_initial_max_data(config, 10000000);
quiche_config_set_initial_max_stream_data_bidi_local(config, 1000000);
@@ -509,6 +520,7 @@
quiche_config_set_initial_max_streams_bidi(config, 100);
quiche_config_set_initial_max_streams_uni(config, 100);
quiche_config_set_disable_active_migration(config, true);
+ quiche_config_set_cc_algorithm(config, QUICHE_CC_RENO);
http3_config = quiche_h3_config_new();
if (http3_config == NULL) {
diff --git a/examples/http3-server.rs b/examples/http3-server.rs
index 53273a5..3ee0f48 100644
--- a/examples/http3-server.rs
+++ b/examples/http3-server.rs
@@ -35,25 +35,6 @@
const MAX_DATAGRAM_SIZE: usize = 1350;
-const USAGE: &str = "Usage:
- http3-server [options]
- http3-server -h | --help
-
-Options:
- --listen <addr> Listen on the given IP:port [default: 127.0.0.1:4433]
- --cert <file> TLS certificate path [default: examples/cert.crt]
- --key <file> TLS certificate key path [default: examples/cert.key]
- --root <dir> Root directory [default: examples/root/]
- --name <str> Name of the server [default: quic.tech]
- --max-data BYTES Connection-wide flow control limit [default: 10000000].
- --max-stream-data BYTES Per-stream flow control limit [default: 1000000].
- --max-streams-bidi STREAMS Number of allowed concurrent streams [default: 100].
- --max-streams-uni STREAMS Number of allowed concurrent streams [default: 100].
- --no-retry Disable stateless retry.
- --no-grease Don't send GREASE.
- -h --help Show this screen.
-";
-
struct PartialResponse {
body: Vec<u8>,
@@ -61,7 +42,7 @@
}
struct Client {
- conn: Box<quiche::Connection>,
+ conn: std::pin::Pin<Box<quiche::Connection>>,
http3_conn: Option<quiche::h3::Connection>,
@@ -74,32 +55,22 @@
let mut buf = [0; 65535];
let mut out = [0; MAX_DATAGRAM_SIZE];
- env_logger::builder()
- .default_format_timestamp_nanos(true)
- .init();
+ let mut args = std::env::args();
- let args = docopt::Docopt::new(USAGE)
- .and_then(|dopt| dopt.parse())
- .unwrap_or_else(|e| e.exit());
+ let cmd = &args.next().unwrap();
- let max_data = args.get_str("--max-data");
- let max_data = u64::from_str_radix(max_data, 10).unwrap();
-
- let max_stream_data = args.get_str("--max-stream-data");
- let max_stream_data = u64::from_str_radix(max_stream_data, 10).unwrap();
-
- let max_streams_bidi = args.get_str("--max-streams-bidi");
- let max_streams_bidi = u64::from_str_radix(max_streams_bidi, 10).unwrap();
-
- let max_streams_uni = args.get_str("--max-streams-uni");
- let max_streams_uni = u64::from_str_radix(max_streams_uni, 10).unwrap();
+ if args.len() != 0 {
+ println!("Usage: {}", cmd);
+ println!("\nSee tools/apps/ for more complete implementations.");
+ return;
+ }
// Setup the event loop.
let poll = mio::Poll::new().unwrap();
let mut events = mio::Events::with_capacity(1024);
// Create the UDP listening socket, and register it with the event loop.
- let socket = net::UdpSocket::bind(args.get_str("--listen")).unwrap();
+ let socket = net::UdpSocket::bind("127.0.0.1:4433").unwrap();
let socket = mio::net::UdpSocket::from_socket(socket).unwrap();
poll.register(
@@ -114,36 +85,33 @@
let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
config
- .load_cert_chain_from_pem_file(args.get_str("--cert"))
+ .load_cert_chain_from_pem_file("examples/cert.crt")
.unwrap();
config
- .load_priv_key_from_pem_file(args.get_str("--key"))
+ .load_priv_key_from_pem_file("examples/cert.key")
.unwrap();
config
.set_application_protos(quiche::h3::APPLICATION_PROTOCOL)
.unwrap();
- config.set_idle_timeout(5000);
+ config.set_max_idle_timeout(5000);
config.set_max_packet_size(MAX_DATAGRAM_SIZE as u64);
- config.set_initial_max_data(max_data);
- config.set_initial_max_stream_data_bidi_local(max_stream_data);
- config.set_initial_max_stream_data_bidi_remote(max_stream_data);
- config.set_initial_max_stream_data_uni(max_stream_data);
- config.set_initial_max_streams_bidi(max_streams_bidi);
- config.set_initial_max_streams_uni(max_streams_uni);
+ config.set_initial_max_data(10_000_000);
+ config.set_initial_max_stream_data_bidi_local(1_000_000);
+ config.set_initial_max_stream_data_bidi_remote(1_000_000);
+ config.set_initial_max_stream_data_uni(1_000_000);
+ config.set_initial_max_streams_bidi(100);
+ config.set_initial_max_streams_uni(100);
config.set_disable_active_migration(true);
-
- if std::env::var_os("SSLKEYLOGFILE").is_some() {
- config.log_keys();
- }
-
- if args.get_bool("--no-grease") {
- config.grease(false);
- }
+ config.enable_early_data();
let h3_config = quiche::h3::Config::new().unwrap();
+ let rng = SystemRandom::new();
+ let conn_id_seed =
+ ring::hmac::Key::generate(ring::hmac::HMAC_SHA256, &rng).unwrap();
+
let mut clients = ClientMap::new();
loop {
@@ -203,20 +171,20 @@
trace!("got packet {:?}", hdr);
- if hdr.ty == quiche::Type::VersionNegotiation {
- error!("Version negotiation invalid on the server");
- continue;
- }
+ let conn_id = ring::hmac::sign(&conn_id_seed, &hdr.dcid);
+ let conn_id = &conn_id.as_ref()[..quiche::MAX_CONN_ID_LEN];
// Lookup a connection based on the packet's connection ID. If there
// is no connection matching, create a new one.
- let (_, client) = if !clients.contains_key(&hdr.dcid) {
+ let (_, client) = if !clients.contains_key(&hdr.dcid) &&
+ !clients.contains_key(conn_id)
+ {
if hdr.ty != quiche::Type::Initial {
error!("Packet is not Initial");
continue;
}
- if hdr.version != quiche::PROTOCOL_VERSION {
+ if !quiche::version_is_supported(hdr.version) {
warn!("Doing version negotiation");
let len =
@@ -236,58 +204,53 @@
continue;
}
- // Generate a random source connection ID for the connection.
let mut scid = [0; quiche::MAX_CONN_ID_LEN];
- SystemRandom::new().fill(&mut scid[..]).unwrap();
+ scid.copy_from_slice(&conn_id);
- let mut odcid = None;
+ // Token is always present in Initial packets.
+ let token = hdr.token.as_ref().unwrap();
- if !args.get_bool("--no-retry") {
- // Token is always present in Initial packets.
- let token = hdr.token.as_ref().unwrap();
+ // Do stateless retry if the client didn't send a token.
+ if token.is_empty() {
+ warn!("Doing stateless retry");
- // Do stateless retry if the client didn't send a token.
- if token.is_empty() {
- warn!("Doing stateless retry");
+ let new_token = mint_token(&hdr, &src);
- let new_token = mint_token(&hdr, &src);
+ let len = quiche::retry(
+ &hdr.scid, &hdr.dcid, &scid, &new_token, &mut out,
+ )
+ .unwrap();
+ let out = &out[..len];
- let len = quiche::retry(
- &hdr.scid, &hdr.dcid, &scid, &new_token, &mut out,
- )
- .unwrap();
- let out = &out[..len];
-
- if let Err(e) = socket.send_to(out, &src) {
- if e.kind() == std::io::ErrorKind::WouldBlock {
- debug!("send() would block");
- break;
- }
-
- panic!("send() failed: {:?}", e);
+ if let Err(e) = socket.send_to(out, &src) {
+ if e.kind() == std::io::ErrorKind::WouldBlock {
+ debug!("send() would block");
+ break;
}
- continue;
+
+ panic!("send() failed: {:?}", e);
}
-
- odcid = validate_token(&src, token);
-
- // The token was not valid, meaning the retry failed, so
- // drop the packet.
- if odcid == None {
- error!("Invalid address validation token");
- continue;
- }
-
- if scid.len() != hdr.dcid.len() {
- error!("Invalid destination connection ID");
- continue;
- }
-
- // Reuse the source connection ID we sent in the Retry
- // packet, instead of changing it again.
- scid.copy_from_slice(&hdr.dcid);
+ continue;
}
+ let odcid = validate_token(&src, token);
+
+ // The token was not valid, meaning the retry failed, so
+ // drop the packet.
+ if odcid == None {
+ error!("Invalid address validation token");
+ continue;
+ }
+
+ if scid.len() != hdr.dcid.len() {
+ error!("Invalid destination connection ID");
+ continue;
+ }
+
+ // Reuse the source connection ID we sent in the Retry
+ // packet, instead of changing it again.
+ scid.copy_from_slice(&hdr.dcid);
+
debug!(
"New connection: dcid={} scid={}",
hex_dump(&hdr.dcid),
@@ -306,7 +269,11 @@
clients.get_mut(&scid[..]).unwrap()
} else {
- clients.get_mut(&hdr.dcid).unwrap()
+ match clients.get_mut(&hdr.dcid) {
+ Some(v) => v,
+
+ None => clients.get_mut(conn_id).unwrap(),
+ }
};
// Process potentially coalesced packets.
@@ -328,7 +295,9 @@
// Create a new HTTP/3 connection as soon as the QUIC connection
// is established.
- if client.conn.is_established() && client.http3_conn.is_none() {
+ if (client.conn.is_in_early_data() || client.conn.is_established()) &&
+ client.http3_conn.is_none()
+ {
debug!(
"{} QUIC handshake completed, now trying HTTP/3",
client.conn.trace_id()
@@ -360,13 +329,16 @@
loop {
let http3_conn = client.http3_conn.as_mut().unwrap();
- match http3_conn.poll(client.conn.as_mut()) {
- Ok((stream_id, quiche::h3::Event::Headers(headers))) => {
+ match http3_conn.poll(&mut client.conn) {
+ Ok((
+ stream_id,
+ quiche::h3::Event::Headers { list, .. },
+ )) => {
handle_request(
client,
stream_id,
- &headers,
- args.get_str("--root"),
+ &list,
+ "examples/root",
);
},
diff --git a/examples/qpack-decode.rs b/examples/qpack-decode.rs
index 6a2714b..832f81a 100644
--- a/examples/qpack-decode.rs
+++ b/examples/qpack-decode.rs
@@ -33,24 +33,19 @@
use quiche::h3::qpack;
-const USAGE: &str = "Usage:
- qpack-decode [options] FILE
- qpack-decode -h | --help
-
-Options:
- -h --help Show this screen.
-";
-
fn main() {
- env_logger::init();
-
- let args = docopt::Docopt::new(USAGE)
- .and_then(|dopt| dopt.parse())
- .unwrap_or_else(|e| e.exit());
-
// TODO: parse params from file name.
- let mut file = File::open(args.get_str("FILE")).unwrap();
+ let mut args = std::env::args();
+
+ let cmd = &args.next().unwrap();
+
+ if args.len() != 1 {
+ println!("Usage: {} FILE", cmd);
+ return;
+ }
+
+ let mut file = File::open(&args.next().unwrap()).unwrap();
let mut dec = qpack::Decoder::new();
diff --git a/examples/qpack-encode.rs b/examples/qpack-encode.rs
index 4b1e50d..7484210 100644
--- a/examples/qpack-encode.rs
+++ b/examples/qpack-encode.rs
@@ -34,22 +34,17 @@
use quiche::h3;
-const USAGE: &str = "Usage:
- qpack-encode [options] FILE
- qpack-encode -h | --help
-
-Options:
- -h --help Show this screen.
-";
-
fn main() {
- env_logger::init();
+ let mut args = std::env::args();
- let args = docopt::Docopt::new(USAGE)
- .and_then(|dopt| dopt.parse())
- .unwrap_or_else(|e| e.exit());
+ let cmd = &args.next().unwrap();
- let file = File::open(args.get_str("FILE")).unwrap();
+ if args.len() != 1 {
+ println!("Usage: {} FILE", cmd);
+ return;
+ }
+
+ let file = File::open(&args.next().unwrap()).unwrap();
let file = BufReader::new(&file);
let mut enc = h3::qpack::Encoder::new();
diff --git a/examples/server.c b/examples/server.c
index 2b473df..e293d4a 100644
--- a/examples/server.c
+++ b/examples/server.c
@@ -1,5 +1,4 @@
-// Copyright (C) 2018, Cloudflare, Inc.
-// Copyright (C) 2018, Alessandro Ghedini
+// Copyright (C) 2018-2019, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -244,7 +243,7 @@
HASH_FIND(hh, conns->h, dcid, dcid_len, conn_io);
if (conn_io == NULL) {
- if (version != QUICHE_PROTOCOL_VERSION) {
+ if (!quiche_version_is_supported(version)) {
fprintf(stderr, "version negotiation\n");
ssize_t written = quiche_negotiate_version(scid, scid_len,
@@ -360,6 +359,12 @@
flush_egress(loop, conn_io);
if (quiche_conn_is_closed(conn_io->conn)) {
+ quiche_stats stats;
+
+ quiche_conn_stats(conn_io->conn, &stats);
+ fprintf(stderr, "connection closed, recv=%zu sent=%zu lost=%zu rtt=%" PRIu64 "ns cwnd=%zu\n",
+ stats.recv, stats.sent, stats.lost, stats.rtt, stats.cwnd);
+
HASH_DELETE(hh, conns->h, conn_io);
ev_timer_stop(loop, &conn_io->timer);
@@ -378,7 +383,11 @@
flush_egress(loop, conn_io);
if (quiche_conn_is_closed(conn_io->conn)) {
- fprintf(stderr, "connection closed\n");
+ quiche_stats stats;
+
+ quiche_conn_stats(conn_io->conn, &stats);
+ fprintf(stderr, "connection closed, recv=%zu sent=%zu lost=%zu rtt=%" PRIu64 "ns cwnd=%zu\n",
+ stats.recv, stats.sent, stats.lost, stats.rtt, stats.cwnd);
HASH_DELETE(hh, conns->h, conn_io);
@@ -434,14 +443,15 @@
quiche_config_load_priv_key_from_pem_file(config, "examples/cert.key");
quiche_config_set_application_protos(config,
- (uint8_t *) "\x05hq-23\x08http/0.9", 15);
+ (uint8_t *) "\x05hq-25\x05hq-24\x05hq-23\x08http/0.9", 21);
- quiche_config_set_idle_timeout(config, 5000);
+ quiche_config_set_max_idle_timeout(config, 5000);
quiche_config_set_max_packet_size(config, MAX_DATAGRAM_SIZE);
quiche_config_set_initial_max_data(config, 10000000);
quiche_config_set_initial_max_stream_data_bidi_local(config, 1000000);
quiche_config_set_initial_max_stream_data_bidi_remote(config, 1000000);
quiche_config_set_initial_max_streams_bidi(config, 100);
+ quiche_config_set_cc_algorithm(config, QUICHE_CC_RENO);
struct connections c;
c.sock = sock;
diff --git a/examples/server.rs b/examples/server.rs
index c29f908..944d810 100644
--- a/examples/server.rs
+++ b/examples/server.rs
@@ -1,5 +1,4 @@
-// Copyright (C) 2018, Cloudflare, Inc.
-// Copyright (C) 2018, Alessandro Ghedini
+// Copyright (C) 2018-2019, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -30,33 +29,12 @@
use std::net;
-use std::io::prelude::*;
-
use std::collections::HashMap;
use ring::rand::*;
const MAX_DATAGRAM_SIZE: usize = 1350;
-const USAGE: &str = "Usage:
- server [options]
- server -h | --help
-
-Options:
- --listen <addr> Listen on the given IP:port [default: 127.0.0.1:4433]
- --cert <file> TLS certificate path [default: examples/cert.crt]
- --key <file> TLS certificate key path [default: examples/cert.key]
- --root <dir> Root directory [default: examples/root/]
- --name <str> Name of the server [default: quic.tech]
- --max-data BYTES Connection-wide flow control limit [default: 10000000].
- --max-stream-data BYTES Per-stream flow control limit [default: 1000000].
- --max-streams-bidi STREAMS Number of allowed concurrent streams [default: 100].
- --max-streams-uni STREAMS Number of allowed concurrent streams [default: 100].
- --dump-packets PATH Dump the incoming packets as files in the given directory.
- --no-retry Disable stateless retry.
- -h --help Show this screen.
-";
-
struct PartialResponse {
body: Vec<u8>,
@@ -64,7 +42,7 @@
}
struct Client {
- conn: Box<quiche::Connection>,
+ conn: std::pin::Pin<Box<quiche::Connection>>,
partial_responses: HashMap<u64, PartialResponse>,
}
@@ -75,38 +53,22 @@
let mut buf = [0; 65535];
let mut out = [0; MAX_DATAGRAM_SIZE];
- env_logger::builder()
- .default_format_timestamp_nanos(true)
- .init();
+ let mut args = std::env::args();
- let args = docopt::Docopt::new(USAGE)
- .and_then(|dopt| dopt.parse())
- .unwrap_or_else(|e| e.exit());
+ let cmd = &args.next().unwrap();
- let max_data = args.get_str("--max-data");
- let max_data = u64::from_str_radix(max_data, 10).unwrap();
-
- let max_stream_data = args.get_str("--max-stream-data");
- let max_stream_data = u64::from_str_radix(max_stream_data, 10).unwrap();
-
- let max_streams_bidi = args.get_str("--max-streams-bidi");
- let max_streams_bidi = u64::from_str_radix(max_streams_bidi, 10).unwrap();
-
- let max_streams_uni = args.get_str("--max-streams-uni");
- let max_streams_uni = u64::from_str_radix(max_streams_uni, 10).unwrap();
-
- let dump_path = if args.get_str("--dump-packets") != "" {
- Some(args.get_str("--dump-packets"))
- } else {
- None
- };
+ if args.len() != 0 {
+ println!("Usage: {}", cmd);
+ println!("\nSee tools/apps/ for more complete implementations.");
+ return;
+ }
// Setup the event loop.
let poll = mio::Poll::new().unwrap();
let mut events = mio::Events::with_capacity(1024);
// Create the UDP listening socket, and register it with the event loop.
- let socket = net::UdpSocket::bind(args.get_str("--listen")).unwrap();
+ let socket = net::UdpSocket::bind("127.0.0.1:4433").unwrap();
let socket = mio::net::UdpSocket::from_socket(socket).unwrap();
poll.register(
@@ -121,34 +83,33 @@
let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
config
- .load_cert_chain_from_pem_file(args.get_str("--cert"))
+ .load_cert_chain_from_pem_file("examples/cert.crt")
.unwrap();
config
- .load_priv_key_from_pem_file(args.get_str("--key"))
+ .load_priv_key_from_pem_file("examples/cert.key")
.unwrap();
config
- .set_application_protos(b"\x05hq-23\x08http/0.9")
+ .set_application_protos(b"\x05hq-25\x05hq-24\x05hq-23\x08http/0.9")
.unwrap();
- config.set_idle_timeout(5000);
+ config.set_max_idle_timeout(5000);
config.set_max_packet_size(MAX_DATAGRAM_SIZE as u64);
- config.set_initial_max_data(max_data);
- config.set_initial_max_stream_data_bidi_local(max_stream_data);
- config.set_initial_max_stream_data_bidi_remote(max_stream_data);
- config.set_initial_max_stream_data_uni(max_stream_data);
- config.set_initial_max_streams_bidi(max_streams_bidi);
- config.set_initial_max_streams_uni(max_streams_uni);
+ config.set_initial_max_data(10_000_000);
+ config.set_initial_max_stream_data_bidi_local(1_000_000);
+ config.set_initial_max_stream_data_bidi_remote(1_000_000);
+ config.set_initial_max_stream_data_uni(1_000_000);
+ config.set_initial_max_streams_bidi(100);
+ config.set_initial_max_streams_uni(100);
config.set_disable_active_migration(true);
+ config.enable_early_data();
- if std::env::var_os("SSLKEYLOGFILE").is_some() {
- config.log_keys();
- }
+ let rng = SystemRandom::new();
+ let conn_id_seed =
+ ring::hmac::Key::generate(ring::hmac::HMAC_SHA256, &rng).unwrap();
let mut clients = ClientMap::new();
- let mut pkt_count = 0;
-
loop {
// Find the shorter timeout from all the active connections.
//
@@ -191,17 +152,6 @@
let pkt_buf = &mut buf[..len];
- if let Some(target_path) = dump_path {
- let path = format!("{}/{}.pkt", target_path, pkt_count);
-
- if let Ok(f) = std::fs::File::create(&path) {
- let mut f = std::io::BufWriter::new(f);
- f.write_all(pkt_buf).ok();
- }
- }
-
- pkt_count += 1;
-
// Parse the QUIC packet's header.
let hdr = match quiche::Header::from_slice(
pkt_buf,
@@ -217,20 +167,20 @@
trace!("got packet {:?}", hdr);
- if hdr.ty == quiche::Type::VersionNegotiation {
- error!("Version negotiation invalid on the server");
- continue;
- }
+ let conn_id = ring::hmac::sign(&conn_id_seed, &hdr.dcid);
+ let conn_id = &conn_id.as_ref()[..quiche::MAX_CONN_ID_LEN];
// Lookup a connection based on the packet's connection ID. If there
// is no connection matching, create a new one.
- let (_, client) = if !clients.contains_key(&hdr.dcid) {
+ let (_, client) = if !clients.contains_key(&hdr.dcid) &&
+ !clients.contains_key(conn_id)
+ {
if hdr.ty != quiche::Type::Initial {
error!("Packet is not Initial");
continue;
}
- if hdr.version != quiche::PROTOCOL_VERSION {
+ if !quiche::version_is_supported(hdr.version) {
warn!("Doing version negotiation");
let len =
@@ -250,59 +200,54 @@
continue;
}
- // Generate a random source connection ID for the connection.
let mut scid = [0; quiche::MAX_CONN_ID_LEN];
- SystemRandom::new().fill(&mut scid[..]).unwrap();
+ scid.copy_from_slice(&conn_id);
- let mut odcid = None;
+ // Token is always present in Initial packets.
+ let token = hdr.token.as_ref().unwrap();
- if !args.get_bool("--no-retry") {
- // Token is always present in Initial packets.
- let token = hdr.token.as_ref().unwrap();
+ // Do stateless retry if the client didn't send a token.
+ if token.is_empty() {
+ warn!("Doing stateless retry");
- // Do stateless retry if the client didn't send a token.
- if token.is_empty() {
- warn!("Doing stateless retry");
+ let new_token = mint_token(&hdr, &src);
- let new_token = mint_token(&hdr, &src);
+ let len = quiche::retry(
+ &hdr.scid, &hdr.dcid, &scid, &new_token, &mut out,
+ )
+ .unwrap();
- let len = quiche::retry(
- &hdr.scid, &hdr.dcid, &scid, &new_token, &mut out,
- )
- .unwrap();
+ let out = &out[..len];
- let out = &out[..len];
-
- if let Err(e) = socket.send_to(out, &src) {
- if e.kind() == std::io::ErrorKind::WouldBlock {
- debug!("send() would block");
- break;
- }
-
- panic!("send() failed: {:?}", e);
+ if let Err(e) = socket.send_to(out, &src) {
+ if e.kind() == std::io::ErrorKind::WouldBlock {
+ debug!("send() would block");
+ break;
}
- continue;
+
+ panic!("send() failed: {:?}", e);
}
-
- odcid = validate_token(&src, token);
-
- // The token was not valid, meaning the retry failed, so
- // drop the packet.
- if odcid == None {
- error!("Invalid address validation token");
- continue;
- }
-
- if scid.len() != hdr.dcid.len() {
- error!("Invalid destination connection ID");
- continue;
- }
-
- // Reuse the source connection ID we sent in the Retry
- // packet, instead of changing it again.
- scid.copy_from_slice(&hdr.dcid);
+ continue;
}
+ let odcid = validate_token(&src, token);
+
+ // The token was not valid, meaning the retry failed, so
+ // drop the packet.
+ if odcid == None {
+ error!("Invalid address validation token");
+ continue;
+ }
+
+ if scid.len() != hdr.dcid.len() {
+ error!("Invalid destination connection ID");
+ continue;
+ }
+
+ // Reuse the source connection ID we sent in the Retry
+ // packet, instead of changing it again.
+ scid.copy_from_slice(&hdr.dcid);
+
debug!(
"New connection: dcid={} scid={}",
hex_dump(&hdr.dcid),
@@ -320,7 +265,11 @@
clients.get_mut(&scid[..]).unwrap()
} else {
- clients.get_mut(&hdr.dcid).unwrap()
+ match clients.get_mut(&hdr.dcid) {
+ Some(v) => v,
+
+ None => clients.get_mut(conn_id).unwrap(),
+ }
};
// Process potentially coalesced packets.
@@ -340,7 +289,7 @@
debug!("{} processed {} bytes", client.conn.trace_id(), read);
- if client.conn.is_established() {
+ if client.conn.is_in_early_data() || client.conn.is_established() {
// Handle writable streams.
for stream_id in client.conn.writable() {
handle_writable(client, stream_id);
@@ -367,12 +316,7 @@
fin
);
- handle_stream(
- client,
- s,
- stream_buf,
- args.get_str("--root"),
- );
+ handle_stream(client, s, stream_buf, "examples/root");
}
}
}
diff --git a/extras/docker/Dockerfile b/extras/docker/Dockerfile
new file mode 100644
index 0000000..000a587
--- /dev/null
+++ b/extras/docker/Dockerfile
@@ -0,0 +1,55 @@
+FROM rust:1.39 as build
+
+WORKDIR /build
+
+RUN apt-get update && apt-get install -y git cmake golang && \
+ rm -rf /var/lib/apt/lists/*
+
+RUN git clone --recurse-submodules --depth 1 https://github.com/cloudflare/quiche
+RUN cd quiche && \
+ cargo build --release --examples
+RUN cd quiche && \
+ cargo build --manifest-path tools/apps/Cargo.toml --release
+
+##
+## quiche-base: base quiche image
+##
+FROM debian:latest as quiche-base
+RUN apt-get update && apt-get install -y ca-certificates && \
+ rm -rf /var/lib/apt/lists/*
+RUN update-ca-certificates
+COPY --from=build /build/quiche/target/release/examples/http3-client \
+ /build/quiche/target/release/examples/http3-server \
+ /build/quiche/target/release/examples/client \
+ /build/quiche/target/release/examples/server \
+ /build/quiche/tools/apps/target/release/quiche-client \
+ /build/quiche/tools/apps/target/release/quiche-server \
+ /usr/local/bin/
+ENV PATH="/usr/local/bin/:${PATH}"
+ENV RUST_LOG=info
+
+##
+## quiche-qns: quiche build image for quic-interop-runner
+## https://github.com/marten-seemann/quic-network-simulator
+## https://github.com/marten-seemann/quic-interop-runner
+##
+
+# setup interop image
+FROM martenseemann/quic-network-simulator-endpoint:latest as quiche-qns
+
+WORKDIR /quiche
+
+# copy binaries and sample certificate for server
+COPY --from=build /build/quiche/examples/cert.crt \
+ /build/quiche/examples/cert.key \
+ examples/
+COPY --from=build /build/quiche/tools/apps/target/release/quiche-client \
+ /build/quiche/tools/apps/target/release/quiche-server \
+ ./
+ENV RUST_LOG=trace
+
+# copy interop test script
+COPY qns/run_endpoint.sh .
+RUN chmod +x run_endpoint.sh
+
+ENTRYPOINT [ "./run_endpoint.sh" ]
diff --git a/extras/docker/Makefile b/extras/docker/Makefile
new file mode 100644
index 0000000..3809abc
--- /dev/null
+++ b/extras/docker/Makefile
@@ -0,0 +1,45 @@
+#
+# Simple Makefile for building docker images
+#
+QNSDIR = ./qns
+DOCKER = docker
+
+BASE_REPO = cloudflare/quiche
+BASE_TAG = latest
+
+QNS_REPO = cloudflare/quiche-qns
+QNS_TAG = latest
+
+BUILD_TAG = base-build
+
+.PHONY: build base qns test publish clean
+
+all: build base qns
+
+# build quiche only
+build: Dockerfile
+ $(DOCKER) build --target build -t $(BASE_REPO):$(BUILD_TAG) .
+
+# run cargo test
+test: build
+ $(DOCKER) run --rm -w /build/quiche $(BASE_REPO):$(BUILD_TAG) cargo test
+
+# build base image
+base: Dockerfile
+ $(DOCKER) build --target quiche-base -t $(BASE_REPO):$(BASE_TAG) .
+
+# build qns image
+qns: Dockerfile qns/run_endpoint.sh
+ $(DOCKER) build --target quiche-qns -t $(QNS_REPO):$(QNS_TAG) .
+
+test: base
+
+publish: base qns
+ $(DOCKER) push $(BASE_REPO):$(BASE_TAG)
+ $(DOCKER) push $(QNS_REPO):$(QNS_TAG)
+
+clean:
+ @for id in `$(DOCKER) images -q $(BASE_REPO)` `$(DOCKER) images -q $(QNS_REPO)`; do \
+ echo ">> Removing $$id"; \
+ $(DOCKER) rmi -f $$id; \
+ done
diff --git a/extras/docker/README.md b/extras/docker/README.md
new file mode 100644
index 0000000..b4bca62
--- /dev/null
+++ b/extras/docker/README.md
@@ -0,0 +1,36 @@
+# Docker images
+
+This directory contains a sample Dockerfile to build quiche.
+
+## How to build
+
+In order to build the Docker images, simply run the following command in this directory:
+
+```
+$ make all
+```
+
+## How to test
+
+In order to test the Docker images, simply run the following command in this directory:
+
+```
+$ make test
+```
+
+# Docker Hub Repositories
+
+You can find the quiche Docker images on the following Docker Hub repositories:
+
+- [cloudflare/quiche](https://hub.docker.com/repository/docker/cloudflare/quiche)
+- [cloudflare/quiche-qns](https://hub.docker.com/repository/docker/cloudflare/quiche-qns)
+
+Tag `latest` will be updated when quiche master branch updates.
+
+## cloudflare/quiche
+
+This includes an example server and client installed in /usr/local/bin.
+
+## cloudflare/quiche-qns
+
+Docker files for https://github.com/marten-seemann/quic-interop-runner
diff --git a/extras/docker/qns/run_endpoint.sh b/extras/docker/qns/run_endpoint.sh
new file mode 100644
index 0000000..0a1a38e
--- /dev/null
+++ b/extras/docker/qns/run_endpoint.sh
@@ -0,0 +1,82 @@
+#!/bin/bash
+set -e
+
+# Set up the routing needed for the simulation
+/setup.sh
+
+# The following variables are available for use:
+# - ROLE contains the role of this execution context, client or server
+# - SERVER_PARAMS contains user-supplied command line parameters
+# - CLIENT_PARAMS contains user-supplied command line parameters
+
+QUICHE_DIR=/quiche
+WWW_DIR=/www
+DOWNLOAD_DIR=/downloads
+QUICHE_CLIENT=quiche-client
+QUICHE_SERVER=quiche-server
+QUICHE_CLIENT_OPT="--no-verify --dump-responses ${DOWNLOAD_DIR}"
+QUICHE_SERVER_OPT="--no-retry --cert examples/cert.crt --key examples/cert.key"
+LOG_DIR=/logs
+LOG=$LOG_DIR/log.txt
+
+check_testcase () {
+ TESTNAME=$1
+
+ case $1 in
+ handshake | resumption | multiconnect )
+ echo "supported"
+ ;;
+ transfer )
+ echo "supported"
+ RUST_LOG="info"
+ ;;
+ retry )
+ echo "supported"
+ QUICHE_SERVER_OPT=""
+ ;;
+ http3 )
+ echo "supported"
+ ;;
+ *)
+ echo "unsupported"
+ exit 127
+ ;;
+ esac
+}
+
+run_quiche_client_tests () {
+ # TODO: https://github.com/marten-seemann/quic-interop-runner/issues/61
+ # remove this sleep when the issue above is resolved.
+ sleep 3
+ $QUICHE_DIR/$QUICHE_CLIENT $QUICHE_CLIENT_OPT \
+ $CLIENT_PARAMS $REQUESTS >& $LOG
+
+}
+
+run_quiche_server_tests() {
+ $QUICHE_DIR/$QUICHE_SERVER --listen 0.0.0.0:443 --root $WWW_DIR \
+ $SERVER_PARAMS $QUICHE_SERVER_OPT >& $LOG
+}
+
+# Update config based on test case
+check_testcase $TESTCASE
+
+# Create quiche log directory
+mkdir -p $LOG_DIR
+
+if [ "$ROLE" == "client" ]; then
+ # Wait for the simulator to start up.
+ /wait-for-it.sh sim:57832 -s -t 30
+ echo "## Starting quiche client..."
+ echo "## Client params: $CLIENT_PARAMS"
+ echo "## Requests: $REQUESTS"
+ echo "## Test case: $TESTCASE"
+ run_quiche_client_tests
+elif [ "$ROLE" == "server" ]; then
+ # Wait for the simulator to start up.
+ /wait-for-it.sh sim:57832 -s -t 30
+ echo "## Starting quiche server..."
+ echo "## Server params: $SERVER_PARAMS"
+ echo "## Test case: $TESTCASE"
+ run_quiche_server_tests
+fi
diff --git a/extras/nginx/README.md b/extras/nginx/README.md
new file mode 100644
index 0000000..848710a
--- /dev/null
+++ b/extras/nginx/README.md
@@ -0,0 +1,167 @@
+Requirements
+------------
+
+Note that in addition to the dependencies required for [building quiche](https://github.com/cloudflare/quiche#building)
+you'll also need any dependency that might be required for [building NGINX](https://nginx.org/en/docs/configure.html)
+itself.
+
+NGINX will need to be built against BoringSSL, not OpenSSL, to work properly
+with quiche. This is done automatically during the build process described below.
+
+Once [OpenSSL merges support for QUIC](https://github.com/openssl/openssl/pull/8797)
+it will be possible to build using OpenSSL as well.
+
+Building
+--------
+
+The first step is to [download and unpack the NGINX source
+code](https://nginx.org/en/download.html). Note that the HTTP/3 and QUIC patch
+only works with the 1.16.x release branch (the latest stable release being
+1.16.1).
+
+```
+ % curl -O https://nginx.org/download/nginx-1.16.1.tar.gz
+ % tar xzvf nginx-1.16.1.tar.gz
+```
+
+As well as quiche, the underlying implementation of HTTP/3 and QUIC:
+```
+ % git clone --recursive https://github.com/cloudflare/quiche
+```
+
+Next you’ll need to apply the patch to NGINX:
+```
+ % cd nginx-1.16.1
+ % patch -p01 < ../quiche/extras/nginx/nginx-1.16.patch
+```
+
+And finally build NGINX with HTTP/3 support enabled:
+```
+ % ./configure \
+ --prefix=$PWD \
+ --build="quiche-$(git --git-dir=../quiche/.git rev-parse --short HEAD)" \
+ --with-http_ssl_module \
+ --with-http_v2_module \
+ --with-http_v3_module \
+ --with-openssl=../quiche/deps/boringssl \
+ --with-quiche=../quiche
+ % make
+```
+
+The (optional) `--build` argument will embed the latest commit hash from the
+quiche repository in the NGINX binary, so that it can be retrieved when running
+`nginx -V`. The `nginx -V` output can also be provided when submiiting bug/issue
+requests to help with triage.
+
+```
+nginx -V
+nginx version: nginx/1.16.1 (quiche-a0e69ed)
+built with OpenSSL 1.1.0 (compatible; BoringSSL) (running with BoringSSL)
+```
+
+The above command instructs the NGINX build system to enable the HTTP/3 support
+(`--with-http_v3_module`) by using the quiche library found in the path it was
+previously downloaded into (`--with-quiche=../quiche`).
+
+Running
+-------
+
+The following configuration file can be used as a starting point to enable
+HTTP/3 support:
+
+```
+events {
+ worker_connections 1024;
+}
+
+http {
+ server {
+ # Enable QUIC and HTTP/3.
+ listen 443 quic reuseport;
+
+ # Enable HTTP/2 (optional).
+ listen 443 ssl http2;
+
+ ssl_certificate cert.crt;
+ ssl_certificate_key cert.key;
+
+ # Enable all TLS versions (TLSv1.3 is required for QUIC).
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
+
+ # Add Alt-Svc header to negotiate HTTP/3.
+ add_header alt-svc 'h3-25=":443"; ma=86400';
+ }
+}
+```
+
+List of configuration directives
+--------------------------------
+
+### http3_max_concurrent_streams
+
+**syntax:** **http3_max_concurrent_streams** *number*;
+
+**default:** *http3_max_concurrent_streams 128;*
+
+**context:** *http*, *server*
+
+Limits the maximum number of concurrent HTTP/3 streams in a connection.
+
+### http3_max_requests
+
+**syntax:** **http3_max_requests** *number*;
+
+**default:** *http3_max_requests 1000;*
+
+**context:** *http*, *server*
+
+Limits the maximum number of requests that can be served on a single HTTP/3
+connection, after which the next client request will lead to connection closing
+and the need of establishing a new connection.
+
+### http3_max_header_size
+
+**syntax:** **http3_max_header_size** *size*;
+
+**default:** *http3_max_header_size 16k;*
+
+**context:** *http*, *server*
+
+Limits the maximum size of the entire request header list after QPACK decompression.
+
+### http3_initial_max_data
+
+**syntax:** **http3_initial_max_data** *size*;
+
+**default:** *http3_initial_max_data 10m;*
+
+**context:** *http*, *server*
+
+Sets the per-connection incoming flow control limit.
+
+### http3_initial_max_stream_data
+
+**syntax:** **http3_initial_max_stream_data** *size*;
+
+**default:** *http3_initial_max_stream_data 1m;*
+
+**context:** *http*, *server*
+
+Sets the per-stream incoming flow control limit.
+
+### http3_idle_timeout
+
+**syntax:** **http3_idle_timeout** *time*;
+
+**default:** *http3_idle_timeout 3m;*
+
+**context:** *http*, *server*
+
+Sets the timeout of inactivity after which the connection is closed.
+
+List of variables
+-----------------
+
+### $http3
+
+"h3" if HTTP/3 was negotiated, or an empty string otherwise.
diff --git a/extras/nginx/nginx-1.16.patch b/extras/nginx/nginx-1.16.patch
new file mode 100644
index 0000000..081f687
--- /dev/null
+++ b/extras/nginx/nginx-1.16.patch
@@ -0,0 +1,4148 @@
+From c4a58f3c04c5393f572bb0e060067163af9a0ecd Mon Sep 17 00:00:00 2001
+From: Alessandro Ghedini <alessandro@cloudflare.com>
+Date: Thu, 10 Oct 2019 17:06:08 +0100
+Subject: [PATCH] Initial QUIC and HTTP/3 implementation using quiche
+
+---
+ auto/lib/conf | 4 +
+ auto/lib/make | 4 +
+ auto/lib/openssl/make | 12 +-
+ auto/lib/quiche/conf | 19 +
+ auto/lib/quiche/make | 22 +
+ auto/make | 3 +-
+ auto/modules | 44 +
+ auto/options | 9 +
+ src/core/ngx_connection.h | 7 +
+ src/core/ngx_core.h | 3 +
+ src/event/ngx_event_quic.c | 581 +++++++
+ src/event/ngx_event_quic.h | 49 +
+ src/event/ngx_event_udp.c | 8 +
+ src/http/modules/ngx_http_ssl_module.c | 13 +-
+ src/http/ngx_http.c | 33 +-
+ src/http/ngx_http.h | 4 +
+ src/http/ngx_http_core_module.c | 7 +
+ src/http/ngx_http_core_module.h | 3 +
+ src/http/ngx_http_request.c | 143 +-
+ src/http/ngx_http_request.h | 3 +
+ src/http/ngx_http_request_body.c | 29 +
+ src/http/ngx_http_upstream.c | 13 +
+ src/http/v3/ngx_http_v3.c | 2094 +++++++++++++++++++++++
+ src/http/v3/ngx_http_v3.h | 76 +
+ src/http/v3/ngx_http_v3_filter_module.c | 68 +
+ src/http/v3/ngx_http_v3_module.c | 286 ++++
+ src/http/v3/ngx_http_v3_module.h | 34 +
+ 27 files changed, 3560 insertions(+), 11 deletions(-)
+ create mode 100644 auto/lib/quiche/conf
+ create mode 100644 auto/lib/quiche/make
+ create mode 100644 src/event/ngx_event_quic.c
+ create mode 100644 src/event/ngx_event_quic.h
+ create mode 100644 src/http/v3/ngx_http_v3.c
+ create mode 100644 src/http/v3/ngx_http_v3.h
+ create mode 100644 src/http/v3/ngx_http_v3_filter_module.c
+ create mode 100644 src/http/v3/ngx_http_v3_module.c
+ create mode 100644 src/http/v3/ngx_http_v3_module.h
+
+diff --git a/auto/lib/conf b/auto/lib/conf
+index 2c7af1040..abf920bae 100644
+--- a/auto/lib/conf
++++ b/auto/lib/conf
+@@ -25,6 +25,10 @@ if [ $USE_OPENSSL = YES ]; then
+ . auto/lib/openssl/conf
+ fi
+
++if [ $USE_QUICHE = YES ]; then
++ . auto/lib/quiche/conf
++fi
++
+ if [ $USE_ZLIB = YES ]; then
+ . auto/lib/zlib/conf
+ fi
+diff --git a/auto/lib/make b/auto/lib/make
+index b64e32908..c8f34ae2e 100644
+--- a/auto/lib/make
++++ b/auto/lib/make
+@@ -11,6 +11,10 @@ if [ $OPENSSL != NONE -a $OPENSSL != NO -a $OPENSSL != YES ]; then
+ . auto/lib/openssl/make
+ fi
+
++if [ $QUICHE != NONE -a $QUICHE != NO -a $QUICHE != YES ]; then
++ . auto/lib/quiche/make
++fi
++
+ if [ $ZLIB != NONE -a $ZLIB != NO -a $ZLIB != YES ]; then
+ . auto/lib/zlib/make
+ fi
+diff --git a/auto/lib/openssl/make b/auto/lib/openssl/make
+index 126a23875..3af2ae557 100644
+--- a/auto/lib/openssl/make
++++ b/auto/lib/openssl/make
+@@ -49,11 +49,13 @@ END
+ cat << END >> $NGX_MAKEFILE
+
+ $OPENSSL/.openssl/include/openssl/ssl.h: $NGX_MAKEFILE
+- cd $OPENSSL \\
+- && if [ -f Makefile ]; then \$(MAKE) clean; fi \\
+- && ./config --prefix=$ngx_prefix no-shared no-threads $OPENSSL_OPT \\
+- && \$(MAKE) \\
+- && \$(MAKE) install_sw LIBDIR=lib
++ mkdir -p $OPENSSL/build $OPENSSL/.openssl/lib $OPENSSL/.openssl/include/openssl \\
++ && cd $OPENSSL/build \\
++ && cmake -DCMAKE_C_FLAGS="$OPENSSL_OPT" -DCMAKE_CXX_FLAGS="$OPENSSL_OPT" .. \\
++ && \$(MAKE) VERBOSE=1 \\
++ && cd .. \\
++ && cp -r include/openssl/*.h .openssl/include/openssl \\
++ && cp build/ssl/libssl.a build/crypto/libcrypto.a .openssl/lib
+
+ END
+
+diff --git a/auto/lib/quiche/conf b/auto/lib/quiche/conf
+new file mode 100644
+index 000000000..2c8995dd7
+--- /dev/null
++++ b/auto/lib/quiche/conf
+@@ -0,0 +1,19 @@
++
++# Copyright (C) Cloudflare, Inc.
++
++
++if [ $QUICHE != NONE ]; then
++
++ have=NGX_QUIC . auto/have
++
++ QUICHE_BUILD_TARGET="release"
++
++ if [ $NGX_DEBUG = YES ]; then
++ QUICHE_BUILD_TARGET="debug"
++ fi
++
++ CORE_INCS="$CORE_INCS $QUICHE/include"
++ CORE_DEPS="$CORE_DEPS $QUICHE/target/$QUICHE_BUILD_TARGET/libquiche.a"
++ CORE_LIBS="$CORE_LIBS $QUICHE/target/$QUICHE_BUILD_TARGET/libquiche.a $NGX_LIBPTHREAD"
++
++fi
+diff --git a/auto/lib/quiche/make b/auto/lib/quiche/make
+new file mode 100644
+index 000000000..bd1bec8b7
+--- /dev/null
++++ b/auto/lib/quiche/make
+@@ -0,0 +1,22 @@
++
++# Copyright (C) Cloudflare, Inc.
++
++
++# Default is release build
++QUICHE_BUILD_FLAGS="--release --no-default-features"
++QUICHE_BUILD_TARGET="release"
++
++if [ $NGX_DEBUG = YES ]; then
++ QUICHE_BUILD_FLAGS="--no-default-features"
++ QUICHE_BUILD_TARGET="debug"
++fi
++
++
++cat << END >> $NGX_MAKEFILE
++
++$QUICHE/target/$QUICHE_BUILD_TARGET/libquiche.a: \\
++ $OPENSSL/.openssl/include/openssl/ssl.h \\
++ $NGX_MAKEFILE
++ cd $QUICHE && cargo build $QUICHE_BUILD_FLAGS
++
++END
+diff --git a/auto/make b/auto/make
+index 34c40cdd5..136c0a64e 100644
+--- a/auto/make
++++ b/auto/make
+@@ -7,7 +7,8 @@ echo "creating $NGX_MAKEFILE"
+
+ mkdir -p $NGX_OBJS/src/core $NGX_OBJS/src/event $NGX_OBJS/src/event/modules \
+ $NGX_OBJS/src/os/unix $NGX_OBJS/src/os/win32 \
+- $NGX_OBJS/src/http $NGX_OBJS/src/http/v2 $NGX_OBJS/src/http/modules \
++ $NGX_OBJS/src/http $NGX_OBJS/src/http/v2 $NGX_OBJS/src/http/v3 \
++ $NGX_OBJS/src/http/modules \
+ $NGX_OBJS/src/http/modules/perl \
+ $NGX_OBJS/src/mail \
+ $NGX_OBJS/src/stream \
+diff --git a/auto/modules b/auto/modules
+index 09bfcb08d..2b2e6a889 100644
+--- a/auto/modules
++++ b/auto/modules
+@@ -134,6 +134,7 @@ if [ $HTTP = YES ]; then
+ # ngx_http_header_filter
+ # ngx_http_chunked_filter
+ # ngx_http_v2_filter
++ # ngx_http_v3_filter
+ # ngx_http_range_header_filter
+ # ngx_http_gzip_filter
+ # ngx_http_postpone_filter
+@@ -166,6 +167,7 @@ if [ $HTTP = YES ]; then
+ ngx_http_header_filter_module \
+ ngx_http_chunked_filter_module \
+ ngx_http_v2_filter_module \
++ ngx_http_v3_filter_module \
+ ngx_http_range_header_filter_module \
+ ngx_http_gzip_filter_module \
+ ngx_http_postpone_filter_module \
+@@ -227,6 +229,17 @@ if [ $HTTP = YES ]; then
+ . auto/module
+ fi
+
++ if [ $HTTP_V3 = YES ]; then
++ ngx_module_name=ngx_http_v3_filter_module
++ ngx_module_incs=
++ ngx_module_deps=
++ ngx_module_srcs=src/http/v3/ngx_http_v3_filter_module.c
++ ngx_module_libs=
++ ngx_module_link=$HTTP_V3
++
++ . auto/module
++ fi
++
+ if :; then
+ ngx_module_name=ngx_http_range_header_filter_module
+ ngx_module_incs=
+@@ -438,6 +451,24 @@ if [ $HTTP = YES ]; then
+ . auto/module
+ fi
+
++ if [ $HTTP_V3 = YES ]; then
++ USE_QUICHE=YES
++ USE_OPENSSL=YES
++ have=NGX_HTTP_V3 . auto/have
++ have=NGX_HTTP_HEADERS . auto/have
++
++ ngx_module_name=ngx_http_v3_module
++ ngx_module_incs=src/http/v3
++ ngx_module_deps="src/http/v3/ngx_http_v3.h \
++ src/http/v3/ngx_http_v3_module.h"
++ ngx_module_srcs="src/http/v3/ngx_http_v3.c \
++ src/http/v3/ngx_http_v3_module.c"
++ ngx_module_libs=
++ ngx_module_link=$HTTP_V3
++
++ . auto/module
++ fi
++
+ if :; then
+ ngx_module_name=ngx_http_static_module
+ ngx_module_incs=
+@@ -1268,6 +1299,19 @@ if [ $USE_OPENSSL = YES ]; then
+ fi
+
+
++if [ $USE_QUICHE = YES ]; then
++ ngx_module_type=CORE
++ ngx_module_name=ngx_quic_module
++ ngx_module_incs=
++ ngx_module_deps=src/event/ngx_event_quic.h
++ ngx_module_srcs=src/event/ngx_event_quic.c
++ ngx_module_libs=
++ ngx_module_link=YES
++
++ . auto/module
++fi
++
++
+ if [ $USE_PCRE = YES ]; then
+ ngx_module_type=CORE
+ ngx_module_name=ngx_regex_module
+diff --git a/auto/options b/auto/options
+index d8b421b0f..6b443f048 100644
+--- a/auto/options
++++ b/auto/options
+@@ -59,6 +59,7 @@ HTTP_CHARSET=YES
+ HTTP_GZIP=YES
+ HTTP_SSL=NO
+ HTTP_V2=NO
++HTTP_V3=NO
+ HTTP_SSI=YES
+ HTTP_POSTPONE=NO
+ HTTP_REALIP=NO
+@@ -148,6 +149,9 @@ PCRE_JIT=NO
+ USE_OPENSSL=NO
+ OPENSSL=NONE
+
++USE_QUICHE=NO
++QUICHE=NONE
++
+ USE_ZLIB=NO
+ ZLIB=NONE
+ ZLIB_OPT=
+@@ -225,6 +229,7 @@ $0: warning: the \"--with-ipv6\" option is deprecated"
+
+ --with-http_ssl_module) HTTP_SSL=YES ;;
+ --with-http_v2_module) HTTP_V2=YES ;;
++ --with-http_v3_module) HTTP_V3=YES ;;
+ --with-http_realip_module) HTTP_REALIP=YES ;;
+ --with-http_addition_module) HTTP_ADDITION=YES ;;
+ --with-http_xslt_module) HTTP_XSLT=YES ;;
+@@ -358,6 +363,9 @@ use the \"--with-mail_ssl_module\" option instead"
+ --with-openssl=*) OPENSSL="$value" ;;
+ --with-openssl-opt=*) OPENSSL_OPT="$value" ;;
+
++ --with-quiche=*) QUICHE="$value" ;;
++ --with-quiche-opt=*) QUICHE_OPT="$value" ;;
++
+ --with-md5=*)
+ NGX_POST_CONF_MSG="$NGX_POST_CONF_MSG
+ $0: warning: the \"--with-md5\" option is deprecated"
+@@ -440,6 +448,7 @@ cat << END
+
+ --with-http_ssl_module enable ngx_http_ssl_module
+ --with-http_v2_module enable ngx_http_v2_module
++ --with-http_v3_module enable ngx_http_v3_module
+ --with-http_realip_module enable ngx_http_realip_module
+ --with-http_addition_module enable ngx_http_addition_module
+ --with-http_xslt_module enable ngx_http_xslt_module
+diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h
+index 54059629e..7df8d4136 100644
+--- a/src/core/ngx_connection.h
++++ b/src/core/ngx_connection.h
+@@ -79,6 +79,9 @@ struct ngx_listening_s {
+ unsigned deferred_accept:1;
+ unsigned delete_deferred:1;
+ unsigned add_deferred:1;
++#if (NGX_QUIC)
++ unsigned quic:1;
++#endif
+ #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
+ char *accept_filter;
+ #endif
+@@ -156,6 +159,10 @@ struct ngx_connection_s {
+
+ ngx_udp_connection_t *udp;
+
++#if (NGX_QUIC)
++ ngx_quic_connection_t *quic;
++#endif
++
+ struct sockaddr *local_sockaddr;
+ socklen_t local_socklen;
+
+diff --git a/src/core/ngx_core.h b/src/core/ngx_core.h
+index 93ca9174d..d0441f034 100644
+--- a/src/core/ngx_core.h
++++ b/src/core/ngx_core.h
+@@ -82,6 +82,9 @@ typedef void (*ngx_connection_handler_pt)(ngx_connection_t *c);
+ #if (NGX_OPENSSL)
+ #include <ngx_event_openssl.h>
+ #endif
++#if (NGX_QUIC)
++#include <ngx_event_quic.h>
++#endif
+ #include <ngx_process_cycle.h>
+ #include <ngx_conf_file.h>
+ #include <ngx_module.h>
+diff --git a/src/event/ngx_event_quic.c b/src/event/ngx_event_quic.c
+new file mode 100644
+index 000000000..71d571a44
+--- /dev/null
++++ b/src/event/ngx_event_quic.c
+@@ -0,0 +1,581 @@
++
++/*
++ * Copyright (C) Cloudflare, Inc.
++ */
++
++#include <ngx_config.h>
++#include <ngx_core.h>
++#include <ngx_event.h>
++
++
++/* Limit outgoing packets to 1200 bytes. This is the minimum value allowed. */
++#define MAX_DATAGRAM_SIZE 1200
++
++/* errors */
++#define NGX_QUIC_NO_ERROR 0x0
++#define NGX_QUIC_INTERNAL_ERROR 0x1
++
++
++static void ngx_quic_read_handler(ngx_event_t *ev);
++static void ngx_quic_write_handler(ngx_event_t *ev);
++
++static void ngx_quic_handshake_completed(ngx_connection_t *c);
++
++static void ngx_quic_shutdown_handler(ngx_event_t *ev);
++
++static void ngx_quic_finalize_connection(ngx_connection_t *c, ngx_uint_t status);
++static void ngx_quic_close_connection(ngx_connection_t *c);
++
++static ngx_int_t ngx_quic_send_udp_packet(ngx_connection_t *c, uint8_t *buf,
++ size_t len);
++
++
++static ngx_command_t ngx_quic_commands[] = {
++
++ ngx_null_command
++};
++
++
++static ngx_core_module_t ngx_quic_module_ctx = {
++ ngx_string("quic"),
++ NULL,
++ NULL
++};
++
++
++ngx_module_t ngx_quic_module = {
++ NGX_MODULE_V1,
++ &ngx_quic_module_ctx, /* module context */
++ ngx_quic_commands, /* module directives */
++ NGX_CORE_MODULE, /* module type */
++ NULL, /* init master */
++ NULL, /* init module */
++ NULL, /* init process */
++ NULL, /* init thread */
++ NULL, /* exit thread */
++ NULL, /* exit process */
++ NULL, /* exit master */
++ NGX_MODULE_V1_PADDING
++};
++
++
++ngx_int_t
++ngx_quic_create_conf(ngx_quic_t *quic)
++{
++ quic->config = quiche_config_new(QUICHE_PROTOCOL_VERSION);
++ if (quic->config == NULL) {
++ ngx_log_error(NGX_LOG_EMERG, quic->log, 0, "failed to create quic config");
++ return NGX_ERROR;
++ }
++
++ return NGX_OK;
++}
++
++
++ngx_int_t
++ngx_quic_validate_initial(ngx_event_t *ev, u_char *buf, ssize_t buf_len)
++{
++ /* Check incoming packet type, if it's not Initial we shouldn't be here. */
++ if (((buf[0] & 0x30) >> 4) != 0) {
++ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,
++ "packet is not quic client initial");
++ return NGX_ERROR;
++ }
++
++ /* Client Initial packets must be at least 1200 bytes. */
++ if (buf_len < QUICHE_MIN_CLIENT_INITIAL_LEN) {
++ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,
++ "quic initial packet is too short");
++ return NGX_ERROR;
++ }
++
++ return NGX_OK;
++}
++
++
++ngx_int_t
++ngx_quic_create_connection(ngx_quic_t *quic, ngx_connection_t *c)
++{
++ int rc;
++ u_char *buf;
++ size_t buf_len;
++ quiche_conn *conn;
++ static uint8_t out[MAX_DATAGRAM_SIZE];
++
++ uint8_t pkt_type;
++ uint32_t pkt_version;
++
++ uint8_t scid[QUICHE_MAX_CONN_ID_LEN];
++ size_t scid_len = sizeof(scid);
++
++ uint8_t dcid[QUICHE_MAX_CONN_ID_LEN];
++ size_t dcid_len = sizeof(dcid);
++
++ uint8_t token[1];
++ size_t token_len = sizeof(token);
++
++ ngx_quic_connection_t *qc;
++
++ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic init connection");
++
++ /* Extract some fields from the client's Initial packet, which was saved
++ * into c->buffer by ngx_event_recvmsg(). */
++ buf = c->buffer->pos;
++ buf_len = ngx_buf_size(c->buffer);
++
++ rc = quiche_header_info(buf, buf_len, QUICHE_MAX_CONN_ID_LEN,
++ &pkt_version, &pkt_type,
++ scid, &scid_len, dcid, &dcid_len,
++ token, &token_len);
++ if (rc < 0) {
++ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
++ "failed to parse quic header: %d", rc);
++ return NGX_ERROR;
++ }
++
++ /* Version mismatch, do version negotiation. */
++ if (!quiche_version_is_supported(pkt_version)) {
++ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
++ "quic version negotiation");
++
++ ssize_t written = quiche_negotiate_version(scid, scid_len,
++ dcid, dcid_len,
++ out, sizeof(out));
++
++ if (written < 0) {
++ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
++ "failed to create quic vneg packet: %d", written);
++ return NGX_ERROR;
++ }
++
++ if (ngx_quic_send_udp_packet(c, out, written) == NGX_ERROR) {
++ return NGX_ERROR;
++ }
++
++ return NGX_DONE;
++ }
++
++ /* Initialize source connection ID with some random bytes. */
++ RAND_bytes(scid, sizeof(scid));
++
++#if (NGX_DEBUG)
++ {
++ uint8_t dcid_hex[QUICHE_MAX_CONN_ID_LEN * 2],
++ scid_hex[QUICHE_MAX_CONN_ID_LEN * 2];
++
++ ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
++ "new quic connection dcid:%*.s new_scid:%*.s",
++ ngx_hex_dump(dcid_hex, dcid, dcid_len) - dcid_hex, dcid_hex,
++ ngx_hex_dump(scid_hex, scid, scid_len) - scid_hex, scid_hex);
++ }
++#endif
++
++ conn = quiche_conn_new_with_tls(scid, sizeof(scid), NULL, 0, quic->config,
++ c->ssl->connection, true);
++ if (conn == NULL) {
++ ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to create quic connection");
++ return NGX_ERROR;
++ }
++
++ qc = ngx_pcalloc(c->pool, sizeof(ngx_quic_connection_t));
++ if (qc == NULL) {
++ return NGX_ERROR;
++ }
++
++ qc->handler = NULL;
++
++ qc->conn = conn;
++
++ c->quic = qc;
++
++ return NGX_OK;
++}
++
++
++ngx_int_t
++ngx_quic_handshake(ngx_connection_t *c)
++{
++ u_char *buf;
++ size_t buf_len;
++ ssize_t done;
++
++ c->log->action = "processing QUIC connection";
++
++ /* Process the client's Initial packet, which was saved into c->buffer by
++ * ngx_event_recvmsg(). */
++ buf = c->buffer->pos;
++ buf_len = ngx_buf_size(c->buffer);
++
++ done = quiche_conn_recv(c->quic->conn, buf, buf_len);
++
++ if ((done < 0) && (done != QUICHE_ERR_DONE)) {
++ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
++ "failed to process quic packet: %d", done);
++ return NGX_ERROR;
++ }
++
++ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
++ return NGX_ERROR;
++ }
++
++ c->read->handler = ngx_quic_read_handler;
++ c->write->handler = ngx_quic_write_handler;
++
++ ngx_post_event(c->write, &ngx_posted_events);
++
++ return NGX_AGAIN;
++}
++
++
++static void
++ngx_quic_read_handler(ngx_event_t *rev)
++{
++ int n;
++ static uint8_t buf[65535];
++ ngx_connection_t *c;
++
++ c = rev->data;
++
++ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic read handler");
++
++ if (rev->timedout) {
++ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
++ "quic connection timed out");
++
++ ngx_quic_finalize_connection(c, NGX_QUIC_INTERNAL_ERROR);
++ return;
++ }
++
++ for (;;) {
++ n = c->recv(c, buf, sizeof(buf));
++ if (n == NGX_AGAIN) {
++ break;
++ }
++
++ if (n == NGX_ERROR) {
++ ngx_quic_finalize_connection(c, NGX_QUIC_INTERNAL_ERROR);
++ return;
++ }
++
++ ssize_t done = quiche_conn_recv(c->quic->conn, buf, n);
++
++ if (done == QUICHE_ERR_DONE) {
++ break;
++ }
++
++ if (done < 0) {
++ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
++ "failed to process quic packet: %d", done);
++
++ ngx_quic_finalize_connection(c, NGX_QUIC_INTERNAL_ERROR);
++ return;
++ }
++ }
++
++ if (quiche_conn_is_in_early_data(c->quic->conn) ||
++ quiche_conn_is_established(c->quic->conn)) {
++ if (!c->ssl->handshaked) {
++ ngx_quic_handshake_completed(c);
++ }
++
++ if ((c->quic == NULL) || (c->quic->handler == NULL)) {
++ return;
++ }
++
++ /* Notify application layer that there might be stream data to read. */
++ c->quic->handler(c);
++ }
++
++ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic done reading");
++
++ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
++ ngx_quic_finalize_connection(c, NGX_QUIC_INTERNAL_ERROR);
++ return;
++ }
++
++ ngx_post_event(c->write, &ngx_posted_events);
++}
++
++
++static void
++ngx_quic_write_handler(ngx_event_t *wev)
++{
++ ngx_connection_t *c;
++ ngx_msec_t expiry;
++ static uint8_t out[MAX_DATAGRAM_SIZE];
++
++ c = wev->data;
++
++ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic write handler");
++
++ if (wev->timedout) {
++ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic alarm fired");
++
++ quiche_conn_on_timeout(c->quic->conn);
++ }
++
++ if (quiche_conn_is_closed(c->quic->conn)) {
++ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
++ "quic connection is closed");
++
++ ngx_quic_finalize_connection(c, NGX_QUIC_NO_ERROR);
++ return;
++ }
++
++ for (;;) {
++ ssize_t written = quiche_conn_send(c->quic->conn, out, sizeof(out));
++
++ if (written == QUICHE_ERR_DONE) {
++ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic done writing");
++ break;
++ }
++
++ if (written < 0) {
++ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
++ "failed to create quic packet: %d", written);
++
++ ngx_quic_finalize_connection(c, NGX_QUIC_INTERNAL_ERROR);
++ return;
++ }
++
++ if (ngx_quic_send_udp_packet(c, out, written) == NGX_ERROR) {
++ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
++ "failed to send quic packet");
++
++ ngx_quic_finalize_connection(c, NGX_QUIC_INTERNAL_ERROR);
++ return;
++ }
++ }
++
++ if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
++ ngx_quic_finalize_connection(c, NGX_QUIC_INTERNAL_ERROR);
++ return;
++ }
++
++ expiry = quiche_conn_timeout_as_millis(c->quic->conn);
++ expiry = ngx_max(expiry, 1);
++
++ if (wev->timer_set) {
++ ngx_del_timer(wev);
++ }
++
++ /* quiche_conn_timeout_as_millis() will return UINT64_MAX when the timer
++ * should be unset (this would be equvalent to returning Option::None in
++ * Rust). To avoid overflow we need to explicitly check for this value. */
++ if (expiry != UINT64_MAX) {
++ ngx_add_timer(wev, expiry);
++ }
++}
++
++
++static void
++ngx_quic_handshake_completed(ngx_connection_t *c)
++{
++#if (NGX_DEBUG)
++ {
++ char buf[129], *s, *d;
++#if OPENSSL_VERSION_NUMBER >= 0x10000000L
++ const
++#endif
++ SSL_CIPHER *cipher;
++
++ cipher = SSL_get_current_cipher(c->ssl->connection);
++
++ if (cipher) {
++ SSL_CIPHER_description(cipher, &buf[1], 128);
++
++ for (s = &buf[1], d = buf; *s; s++) {
++ if (*s == ' ' && *d == ' ') {
++ continue;
++ }
++
++ if (*s == LF || *s == CR) {
++ continue;
++ }
++
++ *++d = *s;
++ }
++
++ if (*d != ' ') {
++ d++;
++ }
++
++ *d = '\0';
++
++ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
++ "QUIC: %s, cipher: \"%s\"",
++ SSL_get_version(c->ssl->connection), &buf[1]);
++
++ if (SSL_session_reused(c->ssl->connection)) {
++ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
++ "quic reused session");
++ }
++
++ } else {
++ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
++ "quic no shared ciphers");
++ }
++ }
++#endif
++
++ ngx_del_timer(c->read);
++
++ c->ssl->handshaked = 1;
++
++ /* Notify application layer that the handshake is complete. */
++ c->ssl->handler(c);
++}
++
++
++ngx_int_t
++ngx_quic_shutdown(ngx_connection_t *c)
++{
++ if (!quiche_conn_is_closed(c->quic->conn)) {
++ /* We shouldn't free the connection state yet, as we need to wait for
++ * the draining timeout to expire. Setup event handlers such that we
++ * will try again when that happens (or when another event is
++ * triggered). */
++ c->read->handler = ngx_quic_shutdown_handler;
++ c->write->handler = ngx_quic_shutdown_handler;
++
++ /* We need to flush any remaining frames to the client (including
++ * CONNECTION_CLOSE), so invoke the write handler. This also takes
++ * care of setting up the draining timer. */
++ ngx_quic_write_handler(c->write);
++
++ /* The QUIC connection might have already been freed inside the write
++ * handler, in which case we are done. */
++ if (c->destroyed) {
++ return NGX_OK;
++ }
++
++ return NGX_AGAIN;
++ }
++
++ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "free quic connection");
++
++ quiche_conn_free(c->quic->conn);
++
++ c->quic = NULL;
++ c->ssl = NULL;
++
++ return NGX_OK;
++}
++
++
++static void
++ngx_quic_shutdown_handler(ngx_event_t *ev)
++{
++ ngx_connection_t *c;
++ ngx_connection_handler_pt handler;
++
++ c = ev->data;
++ handler = c->quic->handler;
++
++ if (ev->timedout) {
++ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic alarm fired");
++
++ quiche_conn_on_timeout(c->quic->conn);
++ }
++
++ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "quic shutdown handler");
++
++ if (ngx_quic_shutdown(c) == NGX_AGAIN) {
++ return;
++ }
++
++ handler(c);
++}
++
++
++static void
++ngx_quic_finalize_connection(ngx_connection_t *c, ngx_uint_t status)
++{
++ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
++ "finalize quic connection: %d", c->fd);
++
++ c->error = 1;
++
++ quiche_conn_close(c->quic->conn, false, status, NULL, 0);
++
++ /* Notify the application layer that the connection is in an error
++ * state and will be closed. */
++ if (c->quic->handler != NULL) {
++ c->quic->handler(c);
++ return;
++ }
++
++ ngx_quic_close_connection(c);
++}
++
++
++static void
++ngx_quic_close_connection(ngx_connection_t *c)
++{
++ ngx_pool_t *pool;
++
++ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
++ "close quic connection: %d", c->fd);
++
++ if (c->quic) {
++ if (ngx_quic_shutdown(c) == NGX_AGAIN) {
++ c->quic->handler = ngx_quic_close_connection;
++ return;
++ }
++
++ if (c->destroyed) {
++ return;
++ }
++ }
++
++#if (NGX_STAT_STUB)
++ (void) ngx_atomic_fetch_add(ngx_stat_active, -1);
++#endif
++
++ c->destroyed = 1;
++
++ pool = c->pool;
++
++ ngx_close_connection(c);
++
++ ngx_destroy_pool(pool);
++}
++
++
++void
++ngx_quic_cleanup_ctx(void *data)
++{
++ ngx_quic_t *quic = data;
++
++ quiche_config_free(quic->config);
++}
++
++
++static ngx_int_t
++ngx_quic_send_udp_packet(ngx_connection_t *c, uint8_t *buf, size_t len)
++{
++ ngx_buf_t out_buf = {0};
++ ngx_chain_t out_chain = {0};
++
++ /* The send_chain() API takes an ngx_chain_t parameter instead of a simple
++ * buffer, so we need to initialize the chain such that it contains only a
++ * single buffer.
++ *
++ * The c->send_chain() call is required (instead of just c->send()) because
++ * it uses the sendmsg(2) syscall (instead of sendto(2)), which allows us to
++ * specify the correct source IP address for the connection. */
++
++ out_buf.start = out_buf.pos = buf;
++ out_buf.end = out_buf.last = buf + len;
++ out_buf.memory = 1;
++ out_buf.flush = 1;
++
++ out_chain.buf = &out_buf;
++ out_chain.next = NULL;
++
++ if (c->send_chain(c, &out_chain, 0) == NGX_CHAIN_ERROR) {
++ return NGX_ERROR;
++ }
++
++ return NGX_OK;
++}
+diff --git a/src/event/ngx_event_quic.h b/src/event/ngx_event_quic.h
+new file mode 100644
+index 000000000..b44a96320
+--- /dev/null
++++ b/src/event/ngx_event_quic.h
+@@ -0,0 +1,49 @@
++
++/*
++ * Copyright (C) Cloudflare, Inc.
++ */
++
++
++#ifndef _NGX_EVENT_QUIC_H_INCLUDED_
++#define _NGX_EVENT_QUIC_H_INCLUDED_
++
++
++#include <stdbool.h>
++
++#include <ngx_config.h>
++#include <ngx_core.h>
++
++#include <quiche.h>
++
++typedef struct ngx_quic_s ngx_quic_t;
++typedef struct ngx_quic_connection_s ngx_quic_connection_t;
++
++struct ngx_quic_s {
++ quiche_config *config;
++ ngx_log_t *log;
++};
++
++struct ngx_quic_connection_s {
++ quiche_conn *conn;
++
++ ngx_connection_handler_pt handler;
++};
++
++
++ngx_int_t ngx_quic_create_conf(ngx_quic_t *quic);
++
++ngx_int_t ngx_quic_validate_initial(ngx_event_t *ev, u_char *buf,
++ ssize_t buf_len);
++
++ngx_int_t ngx_quic_create_connection(ngx_quic_t *quic, ngx_connection_t *c);
++
++ngx_int_t ngx_quic_create_ssl_connection(ngx_ssl_t *ssl, ngx_connection_t *c,
++ ngx_uint_t flags);
++
++ngx_int_t ngx_quic_handshake(ngx_connection_t *c);
++
++ngx_int_t ngx_quic_shutdown(ngx_connection_t *c);
++
++void ngx_quic_cleanup_ctx(void *data);
++
++#endif /* _NGX_EVENT_QUIC_H_INCLUDED_ */
+diff --git a/src/event/ngx_event_udp.c b/src/event/ngx_event_udp.c
+index 557283050..146275121 100644
+--- a/src/event/ngx_event_udp.c
++++ b/src/event/ngx_event_udp.c
+@@ -276,6 +276,14 @@ ngx_event_recvmsg(ngx_event_t *ev)
+ (void) ngx_atomic_fetch_add(ngx_stat_accepted, 1);
+ #endif
+
++#if (NGX_QUIC)
++ if (ls->quic) {
++ if (ngx_quic_validate_initial(ev, buffer, n) != NGX_OK) {
++ goto next;
++ }
++ }
++#endif
++
+ ngx_accept_disabled = ngx_cycle->connection_n / 8
+ - ngx_cycle->free_connection_n;
+
+diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
+index b3f8f4795..00dd9c61a 100644
+--- a/src/http/modules/ngx_http_ssl_module.c
++++ b/src/http/modules/ngx_http_ssl_module.c
+@@ -371,7 +371,7 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out,
+ #if (NGX_DEBUG)
+ unsigned int i;
+ #endif
+-#if (NGX_HTTP_V2)
++#if (NGX_HTTP_V2 || NGX_HTTP_V3)
+ ngx_http_connection_t *hc;
+ #endif
+ #if (NGX_HTTP_V2 || NGX_DEBUG)
+@@ -388,9 +388,11 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out,
+ }
+ #endif
+
+-#if (NGX_HTTP_V2)
++#if (NGX_HTTP_V2 || NGX_HTTP_V3)
+ hc = c->data;
++#endif
+
++#if (NGX_HTTP_V2)
+ if (hc->addr_conf->http2) {
+ srv =
+ (unsigned char *) NGX_HTTP_V2_ALPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE;
+@@ -398,6 +400,13 @@ ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out,
+
+ } else
+ #endif
++#if (NGX_HTTP_V3)
++ if (hc->addr_conf->quic) {
++ srv = (unsigned char *) QUICHE_H3_APPLICATION_PROTOCOL;
++ srvlen = sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1;
++
++ } else
++#endif
+ {
+ srv = (unsigned char *) NGX_HTTP_NPN_ADVERTISE;
+ srvlen = sizeof(NGX_HTTP_NPN_ADVERTISE) - 1;
+diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c
+index 79ef9c644..a29bff836 100644
+--- a/src/http/ngx_http.c
++++ b/src/http/ngx_http.c
+@@ -1141,6 +1141,7 @@ ngx_int_t
+ ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
+ ngx_http_listen_opt_t *lsopt)
+ {
++ int t;
+ in_port_t p;
+ ngx_uint_t i;
+ struct sockaddr *sa;
+@@ -1159,11 +1160,13 @@ ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
+
+ sa = lsopt->sockaddr;
+ p = ngx_inet_get_port(sa);
++ t = lsopt->quic ? SOCK_DGRAM : SOCK_STREAM;
+
+ port = cmcf->ports->elts;
+ for (i = 0; i < cmcf->ports->nelts; i++) {
+
+- if (p != port[i].port || sa->sa_family != port[i].family) {
++ if (p != port[i].port || sa->sa_family != port[i].family
++ || t != port[i].type) {
+ continue;
+ }
+
+@@ -1182,6 +1185,7 @@ ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
+ port->family = sa->sa_family;
+ port->port = p;
+ port->addrs.elts = NULL;
++ port->type = t;
+
+ return ngx_http_add_address(cf, cscf, port, lsopt);
+ }
+@@ -1199,6 +1203,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
+ #if (NGX_HTTP_V2)
+ ngx_uint_t http2;
+ #endif
++#if (NGX_HTTP_V3)
++ ngx_uint_t quic;
++#endif
+
+ /*
+ * we cannot compare whole sockaddr struct's as kernel
+@@ -1234,6 +1241,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
+ #if (NGX_HTTP_V2)
+ http2 = lsopt->http2 || addr[i].opt.http2;
+ #endif
++#if (NGX_HTTP_V3)
++ quic = lsopt->quic || addr[i].opt.quic;
++#endif
+
+ if (lsopt->set) {
+
+@@ -1270,6 +1280,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
+ #if (NGX_HTTP_V2)
+ addr[i].opt.http2 = http2;
+ #endif
++#if (NGX_HTTP_V3)
++ addr[i].opt.quic = quic;
++#endif
+
+ return NGX_OK;
+ }
+@@ -1688,6 +1701,12 @@ ngx_http_init_listening(ngx_conf_t *cf, ngx_http_conf_port_t *port)
+ break;
+ }
+
++#if (NGX_HTTP_V3)
++ if (addr[i].opt.quic) {
++ ls->type = SOCK_DGRAM;
++ }
++#endif
++
+ addr++;
+ last--;
+ }
+@@ -1770,6 +1789,12 @@ ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr)
+ ls->reuseport = addr->opt.reuseport;
+ #endif
+
++#if (NGX_HTTP_V3)
++ ls->quic = addr->opt.quic;
++
++ ls->wildcard = addr->opt.wildcard;
++#endif
++
+ return ls;
+ }
+
+@@ -1803,6 +1828,9 @@ ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport,
+ addrs[i].conf.http2 = addr[i].opt.http2;
+ #endif
+ addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
++#if (NGX_HTTP_V3)
++ addrs[i].conf.quic = addr[i].opt.quic;
++#endif
+
+ if (addr[i].hash.buckets == NULL
+ && (addr[i].wc_head == NULL
+@@ -1868,6 +1896,9 @@ ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport,
+ addrs6[i].conf.http2 = addr[i].opt.http2;
+ #endif
+ addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
++#if (NGX_HTTP_V3)
++ addrs6[i].conf.quic = addr[i].opt.quic;
++#endif
+
+ if (addr[i].hash.buckets == NULL
+ && (addr[i].wc_head == NULL
+diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h
+index 8b43857ee..444f93536 100644
+--- a/src/http/ngx_http.h
++++ b/src/http/ngx_http.h
+@@ -20,6 +20,7 @@ typedef struct ngx_http_file_cache_s ngx_http_file_cache_t;
+ typedef struct ngx_http_log_ctx_s ngx_http_log_ctx_t;
+ typedef struct ngx_http_chunked_s ngx_http_chunked_t;
+ typedef struct ngx_http_v2_stream_s ngx_http_v2_stream_t;
++typedef struct ngx_http_v3_stream_s ngx_http_v3_stream_t;
+
+ typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+@@ -38,6 +39,9 @@ typedef u_char *(*ngx_http_log_handler_pt)(ngx_http_request_t *r,
+ #if (NGX_HTTP_V2)
+ #include <ngx_http_v2.h>
+ #endif
++#if (NGX_HTTP_V3)
++#include <ngx_http_v3.h>
++#endif
+ #if (NGX_HTTP_CACHE)
+ #include <ngx_http_cache.h>
+ #endif
+diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
+index cb49ef74a..1e991c0b3 100644
+--- a/src/http/ngx_http_core_module.c
++++ b/src/http/ngx_http_core_module.c
+@@ -4099,6 +4099,13 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+ continue;
+ }
+
++#if (NGX_HTTP_V3)
++ if (ngx_strcmp(value[n].data, "quic") == 0) {
++ lsopt.quic = 1;
++ continue;
++ }
++#endif
++
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[n]);
+ return NGX_CONF_ERROR;
+diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h
+index 85f6d66dc..fa08e3631 100644
+--- a/src/http/ngx_http_core_module.h
++++ b/src/http/ngx_http_core_module.h
+@@ -82,6 +82,7 @@ typedef struct {
+ unsigned reuseport:1;
+ unsigned so_keepalive:2;
+ unsigned proxy_protocol:1;
++ unsigned quic:1;
+
+ int backlog;
+ int rcvbuf;
+@@ -238,6 +239,7 @@ struct ngx_http_addr_conf_s {
+ unsigned ssl:1;
+ unsigned http2:1;
+ unsigned proxy_protocol:1;
++ unsigned quic:1;
+ };
+
+
+@@ -268,6 +270,7 @@ typedef struct {
+ ngx_int_t family;
+ in_port_t port;
+ ngx_array_t addrs; /* array of ngx_http_conf_addr_t */
++ ngx_int_t type;
+ } ngx_http_conf_port_t;
+
+
+diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
+index 80c19656f..f7b1ad35e 100644
+--- a/src/http/ngx_http_request.c
++++ b/src/http/ngx_http_request.c
+@@ -64,6 +64,10 @@ static void ngx_http_ssl_handshake(ngx_event_t *rev);
+ static void ngx_http_ssl_handshake_handler(ngx_connection_t *c);
+ #endif
+
++#if (NGX_HTTP_V3)
++static void ngx_http_quic_handshake(ngx_event_t *rev);
++#endif
++
+
+ static char *ngx_http_client_errors[] = {
+
+@@ -349,6 +353,18 @@ ngx_http_init_connection(ngx_connection_t *c)
+ c->log->action = "reading PROXY protocol";
+ }
+
++#if (NGX_HTTP_V3)
++ if (hc->addr_conf->quic) {
++ hc->quic = 1;
++
++ /* We already have a UDP packet in the connection buffer, so we don't
++ * need to wait for another read event to kick-off the handshake. */
++ ngx_add_timer(rev, c->listening->post_accept_timeout);
++ ngx_http_quic_handshake(rev);
++ return;
++ }
++#endif
++
+ if (rev->ready) {
+ /* the deferred accept(), iocp */
+
+@@ -797,7 +813,7 @@ ngx_http_ssl_handshake_handler(ngx_connection_t *c)
+
+ c->ssl->no_wait_shutdown = 1;
+
+-#if (NGX_HTTP_V2 \
++#if ((NGX_HTTP_V2 || NGX_HTTP_V3) \
+ && (defined TLSEXT_TYPE_application_layer_protocol_negotiation \
+ || defined TLSEXT_TYPE_next_proto_neg))
+ {
+@@ -807,7 +823,7 @@ ngx_http_ssl_handshake_handler(ngx_connection_t *c)
+
+ hc = c->data;
+
+- if (hc->addr_conf->http2) {
++ if (hc->addr_conf->http2 || hc->addr_conf->quic) {
+
+ #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
+ SSL_get0_alpn_selected(c->ssl->connection, &data, &len);
+@@ -822,11 +838,29 @@ ngx_http_ssl_handshake_handler(ngx_connection_t *c)
+ SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &len);
+ #endif
+
++ }
++
++#if (NGX_HTTP_V2)
++ if (hc->addr_conf->http2) {
+ if (len == 2 && data[0] == 'h' && data[1] == '2') {
+ ngx_http_v2_init(c->read);
+ return;
+ }
+ }
++#endif
++
++#if (NGX_HTTP_V3)
++ if (hc->addr_conf->quic) {
++ if (len >= 2 && data[0] == 'h' && data[1] == '3') {
++ ngx_http_v3_init(c->read);
++ return;
++ }
++
++ ngx_http_close_connection(c);
++ return;
++ }
++#endif
++
+ }
+ #endif
+
+@@ -1033,6 +1067,68 @@ failed:
+
+ #endif
+
++#if (NGX_HTTP_V3)
++
++static void
++ngx_http_quic_handshake(ngx_event_t *rev)
++{
++ ngx_int_t rc;
++ ngx_connection_t *c;
++ ngx_http_connection_t *hc;
++ ngx_http_v3_srv_conf_t *qscf;
++ ngx_http_ssl_srv_conf_t *sscf;
++
++ c = rev->data;
++ hc = c->data;
++
++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
++ "http check quic handshake");
++
++ if (rev->timedout) {
++ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
++ ngx_http_close_connection(c);
++ return;
++ }
++
++ if (c->close) {
++ ngx_http_close_connection(c);
++ return;
++ }
++
++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "https quic handshake");
++
++ sscf = ngx_http_get_module_srv_conf(hc->conf_ctx,
++ ngx_http_ssl_module);
++
++ if (ngx_ssl_create_connection(&sscf->ssl, c, 0) != NGX_OK) {
++ ngx_http_close_connection(c);
++ return;
++ }
++
++ qscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module);
++
++ if (ngx_quic_create_connection(&qscf->quic, c) != NGX_OK) {
++ ngx_http_close_connection(c);
++ return;
++ }
++
++ rc = ngx_quic_handshake(c);
++
++ if (rc == NGX_AGAIN) {
++
++ if (!rev->timer_set) {
++ ngx_add_timer(rev, c->listening->post_accept_timeout);
++ }
++
++ c->ssl->handler = ngx_http_ssl_handshake_handler;
++ return;
++ }
++
++ ngx_http_ssl_handshake_handler(c);
++}
++
++#endif
++
+
+ static void
+ ngx_http_process_request_line(ngx_event_t *rev)
+@@ -2687,6 +2783,13 @@ ngx_http_finalize_connection(ngx_http_request_t *r)
+ }
+ #endif
+
++#if (NGX_HTTP_V3)
++ if (r->qstream) {
++ ngx_http_close_request(r, 0);
++ return;
++ }
++#endif
++
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (r->main->count != 1) {
+@@ -2896,6 +2999,19 @@ ngx_http_test_reading(ngx_http_request_t *r)
+
+ #endif
+
++#if (NGX_HTTP_V3)
++
++ if (r->qstream) {
++ if (c->error) {
++ err = 0;
++ goto closed;
++ }
++
++ return;
++ }
++
++#endif
++
+ #if (NGX_HAVE_KQUEUE)
+
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+@@ -3563,7 +3679,15 @@ ngx_http_close_request(ngx_http_request_t *r, ngx_int_t rc)
+ }
+ #endif
+
++#if (NGX_HTTP_V3)
++ if (r->qstream) {
++ ngx_http_v3_close_stream(r->qstream, rc);
++ return;
++ }
++#endif
++
+ ngx_http_free_request(r, rc);
++
+ ngx_http_close_connection(c);
+ }
+
+@@ -3684,6 +3808,21 @@ ngx_http_close_connection(ngx_connection_t *c)
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "close http connection: %d", c->fd);
+
++#if (NGX_HTTP_V3)
++
++ if (c->quic) {
++ if (ngx_quic_shutdown(c) == NGX_AGAIN) {
++ c->quic->handler = ngx_http_close_connection;
++ return;
++ }
++
++ if (c->destroyed) {
++ return;
++ }
++ }
++
++#endif
++
+ #if (NGX_HTTP_SSL)
+
+ if (c->ssl) {
+diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h
+index fce70efe6..8ac19658c 100644
+--- a/src/http/ngx_http_request.h
++++ b/src/http/ngx_http_request.h
+@@ -24,6 +24,7 @@
+ #define NGX_HTTP_VERSION_10 1000
+ #define NGX_HTTP_VERSION_11 1001
+ #define NGX_HTTP_VERSION_20 2000
++#define NGX_HTTP_VERSION_3 3000
+
+ #define NGX_HTTP_UNKNOWN 0x0001
+ #define NGX_HTTP_GET 0x0002
+@@ -323,6 +324,7 @@ typedef struct {
+ ngx_chain_t *free;
+
+ unsigned ssl:1;
++ unsigned quic:1;
+ unsigned proxy_protocol:1;
+ } ngx_http_connection_t;
+
+@@ -445,6 +447,7 @@ struct ngx_http_request_s {
+
+ ngx_http_connection_t *http_connection;
+ ngx_http_v2_stream_t *stream;
++ ngx_http_v3_stream_t *qstream;
+
+ ngx_http_log_handler_pt log_handler;
+
+diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c
+index c4f092e59..2f8514418 100644
+--- a/src/http/ngx_http_request_body.c
++++ b/src/http/ngx_http_request_body.c
+@@ -85,6 +85,13 @@ ngx_http_read_client_request_body(ngx_http_request_t *r,
+ }
+ #endif
+
++#if (NGX_HTTP_V3)
++ if (r->qstream) {
++ rc = ngx_http_v3_read_request_body(r);
++ goto done;
++ }
++#endif
++
+ preread = r->header_in->last - r->header_in->pos;
+
+ if (preread) {
+@@ -226,6 +233,18 @@ ngx_http_read_unbuffered_request_body(ngx_http_request_t *r)
+ }
+ #endif
+
++#if (NGX_HTTP_V3)
++ if (r->qstream) {
++ rc = ngx_http_v3_read_unbuffered_request_body(r);
++
++ if (rc == NGX_OK) {
++ r->reading_body = 0;
++ }
++
++ return rc;
++ }
++#endif
++
+ if (r->connection->read->timedout) {
+ r->connection->timedout = 1;
+ return NGX_HTTP_REQUEST_TIME_OUT;
+@@ -525,6 +544,13 @@ ngx_http_discard_request_body(ngx_http_request_t *r)
+ }
+ #endif
+
++#if (NGX_HTTP_V3)
++ if (r->qstream) {
++ r->qstream->skip_data = 1;
++ return NGX_OK;
++ }
++#endif
++
+ if (ngx_http_test_expect(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+@@ -808,6 +834,9 @@ ngx_http_test_expect(ngx_http_request_t *r)
+ || r->http_version < NGX_HTTP_VERSION_11
+ #if (NGX_HTTP_V2)
+ || r->stream != NULL
++#endif
++#if (NGX_HTTP_V3)
++ || r->qstream != NULL
+ #endif
+ )
+ {
+diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
+index a7391d09a..398af2797 100644
+--- a/src/http/ngx_http_upstream.c
++++ b/src/http/ngx_http_upstream.c
+@@ -526,6 +526,13 @@ ngx_http_upstream_init(ngx_http_request_t *r)
+ }
+ #endif
+
++#if (NGX_HTTP_V3)
++ if (r->qstream) {
++ ngx_http_upstream_init_request(r);
++ return;
++ }
++#endif
++
+ if (c->read->timer_set) {
+ ngx_del_timer(c->read);
+ }
+@@ -1351,6 +1358,12 @@ ngx_http_upstream_check_broken_connection(ngx_http_request_t *r,
+ }
+ #endif
+
++#if (NGX_HTTP_V3)
++ if (r->qstream) {
++ return;
++ }
++#endif
++
+ #if (NGX_HAVE_KQUEUE)
+
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c
+new file mode 100644
+index 000000000..0877fb9a2
+--- /dev/null
++++ b/src/http/v3/ngx_http_v3.c
+@@ -0,0 +1,2094 @@
++
++/*
++ * Copyright (C) Cloudflare, Inc.
++ */
++
++
++#include <ngx_config.h>
++#include <ngx_core.h>
++#include <ngx_http.h>
++#include <ngx_http_v3_module.h>
++
++
++typedef struct {
++ ngx_str_t name;
++ ngx_uint_t offset;
++ ngx_uint_t hash;
++ ngx_http_header_t *hh;
++} ngx_http_v3_parse_header_t;
++
++
++/* errors */
++#define NGX_HTTP_V3_NO_ERROR 0x0
++#define NGX_HTTP_V3_INTERNAL_ERROR 0x3
++
++
++static void ngx_http_v3_handler(ngx_connection_t *c);
++
++static ngx_http_v3_stream_t *ngx_http_v3_stream_lookup(
++ ngx_http_v3_connection_t *h3c, ngx_uint_t stream_id);
++static ngx_http_v3_stream_t *ngx_http_v3_create_stream(
++ ngx_http_v3_connection_t *h3c);
++static void ngx_http_v3_close_stream_handler(ngx_event_t *ev);
++
++static ngx_int_t ngx_http_v3_validate_header(ngx_http_request_t *r,
++ ngx_http_v3_header_t *header);
++static ngx_int_t ngx_http_v3_pseudo_header(ngx_http_request_t *r,
++ ngx_http_v3_header_t *header);
++static ngx_int_t ngx_http_v3_parse_path(ngx_http_request_t *r,
++ ngx_str_t *value);
++static ngx_int_t ngx_http_v3_parse_method(ngx_http_request_t *r,
++ ngx_str_t *value);
++static ngx_int_t ngx_http_v3_parse_scheme(ngx_http_request_t *r,
++ ngx_str_t *value);
++static ngx_int_t ngx_http_v3_parse_authority(ngx_http_request_t *r,
++ ngx_str_t *value);
++static ngx_int_t ngx_http_v3_parse_header(ngx_http_request_t *r,
++ ngx_http_v3_parse_header_t *header, ngx_str_t *value);
++static ngx_int_t ngx_http_v3_cookie(ngx_http_request_t *r,
++ ngx_http_v3_header_t *header);
++static ngx_int_t ngx_http_v3_construct_cookie_header(ngx_http_request_t *r);
++static ngx_int_t ngx_http_v3_construct_request_line(ngx_http_request_t *r);
++
++static void ngx_http_v3_run_request(ngx_http_request_t *r);
++static ngx_int_t ngx_http_v3_process_request_body(ngx_http_request_t *r,
++ ngx_uint_t do_read, ngx_uint_t last);
++static ngx_int_t ngx_http_v3_filter_request_body(ngx_http_request_t *r);
++static void ngx_http_v3_read_client_request_body_handler(ngx_http_request_t *r);
++
++static ngx_chain_t *ngx_http_v3_send_chain(ngx_connection_t *fc,
++ ngx_chain_t *in, off_t limit);
++
++static void ngx_http_v3_finalize_connection(ngx_http_v3_connection_t *h3c,
++ ngx_uint_t status);
++
++static void ngx_http_v3_pool_cleanup(void *data);
++
++
++static ngx_http_v3_parse_header_t ngx_http_v3_parse_headers[] = {
++ { ngx_string("host"),
++ offsetof(ngx_http_headers_in_t, host), 0, NULL },
++
++ { ngx_string("accept-encoding"),
++ offsetof(ngx_http_headers_in_t, accept_encoding), 0, NULL },
++
++ { ngx_string("accept-language"),
++ offsetof(ngx_http_headers_in_t, accept_language), 0, NULL },
++
++ { ngx_string("user-agent"),
++ offsetof(ngx_http_headers_in_t, user_agent), 0, NULL },
++
++ { ngx_null_string, 0, 0, NULL }
++};
++
++
++void
++ngx_http_v3_init(ngx_event_t *rev)
++{
++ ngx_connection_t *c;
++ ngx_pool_cleanup_t *cln;
++ ngx_http_connection_t *hc;
++ ngx_http_v3_srv_conf_t *h3scf;
++ ngx_http_v3_connection_t *h3c;
++
++ c = rev->data;
++ hc = c->data;
++
++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "init http3 connection");
++
++ c->log->action = "processing HTTP/3 connection";
++
++ h3c = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_connection_t));
++ if (h3c == NULL) {
++ ngx_http_close_connection(c);
++ return;
++ }
++
++ h3scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module);
++
++ h3c->h3 = quiche_h3_conn_new_with_transport(c->quic->conn, h3scf->http3);
++ if (h3c->h3 == NULL) {
++ ngx_http_close_connection(c);
++ return;
++ }
++
++ h3c->http_connection = hc;
++
++ h3c->connection = c;
++
++ h3c->pool = c->pool;
++
++ c->data = h3c;
++
++ c->quic->handler = ngx_http_v3_handler;
++
++ cln = ngx_pool_cleanup_add(c->pool, 0);
++ if (cln == NULL) {
++ ngx_http_close_connection(c);
++ return;
++ }
++
++ cln->handler = ngx_http_v3_pool_cleanup;
++ cln->data = h3c;
++
++ ngx_rbtree_init(&h3c->streams, &h3c->streams_sentinel,
++ ngx_rbtree_insert_value);
++}
++
++
++static int
++ngx_http_v3_for_each_header(uint8_t *name, size_t name_len,
++ uint8_t *value, size_t value_len, void *argp)
++{
++ ngx_int_t rc;
++ ngx_table_elt_t *h;
++ ngx_http_header_t *hh;
++ ngx_http_request_t *r;
++ ngx_http_v3_header_t header;
++ ngx_http_core_srv_conf_t *cscf;
++ ngx_http_core_main_conf_t *cmcf;
++
++ static ngx_str_t cookie = ngx_string("cookie");
++
++ r = argp;
++
++ /* Duplicate the header name because we don't own it. */
++ header.name.data = ngx_pnalloc(r->pool, name_len);
++ if (header.name.data == NULL) {
++ return NGX_ERROR;
++ }
++ header.name.len = name_len;
++
++ ngx_memcpy(header.name.data, name, name_len);
++
++ /* Duplicate the header value because we don't own it. Some of the
++ * functions that process headers require a NULL-terminated string,
++ * so allocate enough memory for that. */
++ header.value.data = ngx_pcalloc(r->pool, value_len + 1);
++ if (header.value.data == NULL) {
++ return NGX_ERROR;
++ }
++ header.value.len = value_len;
++
++ ngx_memcpy(header.value.data, value, value_len);
++
++ if (ngx_http_v3_validate_header(r, &header) != NGX_OK) {
++ return NGX_ERROR;
++ }
++
++ /* Check for pseudo-header. */
++ if (header.name.data[0] == ':') {
++ rc = ngx_http_v3_pseudo_header(r, &header);
++
++ if (rc == NGX_OK) {
++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
++ "http3 header: \":%V: %V\"",
++ &header.name, &header.value);
++
++ return NGX_OK;
++ }
++
++ return NGX_ERROR;
++ }
++
++ if (r->invalid_header) {
++ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
++
++ if (cscf->ignore_invalid_headers) {
++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
++ "client sent invalid header: \"%V\"", &header.name);
++
++ return NGX_ERROR;
++ }
++ }
++
++ /* Handle Cookie header separately. Not sure why, but the HTTP/2 code does
++ * the same. */
++ if (header.name.len == cookie.len
++ && ngx_memcmp(header.name.data, cookie.data, cookie.len) == 0)
++ {
++ if (ngx_http_v3_cookie(r, &header) != NGX_OK) {
++ return NGX_ERROR;
++ }
++
++ } else {
++ h = ngx_list_push(&r->headers_in.headers);
++ if (h == NULL) {
++ return NGX_ERROR;
++ }
++
++ h->key.len = header.name.len;
++ h->key.data = header.name.data;
++
++ /*
++ * TODO Optimization: precalculate hash
++ * and handler for indexed headers.
++ */
++ h->hash = ngx_hash_key(h->key.data, h->key.len);
++
++ h->value.len = header.value.len;
++ h->value.data = header.value.data;
++
++ h->lowcase_key = h->key.data;
++
++ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
++
++ hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,
++ h->lowcase_key, h->key.len);
++
++ if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
++ return NGX_ERROR;
++ }
++ }
++
++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
++ "http3 header: \"%V: %V\"",
++ &header.name, &header.value);
++
++ return NGX_OK;
++}
++
++
++static void
++ngx_http_v3_process_headers(ngx_connection_t *c, quiche_h3_event *ev,
++ int64_t stream_id)
++{
++ int rc;
++ ngx_http_v3_stream_t *stream;
++ ngx_http_v3_srv_conf_t *h3scf;
++ ngx_http_v3_connection_t *h3c;
++
++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 process headers");
++
++ h3c = c->data;
++
++ h3scf = ngx_http_get_module_srv_conf(h3c->http_connection->conf_ctx,
++ ngx_http_v3_module);
++
++ if (h3c->connection->requests >= h3scf->max_requests) {
++ ngx_http_v3_finalize_connection(h3c, NGX_HTTP_V3_NO_ERROR);
++ return;
++ }
++
++ /* Create a new stream to handle the incoming request. */
++ stream = ngx_http_v3_create_stream(h3c);
++ if (stream == NULL) {
++ ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to create HTTP/3 stream");
++
++ ngx_http_v3_finalize_connection(h3c, NGX_HTTP_V3_INTERNAL_ERROR);
++ return;
++ }
++
++ stream->id = stream_id;
++
++ stream->node.key = stream_id;
++
++ ngx_rbtree_insert(&h3c->streams, &stream->node);
++
++ /* Populate ngx_http_request_t from raw HTTP/3 headers. */
++ rc = quiche_h3_event_for_each_header(ev,
++ ngx_http_v3_for_each_header, stream->request);
++
++ if (rc != NGX_OK) {
++ ngx_log_error(NGX_LOG_ERR, c->log, 0,
++ "received invalid HTTP/3 headers");
++
++ ngx_http_v3_finalize_connection(h3c, NGX_HTTP_V3_INTERNAL_ERROR);
++ return;
++ }
++
++ stream->in_closed = !quiche_h3_event_headers_has_body(ev);
++
++ ngx_http_v3_run_request(stream->request);
++}
++
++
++static ngx_int_t
++ngx_http_v3_process_data(ngx_connection_t *c, int64_t stream_id)
++{
++ int rc;
++ ngx_http_request_t *r;
++ ngx_http_v3_stream_t *stream;
++ ngx_http_v3_connection_t *h3c;
++
++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 process data");
++
++ h3c = c->data;
++
++ stream = ngx_http_v3_stream_lookup(h3c, stream_id);
++
++ if (stream == NULL) {
++
++ return NGX_OK;
++ }
++
++ if (stream->skip_data) {
++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
++ "skipping http3 DATA frame");
++
++ return NGX_OK;
++ }
++
++ r = stream->request;
++
++ if (!r->request_body) {
++ return NGX_AGAIN;
++ }
++
++ rc = ngx_http_v3_process_request_body(r, 1, stream->in_closed);
++
++ if (rc == NGX_AGAIN) {
++ return NGX_AGAIN;
++ }
++
++ if (rc != NGX_OK) {
++ stream->skip_data = 1;
++ ngx_http_finalize_request(r, rc);
++ }
++
++ return NGX_OK;
++}
++
++
++static void
++ngx_http_v3_process_blocked_streams(ngx_http_v3_connection_t *h3c)
++{
++ ngx_event_t *wev;
++ quiche_stream_iter *writable;
++ ngx_http_v3_stream_t *stream;
++ uint64_t stream_id;
++
++ writable = quiche_conn_writable(h3c->connection->quic->conn);
++
++ while (quiche_stream_iter_next(writable, &stream_id)) {
++ stream = ngx_http_v3_stream_lookup(h3c, stream_id);
++
++ if (stream == NULL) {
++ continue;
++ }
++
++ if (!stream->blocked) {
++ continue;
++ }
++
++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h3c->connection->log, 0,
++ "http3 stream unblocked %ui", stream->id);
++
++ stream->blocked = 0;
++
++ wev = stream->request->connection->write;
++
++ wev->active = 0;
++ wev->ready = 1;
++
++ if (!wev->delayed) {
++ wev->handler(wev);
++ }
++ }
++
++ quiche_stream_iter_free(writable);
++}
++
++
++static void
++ngx_http_v3_handler(ngx_connection_t *c)
++{
++ ngx_http_v3_connection_t *h3c;
++ ngx_http_v3_stream_t *stream;
++
++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 handler");
++
++ h3c = c->data;
++
++ if (c->error) {
++ ngx_http_v3_finalize_connection(h3c, 0);
++ return;
++ }
++
++ ngx_http_v3_process_blocked_streams(h3c);
++
++ while (!c->error) {
++ quiche_h3_event *ev;
++
++ int64_t stream_id = quiche_h3_conn_poll(h3c->h3, c->quic->conn, &ev);
++ if (stream_id < 0) {
++ break;
++ }
++
++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h3c->connection->log, 0,
++ "http3 event stream:%ui ev:%ui", stream_id,
++ quiche_h3_event_type(ev));
++
++ switch (quiche_h3_event_type(ev)) {
++ case QUICHE_H3_EVENT_HEADERS: {
++ ngx_http_v3_process_headers(c, ev, stream_id);
++ break;
++ }
++
++ case QUICHE_H3_EVENT_DATA: {
++ if (ngx_http_v3_process_data(c, stream_id) == NGX_AGAIN) {
++ quiche_h3_event_free(ev);
++ return;
++ }
++
++ break;
++ }
++
++ case QUICHE_H3_EVENT_FINISHED: {
++ /* Lookup stream. If there isn't one, it means it has already
++ * been closed, so ignore the event. */
++ stream = ngx_http_v3_stream_lookup(h3c, stream_id);
++
++ if (stream != NULL && !stream->in_closed) {
++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
++ "http3 finished");
++
++ stream->in_closed = 1;
++
++ /* Flush request body that was buffered. */
++ if (stream->request->request_body) {
++ ngx_http_v3_process_request_body(stream->request, 0, 1);
++ }
++ }
++
++ break;
++ }
++ }
++
++ quiche_h3_event_free(ev);
++ }
++}
++
++
++static ngx_http_v3_stream_t *
++ngx_http_v3_create_stream(ngx_http_v3_connection_t *h3c)
++{
++ ngx_log_t *log;
++ ngx_event_t *rev, *wev;
++ ngx_connection_t *fc;
++ ngx_http_log_ctx_t *ctx;
++ ngx_http_request_t *r;
++ ngx_http_v3_stream_t *stream;
++ ngx_http_core_srv_conf_t *cscf;
++
++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h3c->connection->log, 0,
++ "http3 create stream");
++
++ fc = h3c->free_fake_connections;
++
++ if (fc) {
++ h3c->free_fake_connections = fc->data;
++
++ rev = fc->read;
++ wev = fc->write;
++ log = fc->log;
++ ctx = log->data;
++
++ } else {
++ fc = ngx_palloc(h3c->pool, sizeof(ngx_connection_t));
++ if (fc == NULL) {
++ return NULL;
++ }
++
++ rev = ngx_palloc(h3c->pool, sizeof(ngx_event_t));
++ if (rev == NULL) {
++ return NULL;
++ }
++
++ wev = ngx_palloc(h3c->pool, sizeof(ngx_event_t));
++ if (wev == NULL) {
++ return NULL;
++ }
++
++ log = ngx_palloc(h3c->pool, sizeof(ngx_log_t));
++ if (log == NULL) {
++ return NULL;
++ }
++
++ ctx = ngx_palloc(h3c->pool, sizeof(ngx_http_log_ctx_t));
++ if (ctx == NULL) {
++ return NULL;
++ }
++
++ ctx->connection = fc;
++ ctx->request = NULL;
++ ctx->current_request = NULL;
++ }
++
++ ngx_memcpy(log, h3c->connection->log, sizeof(ngx_log_t));
++
++ log->data = ctx;
++
++ ngx_memzero(rev, sizeof(ngx_event_t));
++
++ rev->data = fc;
++ rev->ready = 1;
++ rev->handler = ngx_http_v3_close_stream_handler;
++ rev->log = log;
++
++ ngx_memcpy(wev, rev, sizeof(ngx_event_t));
++
++ wev->write = 1;
++
++ ngx_memcpy(fc, h3c->connection, sizeof(ngx_connection_t));
++
++ fc->data = h3c->http_connection;
++ fc->quic = h3c->connection->quic;
++ fc->read = rev;
++ fc->write = wev;
++ fc->sent = 0;
++ fc->buffer = NULL;
++ fc->log = log;
++ fc->buffered = 0;
++ fc->sndlowat = 1;
++ fc->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;
++
++ fc->send_chain = ngx_http_v3_send_chain;
++ fc->need_last_buf = 1;
++
++ r = ngx_http_create_request(fc);
++ if (r == NULL) {
++ return NULL;
++ }
++
++ ngx_str_set(&r->http_protocol, "HTTP/3");
++
++ r->http_version = NGX_HTTP_VERSION_3;
++ r->valid_location = 1;
++
++ fc->data = r;
++ h3c->connection->requests++;
++
++ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
++
++ r->header_in = ngx_create_temp_buf(r->pool,
++ cscf->client_header_buffer_size);
++ if (r->header_in == NULL) {
++ ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
++ return NULL;
++ }
++
++ if (ngx_list_init(&r->headers_in.headers, r->pool, 20,
++ sizeof(ngx_table_elt_t))
++ != NGX_OK)
++ {
++ ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
++ return NULL;
++ }
++
++ r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;
++
++ stream = ngx_pcalloc(h3c->pool, sizeof(ngx_http_v3_stream_t));
++ if (stream == NULL) {
++ ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
++ return NULL;
++ }
++
++ r->qstream = stream;
++
++ stream->request = r;
++ stream->connection = h3c;
++
++ h3c->processing++;
++
++ return stream;
++}
++
++
++static ngx_http_v3_stream_t *
++ngx_http_v3_stream_lookup(ngx_http_v3_connection_t *h3c, ngx_uint_t stream_id)
++{
++ ngx_rbtree_node_t *node, *sentinel;
++
++ node = h3c->streams.root;
++ sentinel = h3c->streams.sentinel;
++
++ while (node != sentinel) {
++
++ if (stream_id < node->key) {
++ node = node->left;
++ continue;
++ }
++
++ if (stream_id > node->key) {
++ node = node->right;
++ continue;
++ }
++
++ /* stream_id == node->key */
++
++ return (ngx_http_v3_stream_t *) node;
++ }
++
++ /* not found */
++
++ return NULL;
++}
++
++
++/* The following functions are copied from the HTTP/2 module, and adapted to
++ * work independently. In theory we could refactor the HTTP/2 module to expose
++ * these functions, but that would be fairly invasive and likely cause more
++ * merge conflicts in the future. */
++
++
++static ngx_int_t
++ngx_http_v3_validate_header(ngx_http_request_t *r, ngx_http_v3_header_t *header)
++{
++ u_char ch;
++ ngx_uint_t i;
++ ngx_http_core_srv_conf_t *cscf;
++
++ if (header->name.len == 0) {
++ return NGX_ERROR;
++ }
++
++ r->invalid_header = 0;
++
++ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
++
++ for (i = (header->name.data[0] == ':'); i != header->name.len; i++) {
++ ch = header->name.data[i];
++
++ if ((ch >= 'a' && ch <= 'z')
++ || (ch == '-')
++ || (ch >= '0' && ch <= '9')
++ || (ch == '_' && cscf->underscores_in_headers))
++ {
++ continue;
++ }
++
++ if (ch == '\0' || ch == LF || ch == CR || ch == ':'
++ || (ch >= 'A' && ch <= 'Z'))
++ {
++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
++ "client sent invalid header name: \"%V\"",
++ &header->name);
++
++ return NGX_ERROR;
++ }
++
++ r->invalid_header = 1;
++ }
++
++ for (i = 0; i != header->value.len; i++) {
++ ch = header->value.data[i];
++
++ if (ch == '\0' || ch == LF || ch == CR) {
++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
++ "client sent header \"%V\" with "
++ "invalid value: \"%V\"",
++ &header->name, &header->value);
++
++ return NGX_ERROR;
++ }
++ }
++
++ return NGX_OK;
++}
++
++
++static ngx_int_t
++ngx_http_v3_pseudo_header(ngx_http_request_t *r, ngx_http_v3_header_t *header)
++{
++ header->name.len--;
++ header->name.data++;
++
++ switch (header->name.len) {
++ case 4:
++ if (ngx_memcmp(header->name.data, "path", sizeof("path") - 1)
++ == 0)
++ {
++ return ngx_http_v3_parse_path(r, &header->value);
++ }
++
++ break;
++
++ case 6:
++ if (ngx_memcmp(header->name.data, "method", sizeof("method") - 1)
++ == 0)
++ {
++ return ngx_http_v3_parse_method(r, &header->value);
++ }
++
++ if (ngx_memcmp(header->name.data, "scheme", sizeof("scheme") - 1)
++ == 0)
++ {
++ return ngx_http_v3_parse_scheme(r, &header->value);
++ }
++
++ break;
++
++ case 9:
++ if (ngx_memcmp(header->name.data, "authority", sizeof("authority") - 1)
++ == 0)
++ {
++ return ngx_http_v3_parse_authority(r, &header->value);
++ }
++
++ break;
++ }
++
++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
++ "client sent unknown pseudo-header \":%V\"",
++ &header->name);
++
++ return NGX_DECLINED;
++}
++
++
++static ngx_int_t
++ngx_http_v3_parse_path(ngx_http_request_t *r, ngx_str_t *value)
++{
++ if (r->unparsed_uri.len) {
++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
++ "client sent duplicate :path header");
++
++ return NGX_DECLINED;
++ }
++
++ if (value->len == 0) {
++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
++ "client sent empty :path header");
++
++ return NGX_DECLINED;
++ }
++
++ r->uri_start = value->data;
++ r->uri_end = value->data + value->len;
++
++ if (ngx_http_parse_uri(r) != NGX_OK) {
++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
++ "client sent invalid :path header: \"%V\"", value);
++
++ return NGX_DECLINED;
++ }
++
++ if (ngx_http_process_request_uri(r) != NGX_OK) {
++ /*
++ * request has been finalized already
++ * in ngx_http_process_request_uri()
++ */
++ return NGX_ABORT;
++ }
++
++ return NGX_OK;
++}
++
++
++static ngx_int_t
++ngx_http_v3_parse_method(ngx_http_request_t *r, ngx_str_t *value)
++{
++ size_t k, len;
++ ngx_uint_t n;
++ const u_char *p, *m;
++
++ /*
++ * This array takes less than 256 sequential bytes,
++ * and if typical CPU cache line size is 64 bytes,
++ * it is prefetched for 4 load operations.
++ */
++ static const struct {
++ u_char len;
++ const u_char method[11];
++ uint32_t value;
++ } tests[] = {
++ { 3, "GET", NGX_HTTP_GET },
++ { 4, "POST", NGX_HTTP_POST },
++ { 4, "HEAD", NGX_HTTP_HEAD },
++ { 7, "OPTIONS", NGX_HTTP_OPTIONS },
++ { 8, "PROPFIND", NGX_HTTP_PROPFIND },
++ { 3, "PUT", NGX_HTTP_PUT },
++ { 5, "MKCOL", NGX_HTTP_MKCOL },
++ { 6, "DELETE", NGX_HTTP_DELETE },
++ { 4, "COPY", NGX_HTTP_COPY },
++ { 4, "MOVE", NGX_HTTP_MOVE },
++ { 9, "PROPPATCH", NGX_HTTP_PROPPATCH },
++ { 4, "LOCK", NGX_HTTP_LOCK },
++ { 6, "UNLOCK", NGX_HTTP_UNLOCK },
++ { 5, "PATCH", NGX_HTTP_PATCH },
++ { 5, "TRACE", NGX_HTTP_TRACE }
++ }, *test;
++
++ if (r->method_name.len) {
++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
++ "client sent duplicate :method header");
++
++ return NGX_DECLINED;
++ }
++
++ if (value->len == 0) {
++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
++ "client sent empty :method header");
++
++ return NGX_DECLINED;
++ }
++
++ r->method_name.len = value->len;
++ r->method_name.data = value->data;
++
++ len = r->method_name.len;
++ n = sizeof(tests) / sizeof(tests[0]);
++ test = tests;
++
++ do {
++ if (len == test->len) {
++ p = r->method_name.data;
++ m = test->method;
++ k = len;
++
++ do {
++ if (*p++ != *m++) {
++ goto next;
++ }
++ } while (--k);
++
++ r->method = test->value;
++ return NGX_OK;
++ }
++
++ next:
++ test++;
++
++ } while (--n);
++
++ p = r->method_name.data;
++
++ do {
++ if ((*p < 'A' || *p > 'Z') && *p != '_' && *p != '-') {
++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
++ "client sent invalid method: \"%V\"",
++ &r->method_name);
++
++ return NGX_DECLINED;
++ }
++
++ p++;
++
++ } while (--len);
++
++ return NGX_OK;
++}
++
++
++static ngx_int_t
++ngx_http_v3_parse_scheme(ngx_http_request_t *r, ngx_str_t *value)
++{
++ if (r->schema_start) {
++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
++ "client sent duplicate :scheme header");
++
++ return NGX_DECLINED;
++ }
++
++ if (value->len == 0) {
++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
++ "client sent empty :scheme header");
++
++ return NGX_DECLINED;
++ }
++
++ r->schema_start = value->data;
++ r->schema_end = value->data + value->len;
++
++ return NGX_OK;
++}
++
++
++static ngx_int_t
++ngx_http_v3_parse_authority(ngx_http_request_t *r, ngx_str_t *value)
++{
++ return ngx_http_v3_parse_header(r, &ngx_http_v3_parse_headers[0], value);
++}
++
++
++static ngx_int_t
++ngx_http_v3_parse_header(ngx_http_request_t *r,
++ ngx_http_v3_parse_header_t *header, ngx_str_t *value)
++{
++ ngx_table_elt_t *h;
++ ngx_http_core_main_conf_t *cmcf;
++
++ h = ngx_list_push(&r->headers_in.headers);
++ if (h == NULL) {
++ return NGX_ERROR;
++ }
++
++ h->key.len = header->name.len;
++ h->key.data = header->name.data;
++ h->lowcase_key = header->name.data;
++
++ if (header->hh == NULL) {
++ header->hash = ngx_hash_key(header->name.data, header->name.len);
++
++ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
++
++ header->hh = ngx_hash_find(&cmcf->headers_in_hash, header->hash,
++ h->lowcase_key, h->key.len);
++ if (header->hh == NULL) {
++ return NGX_ERROR;
++ }
++ }
++
++ h->hash = header->hash;
++
++ h->value.len = value->len;
++ h->value.data = value->data;
++
++ if (header->hh->handler(r, h, header->hh->offset) != NGX_OK) {
++ /* header handler has already finalized request */
++ return NGX_ABORT;
++ }
++
++ return NGX_OK;
++}
++
++
++static ngx_int_t
++ngx_http_v3_construct_request_line(ngx_http_request_t *r)
++{
++ u_char *p;
++
++ static const u_char ending[] = " HTTP/3";
++
++ if (r->method_name.len == 0
++ || r->schema_start == NULL
++ || r->unparsed_uri.len == 0)
++ {
++ if (r->method_name.len == 0) {
++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
++ "client sent no :method header");
++
++ } else if (r->schema_start == NULL) {
++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
++ "client sent no :scheme header");
++
++ } else {
++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
++ "client sent no :path header");
++ }
++
++ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
++ return NGX_ERROR;
++ }
++
++ r->request_line.len = r->method_name.len + 1
++ + r->unparsed_uri.len
++ + sizeof(ending) - 1;
++
++ p = ngx_pnalloc(r->pool, r->request_line.len + 1);
++ if (p == NULL) {
++ ngx_http_v3_close_stream(r->qstream, NGX_HTTP_INTERNAL_SERVER_ERROR);
++ return NGX_ERROR;
++ }
++
++ r->request_line.data = p;
++
++ p = ngx_cpymem(p, r->method_name.data, r->method_name.len);
++
++ *p++ = ' ';
++
++ p = ngx_cpymem(p, r->unparsed_uri.data, r->unparsed_uri.len);
++
++ ngx_memcpy(p, ending, sizeof(ending));
++
++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
++ "http3 request line: \"%V\"", &r->request_line);
++
++ return NGX_OK;
++}
++
++
++static ngx_int_t
++ngx_http_v3_cookie(ngx_http_request_t *r, ngx_http_v3_header_t *header)
++{
++ ngx_str_t *val;
++ ngx_array_t *cookies;
++
++ cookies = r->qstream->cookies;
++
++ if (cookies == NULL) {
++ cookies = ngx_array_create(r->pool, 2, sizeof(ngx_str_t));
++ if (cookies == NULL) {
++ return NGX_ERROR;
++ }
++
++ r->qstream->cookies = cookies;
++ }
++
++ val = ngx_array_push(cookies);
++ if (val == NULL) {
++ return NGX_ERROR;
++ }
++
++ val->len = header->value.len;
++ val->data = header->value.data;
++
++ return NGX_OK;
++}
++
++
++static ngx_int_t
++ngx_http_v3_construct_cookie_header(ngx_http_request_t *r)
++{
++ u_char *buf, *p, *end;
++ size_t len;
++ ngx_str_t *vals;
++ ngx_uint_t i;
++ ngx_array_t *cookies;
++ ngx_table_elt_t *h;
++ ngx_http_header_t *hh;
++ ngx_http_core_main_conf_t *cmcf;
++
++ static ngx_str_t cookie = ngx_string("cookie");
++
++ cookies = r->qstream->cookies;
++
++ if (cookies == NULL) {
++ return NGX_OK;
++ }
++
++ vals = cookies->elts;
++
++ i = 0;
++ len = 0;
++
++ do {
++ len += vals[i].len + 2;
++ } while (++i != cookies->nelts);
++
++ len -= 2;
++
++ buf = ngx_pnalloc(r->pool, len + 1);
++ if (buf == NULL) {
++ ngx_http_v3_close_stream(r->qstream, NGX_HTTP_INTERNAL_SERVER_ERROR);
++ return NGX_ERROR;
++ }
++
++ p = buf;
++ end = buf + len;
++
++ for (i = 0; /* void */ ; i++) {
++
++ p = ngx_cpymem(p, vals[i].data, vals[i].len);
++
++ if (p == end) {
++ *p = '\0';
++ break;
++ }
++
++ *p++ = ';'; *p++ = ' ';
++ }
++
++ h = ngx_list_push(&r->headers_in.headers);
++ if (h == NULL) {
++ ngx_http_v3_close_stream(r->qstream, NGX_HTTP_INTERNAL_SERVER_ERROR);
++ return NGX_ERROR;
++ }
++
++ h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(
++ ngx_hash('c', 'o'), 'o'), 'k'), 'i'), 'e');
++
++ h->key.len = cookie.len;
++ h->key.data = cookie.data;
++
++ h->value.len = len;
++ h->value.data = buf;
++
++ h->lowcase_key = cookie.data;
++
++ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
++
++ hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,
++ h->lowcase_key, h->key.len);
++
++ if (hh == NULL) {
++ ngx_http_v3_close_stream(r->qstream, NGX_HTTP_INTERNAL_SERVER_ERROR);
++ return NGX_ERROR;
++ }
++
++ if (hh->handler(r, h, hh->offset) != NGX_OK) {
++ /*
++ * request has been finalized already
++ * in ngx_http_process_multi_header_lines()
++ */
++ return NGX_ERROR;
++ }
++
++ return NGX_OK;
++}
++
++
++static void
++ngx_http_v3_run_request(ngx_http_request_t *r)
++{
++ if (ngx_http_v3_construct_request_line(r) != NGX_OK) {
++ return;
++ }
++
++ if (ngx_http_v3_construct_cookie_header(r) != NGX_OK) {
++ return;
++ }
++
++ r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;
++
++ if (ngx_http_process_request_header(r) != NGX_OK) {
++ return;
++ }
++
++ if (r->headers_in.content_length_n > 0 && r->qstream->in_closed) {
++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
++ "client prematurely closed stream");
++
++ r->qstream->skip_data = 1;
++
++ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
++ return;
++ }
++
++ if (r->headers_in.content_length_n == -1 && !r->qstream->in_closed) {
++ r->headers_in.chunked = 1;
++ }
++
++ ngx_http_process_request(r);
++}
++
++
++ngx_int_t
++ngx_http_v3_read_request_body(ngx_http_request_t *r)
++{
++ off_t len;
++ ngx_http_v3_stream_t *stream;
++ ngx_http_request_body_t *rb;
++ ngx_http_core_loc_conf_t *clcf;
++
++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
++ "http3 read request body");
++
++ stream = r->qstream;
++ rb = r->request_body;
++
++ if (stream->skip_data) {
++ r->request_body_no_buffering = 0;
++ rb->post_handler(r);
++ return NGX_OK;
++ }
++
++ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
++
++ len = r->headers_in.content_length_n;
++
++ if (r->request_body_no_buffering && !stream->in_closed) {
++
++ if (len < 0 || len > (off_t) clcf->client_body_buffer_size) {
++ len = clcf->client_body_buffer_size;
++ }
++
++ rb->buf = ngx_create_temp_buf(r->pool, (size_t) len);
++
++ } else if (len >= 0 && len <= (off_t) clcf->client_body_buffer_size
++ && !r->request_body_in_file_only)
++ {
++ rb->buf = ngx_create_temp_buf(r->pool, (size_t) len);
++
++ } else {
++ rb->buf = ngx_calloc_buf(r->pool);
++
++ if (rb->buf != NULL) {
++ rb->buf->sync = 1;
++ }
++ }
++
++ if (rb->buf == NULL) {
++ stream->skip_data = 1;
++ return NGX_HTTP_INTERNAL_SERVER_ERROR;
++ }
++
++ rb->rest = 1;
++
++ if (stream->in_closed) {
++ r->request_body_no_buffering = 0;
++
++ return ngx_http_v3_process_request_body(r, 0, 1);
++ }
++
++ /* TODO: set timer */
++ ngx_add_timer(r->connection->read, clcf->client_body_timeout);
++
++ r->read_event_handler = ngx_http_v3_read_client_request_body_handler;
++ r->write_event_handler = ngx_http_request_empty_handler;
++
++ return NGX_AGAIN;
++}
++
++
++static ngx_int_t
++ngx_http_v3_process_request_body(ngx_http_request_t *r, ngx_uint_t do_read,
++ ngx_uint_t last)
++{
++ ssize_t len = 0;
++ ngx_buf_t *buf;
++ ngx_int_t rc;
++ ngx_connection_t *c, *fc;
++ ngx_http_v3_connection_t *h3c;
++ ngx_http_request_body_t *rb;
++ ngx_http_core_loc_conf_t *clcf;
++
++ fc = r->connection;
++ h3c = r->qstream->connection;
++ c = h3c->connection;
++
++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 process request body");
++
++ rb = r->request_body;
++ buf = rb->buf;
++
++ if (buf->sync) {
++ buf->pos = buf->start;
++ buf->last = buf->start;
++
++ r->request_body_in_file_only = 1;
++ }
++
++ if (do_read) {
++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
++ "http3 reading %z bytes of request body",
++ buf->end - buf->last);
++
++ if (buf->last == buf->end) {
++ return NGX_AGAIN;
++ }
++
++ len = quiche_h3_recv_body(h3c->h3, c->quic->conn, r->qstream->id,
++ buf->last, buf->end - buf->last);
++
++ if (len == QUICHE_ERR_DONE) {
++ return NGX_AGAIN;
++ }
++
++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
++ "http3 read %z bytes of request body", len);
++
++ buf->last += len;
++ }
++
++ if (last) {
++ rb->rest = 0;
++
++ if (fc->read->timer_set) {
++ ngx_del_timer(fc->read);
++ }
++
++ if (r->request_body_no_buffering) {
++ ngx_post_event(fc->read, &ngx_posted_events);
++ return NGX_OK;
++ }
++
++ rc = ngx_http_v3_filter_request_body(r);
++
++ if (rc != NGX_OK) {
++ return rc;
++ }
++
++ if (buf->sync) {
++ /* prevent reusing this buffer in the upstream module */
++ rb->buf = NULL;
++ }
++
++ if (r->headers_in.chunked) {
++ r->headers_in.content_length_n = rb->received;
++ }
++
++ r->read_event_handler = ngx_http_block_reading;
++ rb->post_handler(r);
++
++ return NGX_OK;
++ }
++
++ if (len == 0) {
++ return NGX_OK;
++ }
++
++ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
++ ngx_add_timer(fc->read, clcf->client_body_timeout);
++
++ if (r->request_body_no_buffering) {
++ ngx_post_event(fc->read, &ngx_posted_events);
++ return NGX_AGAIN;
++ }
++
++ if (buf->sync) {
++ return ngx_http_v3_filter_request_body(r);
++ }
++
++ return NGX_OK;
++}
++
++
++static ngx_int_t
++ngx_http_v3_filter_request_body(ngx_http_request_t *r)
++{
++ ngx_buf_t *b, *buf;
++ ngx_int_t rc;
++ ngx_chain_t *cl;
++ ngx_http_request_body_t *rb;
++ ngx_http_core_loc_conf_t *clcf;
++
++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
++ "http3 filter request body");
++
++ rb = r->request_body;
++ buf = rb->buf;
++
++ if (buf->pos == buf->last && rb->rest) {
++ cl = NULL;
++ goto update;
++ }
++
++ cl = ngx_chain_get_free_buf(r->pool, &rb->free);
++ if (cl == NULL) {
++ return NGX_HTTP_INTERNAL_SERVER_ERROR;
++ }
++
++ b = cl->buf;
++
++ ngx_memzero(b, sizeof(ngx_buf_t));
++
++ if (buf->pos != buf->last) {
++ r->request_length += buf->last - buf->pos;
++ rb->received += buf->last - buf->pos;
++
++ if (r->headers_in.content_length_n != -1) {
++ if (rb->received > r->headers_in.content_length_n) {
++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
++ "client intended to send body data "
++ "larger than declared");
++
++ return NGX_HTTP_BAD_REQUEST;
++ }
++
++ } else {
++ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
++
++ if (clcf->client_max_body_size
++ && rb->received > clcf->client_max_body_size)
++ {
++ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
++ "client intended to send too large chunked body: "
++ "%O bytes", rb->received);
++
++ return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
++ }
++ }
++
++ b->temporary = 1;
++ b->pos = buf->pos;
++ b->last = buf->last;
++ b->start = b->pos;
++ b->end = b->last;
++
++ buf->pos = buf->last;
++ }
++
++ if (!rb->rest) {
++ if (r->headers_in.content_length_n != -1
++ && r->headers_in.content_length_n != rb->received)
++ {
++ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
++ "client prematurely closed stream: "
++ "only %O out of %O bytes of request body received",
++ rb->received, r->headers_in.content_length_n);
++
++ return NGX_HTTP_BAD_REQUEST;
++ }
++
++ b->last_buf = 1;
++ }
++
++ b->tag = (ngx_buf_tag_t) &ngx_http_v3_filter_request_body;
++ b->flush = r->request_body_no_buffering;
++
++update:
++
++ rc = ngx_http_top_request_body_filter(r, cl);
++
++ ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &cl,
++ (ngx_buf_tag_t) &ngx_http_v3_filter_request_body);
++
++ return rc;
++}
++
++
++static void
++ngx_http_v3_read_client_request_body_handler(ngx_http_request_t *r)
++{
++ ngx_connection_t *fc;
++
++ fc = r->connection;
++
++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
++ "http3 read client request body handler");
++
++ if (fc->read->timedout) {
++ ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, "client timed out");
++
++ fc->timedout = 1;
++ r->qstream->skip_data = 1;
++
++ ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
++ return;
++ }
++
++ if (fc->error) {
++ ngx_log_error(NGX_LOG_INFO, fc->log, 0,
++ "client prematurely closed stream");
++
++ r->qstream->skip_data = 1;
++
++ ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST);
++ return;
++ }
++}
++
++
++ngx_int_t
++ngx_http_v3_read_unbuffered_request_body(ngx_http_request_t *r)
++{
++ ngx_buf_t *buf;
++ ngx_int_t rc;
++ ngx_connection_t *fc;
++ ngx_http_v3_stream_t *stream;
++
++ stream = r->qstream;
++ fc = r->connection;
++
++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
++ "http3 read unbuffered request body");
++
++ if (fc->read->timedout) {
++ stream->skip_data = 1;
++ fc->timedout = 1;
++
++ return NGX_HTTP_REQUEST_TIME_OUT;
++ }
++
++ if (fc->error) {
++ stream->skip_data = 1;
++ return NGX_HTTP_BAD_REQUEST;
++ }
++
++ rc = ngx_http_v3_filter_request_body(r);
++
++ if (rc != NGX_OK) {
++ stream->skip_data = 1;
++ return rc;
++ }
++
++ if (!r->request_body->rest) {
++ return NGX_OK;
++ }
++
++ if (r->request_body->busy != NULL) {
++ return NGX_AGAIN;
++ }
++
++ buf = r->request_body->buf;
++
++ buf->pos = buf->start;
++ buf->last = buf->start;
++
++ ngx_post_event(stream->connection->connection->read, &ngx_posted_events);
++
++ return NGX_AGAIN;
++}
++
++
++/* End of functions copied from HTTP/2 module. */
++
++
++ngx_int_t
++ngx_http_v3_send_response(ngx_http_request_t *r)
++{
++ u_char *tmp;
++ u_char status[3], content_len[NGX_OFF_T_LEN],
++ last_modified[sizeof("Wed, 31 Dec 1986 18:00:00 GMT") - 1],
++ addr[NGX_SOCKADDR_STRLEN];
++ size_t len;
++ ngx_array_t *headers;
++ ngx_str_t host, location;
++ ngx_uint_t i, port, fin;
++ ngx_list_part_t *part;
++ ngx_table_elt_t *header;
++ ngx_connection_t *c, *fc;
++ quiche_h3_header *h;
++ ngx_http_v3_connection_t *h3c;
++ ngx_http_core_loc_conf_t *clcf;
++ ngx_http_core_srv_conf_t *cscf;
++
++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
++ "http3 send response");
++
++ fc = r->connection;
++
++ h3c = r->qstream->connection;
++ c = h3c->connection;
++
++ headers = ngx_array_create(r->pool, 1, sizeof(quiche_h3_header));
++ if (headers == NULL) {
++ return NGX_ERROR;
++ }
++
++ /* Generate :status pseudo-header. */
++ {
++ h = ngx_array_push(headers);
++ if (h == NULL) {
++ return NGX_ERROR;
++ }
++
++ h->name = (u_char *) ":status";
++ h->name_len = sizeof(":status") - 1;
++
++ h->value = status;
++ h->value_len =
++ ngx_sprintf(status, "%03ui", r->headers_out.status) - status;
++ }
++
++ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
++
++ /* Generate Server header.*/
++ if (r->headers_out.server == NULL) {
++ h = ngx_array_push(headers);
++ if (h == NULL) {
++ return NGX_ERROR;
++ }
++
++ h->name = (u_char *) "server";
++ h->name_len = sizeof("server") - 1;
++
++ if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {
++ h->value = (u_char *) NGINX_VER;
++ h->value_len = sizeof(NGINX_VER) - 1;
++
++ } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {
++ h->value = (u_char *) NGINX_VER_BUILD;
++ h->value_len = sizeof(NGINX_VER_BUILD) - 1;
++
++ } else {
++ h->value = (u_char *) "nginx";
++ h->value_len = sizeof("nginx") - 1;
++ }
++ }
++
++ /* Generate Date header. */
++ if (r->headers_out.date == NULL) {
++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
++ "http3 output header: \"date: %V\"",
++ &ngx_cached_http_time);
++
++ h = ngx_array_push(headers);
++ if (h == NULL) {
++ return NGX_ERROR;
++ }
++
++ h->name = (u_char *) "date";
++ h->name_len = sizeof("date") - 1;
++
++ h->value = ngx_cached_http_time.data;
++ h->value_len = ngx_cached_http_time.len;
++ }
++
++ /* Generate Content-Type header. */
++ if (r->headers_out.content_type.len) {
++ h = ngx_array_push(headers);
++ if (h == NULL) {
++ return NGX_ERROR;
++ }
++
++ if (r->headers_out.content_type_len == r->headers_out.content_type.len
++ && r->headers_out.charset.len)
++ {
++ len = r->headers_out.content_type.len + sizeof("; charset=") - 1
++ + r->headers_out.charset.len;
++
++ tmp = ngx_pnalloc(r->pool, len);
++ if (tmp == NULL) {
++ return NGX_ERROR;
++ }
++
++ tmp = ngx_cpymem(tmp, r->headers_out.content_type.data,
++ r->headers_out.content_type.len);
++
++ tmp = ngx_cpymem(tmp, "; charset=", sizeof("; charset=") - 1);
++
++ tmp = ngx_cpymem(tmp, r->headers_out.charset.data,
++ r->headers_out.charset.len);
++
++ /* updated r->headers_out.content_type is also needed for logging */
++
++ r->headers_out.content_type.len = len;
++ r->headers_out.content_type.data = tmp - len;
++ }
++
++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
++ "http3 output header: \"content-type: %V\"",
++ &r->headers_out.content_type);
++
++ h->name = (u_char *) "content-type";
++ h->name_len = sizeof("content-type") - 1;
++
++ h->value = r->headers_out.content_type.data;
++ h->value_len = r->headers_out.content_type.len;
++ }
++
++ /* Generate Content-Length header. */
++ if (r->headers_out.content_length == NULL
++ && r->headers_out.content_length_n >= 0)
++ {
++ h = ngx_array_push(headers);
++ if (h == NULL) {
++ return NGX_ERROR;
++ }
++
++ h->name = (u_char *) "content-length";
++ h->name_len = sizeof("content-length") - 1;
++
++ h->value = content_len;
++ h->value_len =
++ ngx_sprintf(content_len, "%O", r->headers_out.content_length_n) -
++ content_len;
++ }
++
++ /* Generate Last-Modified header. */
++ if (r->headers_out.last_modified == NULL
++ && r->headers_out.last_modified_time != -1)
++ {
++ h = ngx_array_push(headers);
++ if (h == NULL) {
++ return NGX_ERROR;
++ }
++
++ ngx_http_time(last_modified, r->headers_out.last_modified_time);
++
++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,
++ "http3 output header: \"last-modified: %*.s\"",
++ sizeof(last_modified), last_modified);
++
++ h->name = (u_char *) "last-modified";
++ h->name_len = sizeof("last-modified") - 1;
++
++ h->value = last_modified;
++ h->value_len = sizeof(last_modified);
++ }
++
++ /* Generate Location header. */
++ if (r->headers_out.location && r->headers_out.location->value.len) {
++
++ if (r->headers_out.location->value.data[0] == '/'
++ && clcf->absolute_redirect)
++ {
++ if (clcf->server_name_in_redirect) {
++ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
++ host = cscf->server_name;
++
++ } else if (r->headers_in.server.len) {
++ host = r->headers_in.server;
++
++ } else {
++ host.len = NGX_SOCKADDR_STRLEN;
++ host.data = addr;
++
++ if (ngx_connection_local_sockaddr(fc, &host, 0) != NGX_OK) {
++ return NGX_ERROR;
++ }
++ }
++
++ port = ngx_inet_get_port(fc->local_sockaddr);
++
++ location.len = sizeof("https://") - 1 + host.len
++ + r->headers_out.location->value.len;
++
++ if (clcf->port_in_redirect) {
++
++#if (NGX_HTTP_SSL)
++ if (fc->ssl)
++ port = (port == 443) ? 0 : port;
++ else
++#endif
++ port = (port == 80) ? 0 : port;
++
++ } else {
++ port = 0;
++ }
++
++ if (port) {
++ location.len += sizeof(":65535") - 1;
++ }
++
++ location.data = ngx_pnalloc(r->pool, location.len);
++ if (location.data == NULL) {
++ return NGX_ERROR;
++ }
++
++ tmp = ngx_cpymem(location.data, "http", sizeof("http") - 1);
++
++#if (NGX_HTTP_SSL)
++ if (fc->ssl) {
++ *tmp++ = 's';
++ }
++#endif
++
++ *tmp++ = ':'; *tmp++ = '/'; *tmp++ = '/';
++ tmp = ngx_cpymem(tmp, host.data, host.len);
++
++ if (port) {
++ tmp = ngx_sprintf(tmp, ":%ui", port);
++ }
++
++ tmp = ngx_cpymem(tmp, r->headers_out.location->value.data,
++ r->headers_out.location->value.len);
++
++ /* update r->headers_out.location->value for possible logging */
++
++ r->headers_out.location->value.len = tmp - location.data;
++ r->headers_out.location->value.data = location.data;
++ ngx_str_set(&r->headers_out.location->key, "Location");
++ }
++
++ r->headers_out.location->hash = 0;
++
++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
++ "http3 output header: \"location: %V\"",
++ &r->headers_out.location->value);
++
++ h = ngx_array_push(headers);
++ if (h == NULL) {
++ return NGX_ERROR;
++ }
++
++ h->name = (u_char *) "location";
++ h->name_len = sizeof("location") - 1;
++
++ h->value = r->headers_out.location->value.data;
++ h->value_len = r->headers_out.location->value.len;
++ }
++
++#if (NGX_HTTP_GZIP)
++ /* Generate Vary header. */
++ if (r->gzip_vary) {
++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
++ "http3 output header: \"vary: Accept-Encoding\"");
++
++ h->name = (u_char *) "vary";
++ h->name_len = sizeof("vary") - 1;
++
++ h->value = (u_char *) "Accept-Encoding";
++ h->value_len = sizeof("Accept-Encoding") - 1;
++ }
++#endif
++
++ part = &r->headers_out.headers.part;
++ header = part->elts;
++
++ /* Generate all other headers. */
++ for (i = 0; /* void */; i++) {
++
++ if (i >= part->nelts) {
++ if (part->next == NULL) {
++ break;
++ }
++
++ part = part->next;
++ header = part->elts;
++ i = 0;
++ }
++
++ if (header[i].hash == 0) {
++ continue;
++ }
++
++ h = ngx_array_push(headers);
++ if (h == NULL) {
++ return NGX_ERROR;
++ }
++
++#if (NGX_DEBUG)
++ if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {
++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,
++ "http3 output header: \"%V: %V\"",
++ &header[i].key, &header[i].value);
++ }
++#endif
++
++ h->name = header[i].key.data;
++ h->name_len = header[i].key.len;
++
++ h->value = header[i].value.data;
++ h->value_len = header[i].value.len;
++ }
++
++ fin = r->header_only
++ || (r->headers_out.content_length_n == 0 && !r->expect_trailers);
++
++ if (quiche_h3_send_response(h3c->h3, c->quic->conn, r->qstream->id,
++ headers->elts, headers->nelts, fin)) {
++ ngx_array_destroy(headers);
++ return NGX_ERROR;
++ }
++
++ ngx_post_event(c->write, &ngx_posted_events);
++
++ ngx_array_destroy(headers);
++
++ return NGX_OK;
++}
++
++
++static ssize_t
++ngx_http_v3_stream_do_send(ngx_connection_t *fc, ngx_buf_t *b, ngx_int_t fin)
++{
++ ssize_t n;
++ ngx_connection_t *c;
++ ngx_http_request_t *r;
++ ngx_http_v3_connection_t *h3c;
++ ngx_http_v3_stream_t *stream;
++
++ uint8_t *buf = b ? b->pos : NULL;
++ size_t buf_len = b ? ngx_buf_size(b) : 0;
++
++ r = fc->data;
++ stream = r->qstream;
++ h3c = stream->connection;
++ c = h3c->connection;
++
++ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, fc->log, 0,
++ "http3 stream %uz to write %uz bytes, fin=%d",
++ stream->id, buf_len, fin);
++
++ n = quiche_h3_send_body(h3c->h3, c->quic->conn, r->qstream->id,
++ buf, buf_len, fin);
++
++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, stream->connection->connection->log, 0,
++ "http3 stream written %z bytes", n);
++
++ if (n == QUICHE_ERR_DONE) {
++ return NGX_AGAIN;
++ }
++
++ if (n < 0) {
++ ngx_log_error(NGX_LOG_ERR, fc->log, 0, "stream write failed: %d", n);
++ return NGX_ERROR;
++ }
++
++ return n;
++}
++
++
++static ngx_chain_t *
++ngx_http_v3_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)
++{
++ ssize_t n, sent;
++ off_t send, prev_send;
++ ngx_uint_t blocked, fin;
++
++ ngx_http_request_t *r;
++ ngx_http_v3_stream_t *stream;
++
++ r = fc->data;
++ stream = r->qstream;
++
++ send = 0;
++
++ blocked = 0;
++
++ while (in) {
++ prev_send = send;
++
++ fin = in->buf->last_buf;
++
++ send += ngx_buf_size(in->buf);
++
++ n = ngx_http_v3_stream_do_send(fc, in->buf, fin);
++
++ if (n == NGX_ERROR) {
++ return NGX_CHAIN_ERROR;
++ }
++
++ sent = (n == NGX_AGAIN) ? 0 : n;
++
++ fc->sent += sent;
++
++ in->buf->pos += sent;
++
++ if (in->buf->pos == in->buf->last) {
++ in = in->next;
++ }
++
++ if (send - prev_send != sent) {
++ blocked = 1;
++ break;
++ }
++
++ if (fin) {
++ stream->out_closed = 1;
++ }
++ }
++
++ if (blocked) {
++ if (!stream->blocked) {
++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, stream->connection->connection->log, 0,
++ "http3 stream blocked %ui", stream->id);
++
++ stream->blocked = 1;
++
++ fc->write->active = 1;
++ fc->write->ready = 0;
++ }
++ }
++
++ ngx_post_event(stream->connection->connection->write, &ngx_posted_events);
++
++ return in;
++}
++
++
++void
++ngx_http_v3_close_stream(ngx_http_v3_stream_t *stream, ngx_int_t rc)
++{
++ ngx_event_t *ev;
++ ngx_connection_t *fc;
++ ngx_http_v3_connection_t *h3c;
++
++ h3c = stream->connection;
++
++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h3c->connection->log, 0,
++ "http3 close stream %ui", stream->id);
++
++ quiche_conn_stream_shutdown(h3c->connection->quic->conn, stream->id,
++ QUICHE_SHUTDOWN_READ, 0);
++
++ ngx_rbtree_delete(&h3c->streams, &stream->node);
++
++ fc = stream->request->connection;
++
++ ngx_http_free_request(stream->request, rc);
++
++ ev = fc->read;
++
++ if (ev->timer_set) {
++ ngx_del_timer(ev);
++ }
++
++ if (ev->posted) {
++ ngx_delete_posted_event(ev);
++ }
++
++ ev = fc->write;
++
++ if (ev->timer_set) {
++ ngx_del_timer(ev);
++ }
++
++ if (ev->posted) {
++ ngx_delete_posted_event(ev);
++ }
++
++ fc->data = h3c->free_fake_connections;
++ h3c->free_fake_connections = fc;
++
++ h3c->processing--;
++}
++
++
++static void
++ngx_http_v3_close_stream_handler(ngx_event_t *ev)
++{
++ ngx_connection_t *fc;
++ ngx_http_request_t *r;
++
++ fc = ev->data;
++ r = fc->data;
++
++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
++ "http3 close stream handler");
++
++ if (ev->timedout) {
++ ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, "client timed out");
++
++ fc->timedout = 1;
++
++ ngx_http_v3_close_stream(r->qstream, NGX_HTTP_REQUEST_TIME_OUT);
++ return;
++ }
++
++ ngx_http_v3_close_stream(r->qstream, 0);
++}
++
++
++static void
++ngx_http_v3_finalize_connection(ngx_http_v3_connection_t *h3c,
++ ngx_uint_t status)
++{
++ ngx_event_t *ev;
++ ngx_connection_t *c, *fc;
++ ngx_rbtree_node_t *node, *root, *sentinel;
++ ngx_http_request_t *r;
++ ngx_http_v3_stream_t *stream;
++
++ c = h3c->connection;
++
++ quiche_conn_close(c->quic->conn, true, status, NULL, 0);
++
++ c->error = 1;
++
++ if (!h3c->processing) {
++ ngx_http_close_connection(c);
++ return;
++ }
++
++ c->read->handler = ngx_http_empty_handler;
++ c->write->handler = ngx_http_empty_handler;
++
++ sentinel = h3c->streams.sentinel;
++
++ /* Close all pending streams / requests. */
++ for ( ;; ) {
++ root = h3c->streams.root;
++
++ if (root == sentinel) {
++ break;
++ }
++
++ node = ngx_rbtree_min(root, sentinel);
++
++ stream = (ngx_http_v3_stream_t *) node;
++
++ r = stream->request;
++ fc = r->connection;
++
++ fc->error = 1;
++
++ ev = fc->read;
++
++ ev->eof = 1;
++ ev->handler(ev);
++ }
++
++ if (h3c->processing) {
++ return;
++ }
++
++ ngx_http_close_connection(c);
++}
++
++
++static void
++ngx_http_v3_pool_cleanup(void *data)
++{
++ ngx_http_v3_connection_t *h3c = data;
++
++ if (h3c->h3) {
++ quiche_h3_conn_free(h3c->h3);
++
++ h3c->h3 = NULL;
++ }
++}
+diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h
+new file mode 100644
+index 000000000..25a41a77b
+--- /dev/null
++++ b/src/http/v3/ngx_http_v3.h
+@@ -0,0 +1,76 @@
++
++/*
++ * Copyright (C) Cloudflare, Inc.
++ */
++
++
++#ifndef _NGX_HTTP_V3_H_INCLUDED_
++#define _NGX_HTTP_V3_H_INCLUDED_
++
++
++#include <ngx_config.h>
++#include <ngx_core.h>
++#include <ngx_http.h>
++#include <ngx_http_v3_module.h>
++
++
++#define NGX_HTTP_V3_ALPN_ADVERTISE "\x05h3-18"
++
++
++typedef struct ngx_http_v3_connection_s ngx_http_v3_connection_t;
++
++
++struct ngx_http_v3_connection_s {
++ quiche_h3_conn *h3;
++
++ ngx_connection_t *connection;
++ ngx_http_connection_t *http_connection;
++
++ ngx_pool_t *pool;
++
++ ngx_uint_t processing;
++
++ ngx_rbtree_t streams;
++ ngx_rbtree_node_t streams_sentinel;
++
++ ngx_connection_t *free_fake_connections;
++};
++
++
++struct ngx_http_v3_stream_s {
++ ngx_rbtree_node_t node;
++
++ uint64_t id;
++
++ ngx_http_request_t *request;
++
++ ngx_http_v3_connection_t *connection;
++
++ ngx_array_t *cookies;
++
++ ngx_http_v3_stream_t *next;
++
++ ngx_uint_t in_closed:1;
++ ngx_uint_t out_closed:1;
++ ngx_uint_t skip_data:1;
++ ngx_uint_t blocked:1;
++};
++
++
++typedef struct {
++ ngx_str_t name;
++ ngx_str_t value;
++} ngx_http_v3_header_t;
++
++
++void ngx_http_v3_init(ngx_event_t *rev);
++
++ngx_int_t ngx_http_v3_read_request_body(ngx_http_request_t *r);
++ngx_int_t ngx_http_v3_read_unbuffered_request_body(ngx_http_request_t *r);
++
++ngx_int_t ngx_http_v3_send_response(ngx_http_request_t *r);
++
++void ngx_http_v3_close_stream(ngx_http_v3_stream_t *stream, ngx_int_t rc);
++
++
++#endif /* _NGX_HTTP_V3_H_INCLUDED_ */
+diff --git a/src/http/v3/ngx_http_v3_filter_module.c b/src/http/v3/ngx_http_v3_filter_module.c
+new file mode 100644
+index 000000000..5bbff8602
+--- /dev/null
++++ b/src/http/v3/ngx_http_v3_filter_module.c
+@@ -0,0 +1,68 @@
++
++/*
++ * Copyright (C) Cloudflare, Inc.
++ */
++
++
++#include <ngx_config.h>
++#include <ngx_core.h>
++#include <ngx_http.h>
++#include <ngx_http_v3_module.h>
++
++
++static ngx_int_t ngx_http_v3_filter_init(ngx_conf_t *cf);
++
++
++static ngx_http_module_t ngx_http_v3_filter_module_ctx = {
++ NULL, /* preconfiguration */
++ ngx_http_v3_filter_init, /* postconfiguration */
++
++ NULL, /* create main configuration */
++ NULL, /* init main configuration */
++
++ NULL, /* create server configuration */
++ NULL, /* merge server configuration */
++
++ NULL, /* create location configuration */
++ NULL /* merge location configuration */
++};
++
++
++ngx_module_t ngx_http_v3_filter_module = {
++ NGX_MODULE_V1,
++ &ngx_http_v3_filter_module_ctx, /* module context */
++ NULL, /* module directives */
++ NGX_HTTP_MODULE, /* module type */
++ NULL, /* init master */
++ NULL, /* init module */
++ NULL, /* init process */
++ NULL, /* init thread */
++ NULL, /* exit thread */
++ NULL, /* exit process */
++ NULL, /* exit master */
++ NGX_MODULE_V1_PADDING
++};
++
++
++static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
++
++
++static ngx_int_t
++ngx_http_v3_header_filter(ngx_http_request_t *r)
++{
++ if (!r->qstream) {
++ return ngx_http_next_header_filter(r);
++ }
++
++ return ngx_http_v3_send_response(r);
++}
++
++
++static ngx_int_t
++ngx_http_v3_filter_init(ngx_conf_t *cf)
++{
++ ngx_http_next_header_filter = ngx_http_top_header_filter;
++ ngx_http_top_header_filter = ngx_http_v3_header_filter;
++
++ return NGX_OK;
++}
+diff --git a/src/http/v3/ngx_http_v3_module.c b/src/http/v3/ngx_http_v3_module.c
+new file mode 100644
+index 000000000..ba6274253
+--- /dev/null
++++ b/src/http/v3/ngx_http_v3_module.c
+@@ -0,0 +1,286 @@
++
++/*
++ * Copyright (C) Cloudflare, Inc.
++ */
++
++
++#include <ngx_config.h>
++#include <ngx_core.h>
++#include <ngx_http.h>
++#include <ngx_http_v3_module.h>
++
++#include <quiche.h>
++
++
++static ngx_int_t ngx_http_v3_add_variables(ngx_conf_t *cf);
++
++static void *ngx_http_v3_create_srv_conf(ngx_conf_t *cf);
++static char *ngx_http_v3_merge_srv_conf(ngx_conf_t *cf,
++ void *parent, void *child);
++
++static ngx_int_t ngx_http_v3_variable(ngx_http_request_t *r,
++ ngx_http_variable_value_t *v, uintptr_t data);
++
++static void ngx_http_v3_cleanup_ctx(void *data);
++
++
++static ngx_command_t ngx_http_v3_commands[] = {
++
++ { ngx_string("http3_max_concurrent_streams"),
++ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
++ ngx_conf_set_num_slot,
++ NGX_HTTP_SRV_CONF_OFFSET,
++ offsetof(ngx_http_v3_srv_conf_t, concurrent_streams),
++ NULL },
++
++ { ngx_string("http3_max_requests"),
++ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
++ ngx_conf_set_num_slot,
++ NGX_HTTP_SRV_CONF_OFFSET,
++ offsetof(ngx_http_v3_srv_conf_t, max_requests),
++ NULL },
++
++ { ngx_string("http3_max_header_size"),
++ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
++ ngx_conf_set_size_slot,
++ NGX_HTTP_SRV_CONF_OFFSET,
++ offsetof(ngx_http_v3_srv_conf_t, max_header_size),
++ NULL },
++
++ { ngx_string("http3_initial_max_data"),
++ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
++ ngx_conf_set_size_slot,
++ NGX_HTTP_SRV_CONF_OFFSET,
++ offsetof(ngx_http_v3_srv_conf_t, max_data),
++ NULL },
++
++ { ngx_string("http3_initial_max_stream_data"),
++ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
++ ngx_conf_set_size_slot,
++ NGX_HTTP_SRV_CONF_OFFSET,
++ offsetof(ngx_http_v3_srv_conf_t, max_stream_data),
++ NULL },
++
++ { ngx_string("http3_idle_timeout"),
++ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
++ ngx_conf_set_msec_slot,
++ NGX_HTTP_SRV_CONF_OFFSET,
++ offsetof(ngx_http_v3_srv_conf_t, idle_timeout),
++ NULL },
++
++ ngx_null_command
++};
++
++
++static ngx_http_module_t ngx_http_v3_module_ctx = {
++ ngx_http_v3_add_variables, /* preconfiguration */
++ NULL, /* postconfiguration */
++
++ NULL, /* create main configuration */
++ NULL, /* init main configuration */
++
++ ngx_http_v3_create_srv_conf, /* create server configuration */
++ ngx_http_v3_merge_srv_conf, /* merge server configuration */
++
++ NULL, /* create location configuration */
++ NULL /* merge location configuration */
++};
++
++
++ngx_module_t ngx_http_v3_module = {
++ NGX_MODULE_V1,
++ &ngx_http_v3_module_ctx, /* module context */
++ ngx_http_v3_commands, /* module directives */
++ NGX_HTTP_MODULE, /* module type */
++ NULL, /* init master */
++ NULL, /* init module */
++ NULL, /* init process */
++ NULL, /* init thread */
++ NULL, /* exit thread */
++ NULL, /* exit process */
++ NULL, /* exit master */
++ NGX_MODULE_V1_PADDING
++};
++
++
++static ngx_http_variable_t ngx_http_v3_variables[] = {
++
++ { ngx_string("http3"), NULL,
++ ngx_http_v3_variable, 0,
++ NGX_HTTP_VAR_CHANGEABLE, 0 },
++
++ ngx_http_null_variable
++};
++
++
++static ngx_int_t
++ngx_http_v3_add_variables(ngx_conf_t *cf)
++{
++ ngx_http_variable_t *var, *v;
++
++ for (v = ngx_http_v3_variables; v->name.len; v++) {
++ var = ngx_http_add_variable(cf, &v->name, v->flags);
++ if (var == NULL) {
++ return NGX_ERROR;
++ }
++
++ var->get_handler = v->get_handler;
++ var->data = v->data;
++ }
++
++ return NGX_OK;
++}
++
++
++static void *
++ngx_http_v3_create_srv_conf(ngx_conf_t *cf)
++{
++ ngx_http_v3_srv_conf_t *h3scf;
++
++ h3scf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v3_srv_conf_t));
++ if (h3scf == NULL) {
++ return NULL;
++ }
++
++ h3scf->idle_timeout = NGX_CONF_UNSET_MSEC;
++ h3scf->max_data = NGX_CONF_UNSET_SIZE;
++ h3scf->max_stream_data = NGX_CONF_UNSET_SIZE;
++ h3scf->max_requests = NGX_CONF_UNSET_UINT;
++ h3scf->max_header_size = NGX_CONF_UNSET_SIZE;
++ h3scf->concurrent_streams = NGX_CONF_UNSET_UINT;
++
++ return h3scf;
++}
++
++
++#if (NGX_DEBUG)
++static void
++quiche_log(const char *line, void *argp)
++{
++ ngx_log_t *log = ngx_cycle->log;
++
++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, line);
++}
++#endif
++
++
++static char *
++ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
++{
++ ngx_http_v3_srv_conf_t *prev = parent;
++ ngx_http_v3_srv_conf_t *conf = child;
++
++ ngx_pool_cleanup_t *cln;
++
++ ngx_conf_merge_msec_value(conf->idle_timeout,
++ prev->idle_timeout, 180000);
++
++ ngx_conf_merge_size_value(conf->max_data,
++ prev->max_data, 10485760);
++
++ ngx_conf_merge_size_value(conf->max_stream_data,
++ prev->max_stream_data, 1048576);
++
++ ngx_conf_merge_uint_value(conf->max_requests,
++ prev->max_requests, 1000);
++
++ ngx_conf_merge_size_value(conf->max_header_size,
++ prev->max_header_size, 16384);
++
++ ngx_conf_merge_uint_value(conf->concurrent_streams,
++ prev->concurrent_streams, 128);
++
++ conf->quic.log = cf->log;
++
++#if (NGX_DEBUG)
++ /* Enable quiche debug logging. quiche commit ceade4 or later is required */
++ quiche_enable_debug_logging(quiche_log, NULL);
++#endif
++
++ if (ngx_quic_create_conf(&conf->quic) != NGX_OK) {
++ return NGX_CONF_ERROR;
++ }
++
++ quiche_config_set_max_idle_timeout(conf->quic.config, conf->idle_timeout);
++
++ quiche_config_set_initial_max_data(conf->quic.config, conf->max_data);
++
++ quiche_config_set_initial_max_stream_data_bidi_remote(conf->quic.config,
++ conf->max_stream_data);
++
++ quiche_config_set_initial_max_stream_data_uni(conf->quic.config,
++ conf->max_stream_data);
++
++ quiche_config_set_initial_max_streams_bidi(conf->quic.config,
++ conf->concurrent_streams);
++
++ /* For HTTP/3 we only need 3 unidirectional streams. */
++ quiche_config_set_initial_max_streams_uni(conf->quic.config, 3);
++
++ conf->http3 = quiche_h3_config_new();
++ if (conf->http3 == NULL) {
++ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
++ "failed to create HTTP/3 config");
++ return NGX_CONF_ERROR;
++ }
++
++ quiche_h3_config_set_max_header_list_size(conf->http3,
++ conf->max_header_size);
++
++ cln = ngx_pool_cleanup_add(cf->pool, 0);
++ if (cln == NULL) {
++ return NGX_CONF_ERROR;
++ }
++
++ cln->handler = ngx_quic_cleanup_ctx;
++ cln->data = &conf->quic;
++
++ cln = ngx_pool_cleanup_add(cf->pool, 0);
++ if (cln == NULL) {
++ return NGX_CONF_ERROR;
++ }
++
++ cln->handler = ngx_http_v3_cleanup_ctx;
++ cln->data = conf->http3;
++
++ return NGX_CONF_OK;
++}
++
++
++static ngx_int_t
++ngx_http_v3_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
++ uintptr_t data)
++{
++ ngx_connection_t *c;
++
++ v->valid = 1;
++ v->no_cacheable = 1;
++ v->not_found = 0;
++
++ c = r->connection;
++ if (c == NULL) {
++ return NGX_ERROR;
++ }
++
++ if (c->quic != NULL) {
++ v->len = sizeof("h3") - 1;
++ v->valid = 1;
++ v->no_cacheable = 0;
++ v->not_found = 0;
++ v->data = (u_char *) "h3";
++
++ return NGX_OK;
++ }
++
++ *v = ngx_http_variable_null_value;
++ return NGX_OK;
++}
++
++
++static void
++ngx_http_v3_cleanup_ctx(void *data)
++{
++ quiche_h3_config *config = data;
++
++ quiche_h3_config_free(config);
++}
+diff --git a/src/http/v3/ngx_http_v3_module.h b/src/http/v3/ngx_http_v3_module.h
+new file mode 100644
+index 000000000..72e189def
+--- /dev/null
++++ b/src/http/v3/ngx_http_v3_module.h
+@@ -0,0 +1,34 @@
++
++/*
++ * Copyright (C) Cloudflare, Inc.
++ */
++
++
++#ifndef _NGX_HTTP_V3_MODULE_H_INCLUDED_
++#define _NGX_HTTP_V3_MODULE_H_INCLUDED_
++
++
++#include <ngx_config.h>
++#include <ngx_core.h>
++
++#include <quiche.h>
++
++
++typedef struct {
++ ngx_quic_t quic;
++
++ quiche_h3_config *http3;
++
++ ngx_msec_t idle_timeout;
++ size_t max_data;
++ size_t max_stream_data;
++ ngx_uint_t max_requests;
++ ngx_uint_t max_header_size;
++ ngx_uint_t concurrent_streams;
++} ngx_http_v3_srv_conf_t;
++
++
++extern ngx_module_t ngx_http_v3_module;
++
++
++#endif /* _NGX_HTTP_V3_MODULE_H_INCLUDED_ */
+--
+2.24.1
+
diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml
index be1367d..492e5ad 100644
--- a/fuzz/Cargo.toml
+++ b/fuzz/Cargo.toml
@@ -8,6 +8,9 @@
[package.metadata]
cargo-fuzz = true
+[profile.dev]
+opt-level = 3
+
[dependencies]
quiche = { path = "..", features = ["fuzzing"] }
lazy_static = "1"
diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile
index 193fef1..ecd2085 100644
--- a/fuzz/Dockerfile
+++ b/fuzz/Dockerfile
@@ -3,6 +3,9 @@
WORKDIR /home/mayhem/
+# Install llvm-symbolizer to have source code information in stack traces
+RUN apt-get update && apt-get install llvm -y
+
COPY ./cert.crt ./
COPY ./cert.key ./
diff --git a/fuzz/corpus/packet_recv_client/0013791927a7d5a5ba059a8d869473c1350fc74e b/fuzz/corpus/packet_recv_client/0013791927a7d5a5ba059a8d869473c1350fc74e
new file mode 100644
index 0000000..4ed46f4
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/0013791927a7d5a5ba059a8d869473c1350fc74e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/00ce7af52d3b634711d75df4bf0ea3c33d509330 b/fuzz/corpus/packet_recv_client/00ce7af52d3b634711d75df4bf0ea3c33d509330
deleted file mode 100644
index 4764739..0000000
--- a/fuzz/corpus/packet_recv_client/00ce7af52d3b634711d75df4bf0ea3c33d509330
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/019391ffb154f7f9a859128a3e8c511788b450ba b/fuzz/corpus/packet_recv_client/019391ffb154f7f9a859128a3e8c511788b450ba
new file mode 100644
index 0000000..437fcd7
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/019391ffb154f7f9a859128a3e8c511788b450ba
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/026cae0e23f759a98d6c156421680cd3a6eded39 b/fuzz/corpus/packet_recv_client/026cae0e23f759a98d6c156421680cd3a6eded39
new file mode 100644
index 0000000..064bb5b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/026cae0e23f759a98d6c156421680cd3a6eded39
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/03334a4d754afde9b6f8f4b5549f2b0124f3dfeb b/fuzz/corpus/packet_recv_client/03334a4d754afde9b6f8f4b5549f2b0124f3dfeb
new file mode 100644
index 0000000..0283953
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/03334a4d754afde9b6f8f4b5549f2b0124f3dfeb
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/03e341860976a327a84948d03caca36c6e5fda4c b/fuzz/corpus/packet_recv_client/03e341860976a327a84948d03caca36c6e5fda4c
new file mode 100644
index 0000000..75657f4
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/03e341860976a327a84948d03caca36c6e5fda4c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/0460b2660dc3161049f3ccb4cbeb2e9a399577ec b/fuzz/corpus/packet_recv_client/0460b2660dc3161049f3ccb4cbeb2e9a399577ec
new file mode 100644
index 0000000..732ef91
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/0460b2660dc3161049f3ccb4cbeb2e9a399577ec
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/04b03d6325c001e1e9dde5975231f0ba3bab71aa b/fuzz/corpus/packet_recv_client/04b03d6325c001e1e9dde5975231f0ba3bab71aa
new file mode 100644
index 0000000..b749b07
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/04b03d6325c001e1e9dde5975231f0ba3bab71aa
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/05276d4714a4ad3cf549bbe7efc2a5c3737619b2 b/fuzz/corpus/packet_recv_client/05276d4714a4ad3cf549bbe7efc2a5c3737619b2
new file mode 100644
index 0000000..62617f4
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/05276d4714a4ad3cf549bbe7efc2a5c3737619b2
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/05823d651a401c1b6d6c3bdaa32e1573c147eaee b/fuzz/corpus/packet_recv_client/05823d651a401c1b6d6c3bdaa32e1573c147eaee
new file mode 100644
index 0000000..0e7bb7b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/05823d651a401c1b6d6c3bdaa32e1573c147eaee
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/0614d68b678cf3181560ce548bbac385237495df b/fuzz/corpus/packet_recv_client/0614d68b678cf3181560ce548bbac385237495df
new file mode 100644
index 0000000..5e625ef
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/0614d68b678cf3181560ce548bbac385237495df
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/07061333f26878771af94d0b9748fd4c8bd6a531 b/fuzz/corpus/packet_recv_client/07061333f26878771af94d0b9748fd4c8bd6a531
new file mode 100644
index 0000000..235a15d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/07061333f26878771af94d0b9748fd4c8bd6a531
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/0758a8cfa939512e907fb8d550ec62c491ed781a b/fuzz/corpus/packet_recv_client/0758a8cfa939512e907fb8d550ec62c491ed781a
new file mode 100644
index 0000000..7c6f0a5
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/0758a8cfa939512e907fb8d550ec62c491ed781a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/07747bd55e7b078fe7075bdd8d8cfec0b387a56b b/fuzz/corpus/packet_recv_client/07747bd55e7b078fe7075bdd8d8cfec0b387a56b
new file mode 100644
index 0000000..030e963
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/07747bd55e7b078fe7075bdd8d8cfec0b387a56b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/07ac78dc6c038f92a4c9f1e80858ce078809e3dd b/fuzz/corpus/packet_recv_client/07ac78dc6c038f92a4c9f1e80858ce078809e3dd
new file mode 100644
index 0000000..54f9dee
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/07ac78dc6c038f92a4c9f1e80858ce078809e3dd
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/07ae2b3df95476b13eaf02ecb2e6bf04ae0842cf b/fuzz/corpus/packet_recv_client/07ae2b3df95476b13eaf02ecb2e6bf04ae0842cf
new file mode 100644
index 0000000..423f8cf
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/07ae2b3df95476b13eaf02ecb2e6bf04ae0842cf
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/080561a160bd0fa795238b128a11524a0a4c0816 b/fuzz/corpus/packet_recv_client/080561a160bd0fa795238b128a11524a0a4c0816
deleted file mode 100644
index 7efab29..0000000
--- a/fuzz/corpus/packet_recv_client/080561a160bd0fa795238b128a11524a0a4c0816
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/088b6eed754400f8a2f8bf86b859a6e52045dda5 b/fuzz/corpus/packet_recv_client/088b6eed754400f8a2f8bf86b859a6e52045dda5
new file mode 100644
index 0000000..578d623
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/088b6eed754400f8a2f8bf86b859a6e52045dda5
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/08d0509f835b9797e233f3490b80b1028ad7a3e5 b/fuzz/corpus/packet_recv_client/08d0509f835b9797e233f3490b80b1028ad7a3e5
new file mode 100644
index 0000000..ee8f9d1
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/08d0509f835b9797e233f3490b80b1028ad7a3e5
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/0a3a0d02a91f7ce676f56a25b5555fcccb043bc3 b/fuzz/corpus/packet_recv_client/0a3a0d02a91f7ce676f56a25b5555fcccb043bc3
deleted file mode 100644
index cf02f64..0000000
--- a/fuzz/corpus/packet_recv_client/0a3a0d02a91f7ce676f56a25b5555fcccb043bc3
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/0a9aa33d455d094f3c7f0e7e7f1d6c5f4d21b206 b/fuzz/corpus/packet_recv_client/0a9aa33d455d094f3c7f0e7e7f1d6c5f4d21b206
new file mode 100644
index 0000000..4d4f0de
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/0a9aa33d455d094f3c7f0e7e7f1d6c5f4d21b206
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/0b0f82a5b077713e26989f942538f0e85a7da9bd b/fuzz/corpus/packet_recv_client/0b0f82a5b077713e26989f942538f0e85a7da9bd
new file mode 100644
index 0000000..68aa632
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/0b0f82a5b077713e26989f942538f0e85a7da9bd
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/0c1a614dbd7370168a721594db6377403e8c31b8 b/fuzz/corpus/packet_recv_client/0c1a614dbd7370168a721594db6377403e8c31b8
new file mode 100644
index 0000000..634fd64
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/0c1a614dbd7370168a721594db6377403e8c31b8
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/0c584a888b8212ac02ac4cca9c442e01a47c43b9 b/fuzz/corpus/packet_recv_client/0c584a888b8212ac02ac4cca9c442e01a47c43b9
new file mode 100644
index 0000000..935e5a6
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/0c584a888b8212ac02ac4cca9c442e01a47c43b9
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/0cbe29dfe09b50ebb5a287dcfc485f13d4b8bb20 b/fuzz/corpus/packet_recv_client/0cbe29dfe09b50ebb5a287dcfc485f13d4b8bb20
new file mode 100644
index 0000000..209d3e3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/0cbe29dfe09b50ebb5a287dcfc485f13d4b8bb20
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/0ccd6699d6aef4a082811c33d51058089a7f18dc b/fuzz/corpus/packet_recv_client/0ccd6699d6aef4a082811c33d51058089a7f18dc
new file mode 100644
index 0000000..eb69d99
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/0ccd6699d6aef4a082811c33d51058089a7f18dc
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/0d49bf751ccc3fd6132b54b2a914b3a022984d86 b/fuzz/corpus/packet_recv_client/0d49bf751ccc3fd6132b54b2a914b3a022984d86
new file mode 100644
index 0000000..c31b9d2
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/0d49bf751ccc3fd6132b54b2a914b3a022984d86
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/0d5a3f4aae9b00a5485bc458e458a477b35e2084 b/fuzz/corpus/packet_recv_client/0d5a3f4aae9b00a5485bc458e458a477b35e2084
new file mode 100644
index 0000000..120b909
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/0d5a3f4aae9b00a5485bc458e458a477b35e2084
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/0dbe05399aa4f3ae881a3311a626cb5ed33458e4 b/fuzz/corpus/packet_recv_client/0dbe05399aa4f3ae881a3311a626cb5ed33458e4
new file mode 100644
index 0000000..6ce51bc
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/0dbe05399aa4f3ae881a3311a626cb5ed33458e4
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/0f4d4798761e3b22377802a4f65d671cc599f5bc b/fuzz/corpus/packet_recv_client/0f4d4798761e3b22377802a4f65d671cc599f5bc
new file mode 100644
index 0000000..91c8131
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/0f4d4798761e3b22377802a4f65d671cc599f5bc
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/0fbfde25e140415d51ee5181f654bf546e84a66c b/fuzz/corpus/packet_recv_client/0fbfde25e140415d51ee5181f654bf546e84a66c
new file mode 100644
index 0000000..40f0617
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/0fbfde25e140415d51ee5181f654bf546e84a66c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/10593f642e73c36cdbb1078fcdf7a3bae5aef766 b/fuzz/corpus/packet_recv_client/10593f642e73c36cdbb1078fcdf7a3bae5aef766
new file mode 100644
index 0000000..95bd9ac
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/10593f642e73c36cdbb1078fcdf7a3bae5aef766
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/11973ce418463e9d1831215fdcfcc24856f61b23 b/fuzz/corpus/packet_recv_client/11973ce418463e9d1831215fdcfcc24856f61b23
new file mode 100644
index 0000000..a591e5c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/11973ce418463e9d1831215fdcfcc24856f61b23
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/11aed70040415951729b00f9f034444b7dcccfb7 b/fuzz/corpus/packet_recv_client/11aed70040415951729b00f9f034444b7dcccfb7
new file mode 100644
index 0000000..77ffe38
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/11aed70040415951729b00f9f034444b7dcccfb7
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/121e56c69b1721e2ceaf15b00b441abbd134af9c b/fuzz/corpus/packet_recv_client/121e56c69b1721e2ceaf15b00b441abbd134af9c
new file mode 100644
index 0000000..f6f7385
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/121e56c69b1721e2ceaf15b00b441abbd134af9c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/12254b31de4fd318b442ac240b68e1657b5902ce b/fuzz/corpus/packet_recv_client/12254b31de4fd318b442ac240b68e1657b5902ce
new file mode 100644
index 0000000..cd14db3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/12254b31de4fd318b442ac240b68e1657b5902ce
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/1227a157b7a3bffe5413eb9d1ec4f095e1521c4d b/fuzz/corpus/packet_recv_client/1227a157b7a3bffe5413eb9d1ec4f095e1521c4d
new file mode 100644
index 0000000..f0a8ab2
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/1227a157b7a3bffe5413eb9d1ec4f095e1521c4d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/1291e727ae7ab73f303e55b6714a2dea217ee311 b/fuzz/corpus/packet_recv_client/1291e727ae7ab73f303e55b6714a2dea217ee311
new file mode 100644
index 0000000..a8b2cb6
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/1291e727ae7ab73f303e55b6714a2dea217ee311
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/13381e2e1e3c702a38b0ad8c04a6a0abb2c5cb7e b/fuzz/corpus/packet_recv_client/13381e2e1e3c702a38b0ad8c04a6a0abb2c5cb7e
deleted file mode 100644
index 98eb305..0000000
--- a/fuzz/corpus/packet_recv_client/13381e2e1e3c702a38b0ad8c04a6a0abb2c5cb7e
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/13b14e9ad14c1dc1e51a3c0a4a5abba9a0c6e82d b/fuzz/corpus/packet_recv_client/13b14e9ad14c1dc1e51a3c0a4a5abba9a0c6e82d
new file mode 100644
index 0000000..3e9cd68
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/13b14e9ad14c1dc1e51a3c0a4a5abba9a0c6e82d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/13df9abe8be67f42cb1110788cc17f9dc1c5cf64 b/fuzz/corpus/packet_recv_client/13df9abe8be67f42cb1110788cc17f9dc1c5cf64
deleted file mode 100644
index 4ff9c04..0000000
--- a/fuzz/corpus/packet_recv_client/13df9abe8be67f42cb1110788cc17f9dc1c5cf64
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/14ac70d8f219db53b5490cf5355c532733523203 b/fuzz/corpus/packet_recv_client/14ac70d8f219db53b5490cf5355c532733523203
new file mode 100644
index 0000000..eeff06f
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/14ac70d8f219db53b5490cf5355c532733523203
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/14bbfe6048304fe02ca6919a3ad9b3ca5039b904 b/fuzz/corpus/packet_recv_client/14bbfe6048304fe02ca6919a3ad9b3ca5039b904
new file mode 100644
index 0000000..60a1339
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/14bbfe6048304fe02ca6919a3ad9b3ca5039b904
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/14e44d00576d71d8ab86f613af877c9405869e2c b/fuzz/corpus/packet_recv_client/14e44d00576d71d8ab86f613af877c9405869e2c
new file mode 100644
index 0000000..c019695
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/14e44d00576d71d8ab86f613af877c9405869e2c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/150ee3ea001ac72ce252571ee9d900b9ec54ffcf b/fuzz/corpus/packet_recv_client/150ee3ea001ac72ce252571ee9d900b9ec54ffcf
new file mode 100644
index 0000000..7a61046
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/150ee3ea001ac72ce252571ee9d900b9ec54ffcf
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/15116bfd911ae76b41ceada3f8a4c56f96c17129 b/fuzz/corpus/packet_recv_client/15116bfd911ae76b41ceada3f8a4c56f96c17129
new file mode 100644
index 0000000..d1449d7
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/15116bfd911ae76b41ceada3f8a4c56f96c17129
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/1556b08bd1ae8f3db5092b7cd1cc6ea1f2fd6acf b/fuzz/corpus/packet_recv_client/1556b08bd1ae8f3db5092b7cd1cc6ea1f2fd6acf
new file mode 100644
index 0000000..3516134
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/1556b08bd1ae8f3db5092b7cd1cc6ea1f2fd6acf
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/1563c2b62d55f4d19f7fec338070b6a1023bc9b4 b/fuzz/corpus/packet_recv_client/1563c2b62d55f4d19f7fec338070b6a1023bc9b4
deleted file mode 100644
index db49338..0000000
--- a/fuzz/corpus/packet_recv_client/1563c2b62d55f4d19f7fec338070b6a1023bc9b4
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/15c7a57add110f4a98be440024a16766ba34b5fe b/fuzz/corpus/packet_recv_client/15c7a57add110f4a98be440024a16766ba34b5fe
deleted file mode 100644
index d6b0a8c..0000000
--- a/fuzz/corpus/packet_recv_client/15c7a57add110f4a98be440024a16766ba34b5fe
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/16eedfacb9f6d5288c06143f8e6c54c0066ac85f b/fuzz/corpus/packet_recv_client/16eedfacb9f6d5288c06143f8e6c54c0066ac85f
deleted file mode 100644
index f8d4784..0000000
--- a/fuzz/corpus/packet_recv_client/16eedfacb9f6d5288c06143f8e6c54c0066ac85f
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/174afa5ccaef59c0bf40def43fad3ec15e398d5c b/fuzz/corpus/packet_recv_client/174afa5ccaef59c0bf40def43fad3ec15e398d5c
new file mode 100644
index 0000000..7369789
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/174afa5ccaef59c0bf40def43fad3ec15e398d5c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/177aa063280a4db2971a07da532a67fd3e7f239c b/fuzz/corpus/packet_recv_client/177aa063280a4db2971a07da532a67fd3e7f239c
new file mode 100644
index 0000000..c5e0f3d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/177aa063280a4db2971a07da532a67fd3e7f239c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/17b6564c75c581c215334120dd5ccb1dc3a4d42a b/fuzz/corpus/packet_recv_client/17b6564c75c581c215334120dd5ccb1dc3a4d42a
new file mode 100644
index 0000000..d92017b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/17b6564c75c581c215334120dd5ccb1dc3a4d42a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/18c3cbdbeb109768b4a6425904f14d33573454d8 b/fuzz/corpus/packet_recv_client/18c3cbdbeb109768b4a6425904f14d33573454d8
new file mode 100644
index 0000000..f9dcb1e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/18c3cbdbeb109768b4a6425904f14d33573454d8
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/1951c1d3ac6c377a13dcdbbf97cd47930dd4b2d6 b/fuzz/corpus/packet_recv_client/1951c1d3ac6c377a13dcdbbf97cd47930dd4b2d6
new file mode 100644
index 0000000..9781403
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/1951c1d3ac6c377a13dcdbbf97cd47930dd4b2d6
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/1974279fb14eb2e6668bda061436ceeaf93e3a93 b/fuzz/corpus/packet_recv_client/1974279fb14eb2e6668bda061436ceeaf93e3a93
new file mode 100644
index 0000000..d2fb575
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/1974279fb14eb2e6668bda061436ceeaf93e3a93
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/19dd461fd1c2588e19f3efc1adf85536edaded5c b/fuzz/corpus/packet_recv_client/19dd461fd1c2588e19f3efc1adf85536edaded5c
new file mode 100644
index 0000000..4643eda
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/19dd461fd1c2588e19f3efc1adf85536edaded5c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/19ddf506a26828cff1f6322c7cc7bb02d31bf95d b/fuzz/corpus/packet_recv_client/19ddf506a26828cff1f6322c7cc7bb02d31bf95d
new file mode 100644
index 0000000..b14cfb1
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/19ddf506a26828cff1f6322c7cc7bb02d31bf95d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/1a53c95f80ee3a9d7e7df7f0043f174b0284d775 b/fuzz/corpus/packet_recv_client/1a53c95f80ee3a9d7e7df7f0043f174b0284d775
new file mode 100644
index 0000000..701f25c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/1a53c95f80ee3a9d7e7df7f0043f174b0284d775
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/1b4016ba45589659fc68c56a815a480e5b0ef3bd b/fuzz/corpus/packet_recv_client/1b4016ba45589659fc68c56a815a480e5b0ef3bd
new file mode 100644
index 0000000..b4ebf0e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/1b4016ba45589659fc68c56a815a480e5b0ef3bd
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/1b9e192936d77f4a8b105f82d9baee66e942bae8 b/fuzz/corpus/packet_recv_client/1b9e192936d77f4a8b105f82d9baee66e942bae8
new file mode 100644
index 0000000..0f0a065
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/1b9e192936d77f4a8b105f82d9baee66e942bae8
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/1bb3c8d4c88cf41aaad0dbdb13314ff5252dfcb5 b/fuzz/corpus/packet_recv_client/1bb3c8d4c88cf41aaad0dbdb13314ff5252dfcb5
new file mode 100644
index 0000000..93c9618
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/1bb3c8d4c88cf41aaad0dbdb13314ff5252dfcb5
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/1bf65ad3ba6ea1290a42f449b481183c406e995e b/fuzz/corpus/packet_recv_client/1bf65ad3ba6ea1290a42f449b481183c406e995e
new file mode 100644
index 0000000..7698d30
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/1bf65ad3ba6ea1290a42f449b481183c406e995e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/1bf9890564b922a9d4777f17e05820990ce61858 b/fuzz/corpus/packet_recv_client/1bf9890564b922a9d4777f17e05820990ce61858
new file mode 100644
index 0000000..e30f867
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/1bf9890564b922a9d4777f17e05820990ce61858
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/1c0d55e715b271aea1e232b060a30986356fa5b2 b/fuzz/corpus/packet_recv_client/1c0d55e715b271aea1e232b060a30986356fa5b2
new file mode 100644
index 0000000..49e0b5a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/1c0d55e715b271aea1e232b060a30986356fa5b2
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/1c279a3543712e347cab805f2228eabc2d70bfd1 b/fuzz/corpus/packet_recv_client/1c279a3543712e347cab805f2228eabc2d70bfd1
new file mode 100644
index 0000000..c3892e3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/1c279a3543712e347cab805f2228eabc2d70bfd1
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/1c6b8690a5461644773b16f1533110c9999a4619 b/fuzz/corpus/packet_recv_client/1c6b8690a5461644773b16f1533110c9999a4619
new file mode 100644
index 0000000..1c0f6a3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/1c6b8690a5461644773b16f1533110c9999a4619
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/1d521fd01fe6db2e7c4ee27bbef5d7b644d2f97d b/fuzz/corpus/packet_recv_client/1d521fd01fe6db2e7c4ee27bbef5d7b644d2f97d
new file mode 100644
index 0000000..68a3be8
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/1d521fd01fe6db2e7c4ee27bbef5d7b644d2f97d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/1d850f92a9214fea394c03abb75086ac804b312a b/fuzz/corpus/packet_recv_client/1d850f92a9214fea394c03abb75086ac804b312a
deleted file mode 100644
index 71b567c..0000000
--- a/fuzz/corpus/packet_recv_client/1d850f92a9214fea394c03abb75086ac804b312a
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/1e2d805f0b422f2561f3a266acb6d44e6cd1827d b/fuzz/corpus/packet_recv_client/1e2d805f0b422f2561f3a266acb6d44e6cd1827d
new file mode 100644
index 0000000..e572997
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/1e2d805f0b422f2561f3a266acb6d44e6cd1827d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/1e53a9bc2ec2f0f17625c75a5ba551f7c0f20778 b/fuzz/corpus/packet_recv_client/1e53a9bc2ec2f0f17625c75a5ba551f7c0f20778
new file mode 100644
index 0000000..1a63a17
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/1e53a9bc2ec2f0f17625c75a5ba551f7c0f20778
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/1eae91555501b7dbd130cef5db9314d34ec1b564 b/fuzz/corpus/packet_recv_client/1eae91555501b7dbd130cef5db9314d34ec1b564
new file mode 100644
index 0000000..e756e17
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/1eae91555501b7dbd130cef5db9314d34ec1b564
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/1ebb052dc15d6f55e777bfb1f992e6b961dd905d b/fuzz/corpus/packet_recv_client/1ebb052dc15d6f55e777bfb1f992e6b961dd905d
new file mode 100644
index 0000000..38daba4
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/1ebb052dc15d6f55e777bfb1f992e6b961dd905d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/1f742cd4f05f3139c8c5515767575bee9a065ae6 b/fuzz/corpus/packet_recv_client/1f742cd4f05f3139c8c5515767575bee9a065ae6
new file mode 100644
index 0000000..7746b95
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/1f742cd4f05f3139c8c5515767575bee9a065ae6
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/1fd9bef29530a79434252c15c692731269bc2d61 b/fuzz/corpus/packet_recv_client/1fd9bef29530a79434252c15c692731269bc2d61
new file mode 100644
index 0000000..a529de5
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/1fd9bef29530a79434252c15c692731269bc2d61
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/21a67a9b8dd17ebfe743f4e196b5aceb75f06038 b/fuzz/corpus/packet_recv_client/21a67a9b8dd17ebfe743f4e196b5aceb75f06038
new file mode 100644
index 0000000..d919c8f
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/21a67a9b8dd17ebfe743f4e196b5aceb75f06038
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/21cb0f955610228ea9f78b72aa5a3821c1212dbb b/fuzz/corpus/packet_recv_client/21cb0f955610228ea9f78b72aa5a3821c1212dbb
new file mode 100644
index 0000000..4cc4e89
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/21cb0f955610228ea9f78b72aa5a3821c1212dbb
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/21dc3368958fc608f24f2645e55489f18adb2fdb b/fuzz/corpus/packet_recv_client/21dc3368958fc608f24f2645e55489f18adb2fdb
new file mode 100644
index 0000000..9bfa6be
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/21dc3368958fc608f24f2645e55489f18adb2fdb
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/221897a467e901d255db0fa13248af7be55e8bae b/fuzz/corpus/packet_recv_client/221897a467e901d255db0fa13248af7be55e8bae
new file mode 100644
index 0000000..9b7995c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/221897a467e901d255db0fa13248af7be55e8bae
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/22a3f8827266e7f4acf795487fafdcd9dcf3e2a9 b/fuzz/corpus/packet_recv_client/22a3f8827266e7f4acf795487fafdcd9dcf3e2a9
deleted file mode 100644
index 16d06c9..0000000
--- a/fuzz/corpus/packet_recv_client/22a3f8827266e7f4acf795487fafdcd9dcf3e2a9
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/23942ac8ade844f329d7e8306ebb9080a5357bf7 b/fuzz/corpus/packet_recv_client/23942ac8ade844f329d7e8306ebb9080a5357bf7
new file mode 100644
index 0000000..ce6dcf5
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/23942ac8ade844f329d7e8306ebb9080a5357bf7
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/246634449c76aff8bb0fed0940a1eeb383f42d1d b/fuzz/corpus/packet_recv_client/246634449c76aff8bb0fed0940a1eeb383f42d1d
new file mode 100644
index 0000000..a411c0b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/246634449c76aff8bb0fed0940a1eeb383f42d1d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/24811cf89b7c408aa52d49c7596d7b8195d23463 b/fuzz/corpus/packet_recv_client/24811cf89b7c408aa52d49c7596d7b8195d23463
new file mode 100644
index 0000000..75b3d5c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/24811cf89b7c408aa52d49c7596d7b8195d23463
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/2481474980ddfa5eb319312ad338418e71b669a8 b/fuzz/corpus/packet_recv_client/2481474980ddfa5eb319312ad338418e71b669a8
new file mode 100644
index 0000000..9af06b1
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/2481474980ddfa5eb319312ad338418e71b669a8
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/24ec7850224c2dbbc5909ba16f1e4616007d56f8 b/fuzz/corpus/packet_recv_client/24ec7850224c2dbbc5909ba16f1e4616007d56f8
new file mode 100644
index 0000000..b5757c9
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/24ec7850224c2dbbc5909ba16f1e4616007d56f8
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/253b2dffb8e3388345da5d1bea10851ac9802f35 b/fuzz/corpus/packet_recv_client/253b2dffb8e3388345da5d1bea10851ac9802f35
new file mode 100644
index 0000000..8c0c4e0
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/253b2dffb8e3388345da5d1bea10851ac9802f35
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/25b9813b06581ed77712ccad6fc0b26ac10b7cc6 b/fuzz/corpus/packet_recv_client/25b9813b06581ed77712ccad6fc0b26ac10b7cc6
new file mode 100644
index 0000000..fa77431
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/25b9813b06581ed77712ccad6fc0b26ac10b7cc6
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/2694224fcfeb0564f21e5e1559d83dcbd0287bea b/fuzz/corpus/packet_recv_client/2694224fcfeb0564f21e5e1559d83dcbd0287bea
deleted file mode 100644
index 6a1c6bf..0000000
--- a/fuzz/corpus/packet_recv_client/2694224fcfeb0564f21e5e1559d83dcbd0287bea
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/27c7282f084ceb9877e18fa5df69cc6a2efc09ef b/fuzz/corpus/packet_recv_client/27c7282f084ceb9877e18fa5df69cc6a2efc09ef
new file mode 100644
index 0000000..51f7843
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/27c7282f084ceb9877e18fa5df69cc6a2efc09ef
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/280d0b015b96d4cd883d8ff5fb3b921309c990f2 b/fuzz/corpus/packet_recv_client/280d0b015b96d4cd883d8ff5fb3b921309c990f2
deleted file mode 100644
index 1fe9220..0000000
--- a/fuzz/corpus/packet_recv_client/280d0b015b96d4cd883d8ff5fb3b921309c990f2
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/2a7203194de1dca5b5656e33c8ad671565746d3f b/fuzz/corpus/packet_recv_client/2a7203194de1dca5b5656e33c8ad671565746d3f
new file mode 100644
index 0000000..bbc2c18
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/2a7203194de1dca5b5656e33c8ad671565746d3f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/2a87ca1d30903256e661dab5a8764e175b9fa71e b/fuzz/corpus/packet_recv_client/2a87ca1d30903256e661dab5a8764e175b9fa71e
new file mode 100644
index 0000000..da1eaf4
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/2a87ca1d30903256e661dab5a8764e175b9fa71e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/2b36ddc5a4b7ab2c3aefdd392039cd80cfb4e2e4 b/fuzz/corpus/packet_recv_client/2b36ddc5a4b7ab2c3aefdd392039cd80cfb4e2e4
deleted file mode 100644
index 9c99212..0000000
--- a/fuzz/corpus/packet_recv_client/2b36ddc5a4b7ab2c3aefdd392039cd80cfb4e2e4
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/2b3c4e0383088a228b67a7d7cc08dbe990a685fe b/fuzz/corpus/packet_recv_client/2b3c4e0383088a228b67a7d7cc08dbe990a685fe
new file mode 100644
index 0000000..83eb87b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/2b3c4e0383088a228b67a7d7cc08dbe990a685fe
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/2b9c33c7eec8795e454872aa1de94aeb83e63a44 b/fuzz/corpus/packet_recv_client/2b9c33c7eec8795e454872aa1de94aeb83e63a44
new file mode 100644
index 0000000..781d182
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/2b9c33c7eec8795e454872aa1de94aeb83e63a44
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/2becdf02c4bd7d70d2ca671cd3f11358e531236e b/fuzz/corpus/packet_recv_client/2becdf02c4bd7d70d2ca671cd3f11358e531236e
new file mode 100644
index 0000000..78bac67
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/2becdf02c4bd7d70d2ca671cd3f11358e531236e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/2c42f906fc74dc6b7202013364ff8835c5cb936c b/fuzz/corpus/packet_recv_client/2c42f906fc74dc6b7202013364ff8835c5cb936c
new file mode 100644
index 0000000..9e2876f
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/2c42f906fc74dc6b7202013364ff8835c5cb936c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/2d072020951e7cc7845febe71d2aeaa2ebf882d4 b/fuzz/corpus/packet_recv_client/2d072020951e7cc7845febe71d2aeaa2ebf882d4
new file mode 100644
index 0000000..92559df
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/2d072020951e7cc7845febe71d2aeaa2ebf882d4
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/2d8a5845c09a670fd440f80c62301a120dee6dc3 b/fuzz/corpus/packet_recv_client/2d8a5845c09a670fd440f80c62301a120dee6dc3
deleted file mode 100644
index 80518a2..0000000
--- a/fuzz/corpus/packet_recv_client/2d8a5845c09a670fd440f80c62301a120dee6dc3
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/2dbe94b84188fa53867e7515f4f7be624260181f b/fuzz/corpus/packet_recv_client/2dbe94b84188fa53867e7515f4f7be624260181f
new file mode 100644
index 0000000..2b065da
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/2dbe94b84188fa53867e7515f4f7be624260181f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/2eaf4d72cbbaaf3c1f6732e30d4c21af0d092b77 b/fuzz/corpus/packet_recv_client/2eaf4d72cbbaaf3c1f6732e30d4c21af0d092b77
new file mode 100644
index 0000000..093ff95
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/2eaf4d72cbbaaf3c1f6732e30d4c21af0d092b77
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/2f4d2786e73c240bbca4625839730bfca71b0a5b b/fuzz/corpus/packet_recv_client/2f4d2786e73c240bbca4625839730bfca71b0a5b
deleted file mode 100644
index b388161..0000000
--- a/fuzz/corpus/packet_recv_client/2f4d2786e73c240bbca4625839730bfca71b0a5b
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/2f69d0b99b0ffce06b02fdd1e84775093ff73433 b/fuzz/corpus/packet_recv_client/2f69d0b99b0ffce06b02fdd1e84775093ff73433
new file mode 100644
index 0000000..b6ced4e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/2f69d0b99b0ffce06b02fdd1e84775093ff73433
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/2f6fd6533a50f9d38a29a04c22bd9a6e97da2891 b/fuzz/corpus/packet_recv_client/2f6fd6533a50f9d38a29a04c22bd9a6e97da2891
new file mode 100644
index 0000000..4344249
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/2f6fd6533a50f9d38a29a04c22bd9a6e97da2891
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/2faab375de2e0b9bd918201a3bc91a52a9bb98e2 b/fuzz/corpus/packet_recv_client/2faab375de2e0b9bd918201a3bc91a52a9bb98e2
new file mode 100644
index 0000000..cfc1fab
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/2faab375de2e0b9bd918201a3bc91a52a9bb98e2
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/2fc05c24a32dc7c0917069b8d01e335c96588b33 b/fuzz/corpus/packet_recv_client/2fc05c24a32dc7c0917069b8d01e335c96588b33
deleted file mode 100644
index 6f22332..0000000
--- a/fuzz/corpus/packet_recv_client/2fc05c24a32dc7c0917069b8d01e335c96588b33
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/31642abb06a171cb38db79dba80beae39f99058c b/fuzz/corpus/packet_recv_client/31642abb06a171cb38db79dba80beae39f99058c
deleted file mode 100644
index ebc09aa..0000000
--- a/fuzz/corpus/packet_recv_client/31642abb06a171cb38db79dba80beae39f99058c
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/316b827a9e55172f244eef8f815f66b4155d92a8 b/fuzz/corpus/packet_recv_client/316b827a9e55172f244eef8f815f66b4155d92a8
new file mode 100644
index 0000000..7cc70c0
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/316b827a9e55172f244eef8f815f66b4155d92a8
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/31f421c120ded0fe15d804c7255c0828a1fc1f4b b/fuzz/corpus/packet_recv_client/31f421c120ded0fe15d804c7255c0828a1fc1f4b
new file mode 100644
index 0000000..f82fedd
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/31f421c120ded0fe15d804c7255c0828a1fc1f4b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/320fee46017f93e2b49a80f9fe26d79b4d109830 b/fuzz/corpus/packet_recv_client/320fee46017f93e2b49a80f9fe26d79b4d109830
new file mode 100644
index 0000000..dfbda2b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/320fee46017f93e2b49a80f9fe26d79b4d109830
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/323466c360970ae6fcd508fe7fba396907f47617 b/fuzz/corpus/packet_recv_client/323466c360970ae6fcd508fe7fba396907f47617
deleted file mode 100644
index 86a535c..0000000
--- a/fuzz/corpus/packet_recv_client/323466c360970ae6fcd508fe7fba396907f47617
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/32910d3477ffe0655ea833b56f9ce1147829cbbc b/fuzz/corpus/packet_recv_client/32910d3477ffe0655ea833b56f9ce1147829cbbc
deleted file mode 100644
index cdd5c1a..0000000
--- a/fuzz/corpus/packet_recv_client/32910d3477ffe0655ea833b56f9ce1147829cbbc
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/329ff19b5d7f86cc26f4082221d72683c13aa74a b/fuzz/corpus/packet_recv_client/329ff19b5d7f86cc26f4082221d72683c13aa74a
new file mode 100644
index 0000000..527d350
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/329ff19b5d7f86cc26f4082221d72683c13aa74a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/32f28d90bc8e5112f232489d036ab45c1ce9e74b b/fuzz/corpus/packet_recv_client/32f28d90bc8e5112f232489d036ab45c1ce9e74b
new file mode 100644
index 0000000..00c19a1
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/32f28d90bc8e5112f232489d036ab45c1ce9e74b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/331b09f021f85d2101895076380db160f9df0415 b/fuzz/corpus/packet_recv_client/331b09f021f85d2101895076380db160f9df0415
deleted file mode 100644
index 12cf704..0000000
--- a/fuzz/corpus/packet_recv_client/331b09f021f85d2101895076380db160f9df0415
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/331e488f5a5bb2cbd7a716019aae9ecbeebb9a4d b/fuzz/corpus/packet_recv_client/331e488f5a5bb2cbd7a716019aae9ecbeebb9a4d
new file mode 100644
index 0000000..55c3b52
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/331e488f5a5bb2cbd7a716019aae9ecbeebb9a4d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/33aed9e4f0b631ee918b874aab912bff76a8bff4 b/fuzz/corpus/packet_recv_client/33aed9e4f0b631ee918b874aab912bff76a8bff4
new file mode 100644
index 0000000..dbfd351
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/33aed9e4f0b631ee918b874aab912bff76a8bff4
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/33e2d611595b6a758304f86d6d44e95cb1eb51ad b/fuzz/corpus/packet_recv_client/33e2d611595b6a758304f86d6d44e95cb1eb51ad
new file mode 100644
index 0000000..47a69fb
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/33e2d611595b6a758304f86d6d44e95cb1eb51ad
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/349a8c9ca9f8e719535449274623a73f6cecbd27 b/fuzz/corpus/packet_recv_client/349a8c9ca9f8e719535449274623a73f6cecbd27
deleted file mode 100644
index b04f927..0000000
--- a/fuzz/corpus/packet_recv_client/349a8c9ca9f8e719535449274623a73f6cecbd27
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/34a2b05238c7fa4dd968e3c9679467f7c75cd8f2 b/fuzz/corpus/packet_recv_client/34a2b05238c7fa4dd968e3c9679467f7c75cd8f2
new file mode 100644
index 0000000..b455f8d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/34a2b05238c7fa4dd968e3c9679467f7c75cd8f2
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/357becfb47bce46135fa02edc9741f9ef329408f b/fuzz/corpus/packet_recv_client/357becfb47bce46135fa02edc9741f9ef329408f
new file mode 100644
index 0000000..d901e2b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/357becfb47bce46135fa02edc9741f9ef329408f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/3622643de5468dad338a9c478890c1034b580cd0 b/fuzz/corpus/packet_recv_client/3622643de5468dad338a9c478890c1034b580cd0
new file mode 100644
index 0000000..f5889d2
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/3622643de5468dad338a9c478890c1034b580cd0
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/363d4c446fef6be2ed09db93a9f7eb9e1a458e1f b/fuzz/corpus/packet_recv_client/363d4c446fef6be2ed09db93a9f7eb9e1a458e1f
new file mode 100644
index 0000000..260aa81
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/363d4c446fef6be2ed09db93a9f7eb9e1a458e1f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/368c8e3da600ed2e83c3080e126af18491e3be9f b/fuzz/corpus/packet_recv_client/368c8e3da600ed2e83c3080e126af18491e3be9f
new file mode 100644
index 0000000..a4808a9
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/368c8e3da600ed2e83c3080e126af18491e3be9f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/3736a9453b76e882b9fe4cf26435f9c681e419b4 b/fuzz/corpus/packet_recv_client/3736a9453b76e882b9fe4cf26435f9c681e419b4
new file mode 100644
index 0000000..60af359
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/3736a9453b76e882b9fe4cf26435f9c681e419b4
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/386288248cf0eb814d438ab03e1e6aefd02ada73 b/fuzz/corpus/packet_recv_client/386288248cf0eb814d438ab03e1e6aefd02ada73
new file mode 100644
index 0000000..cac772e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/386288248cf0eb814d438ab03e1e6aefd02ada73
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/3884f8f614583b841dfbd132e427263b22afbfbf b/fuzz/corpus/packet_recv_client/3884f8f614583b841dfbd132e427263b22afbfbf
new file mode 100644
index 0000000..b49bb42
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/3884f8f614583b841dfbd132e427263b22afbfbf
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/38f42bc12283bca57662977b60b8bc04422604fa b/fuzz/corpus/packet_recv_client/38f42bc12283bca57662977b60b8bc04422604fa
new file mode 100644
index 0000000..ea41b95
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/38f42bc12283bca57662977b60b8bc04422604fa
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/3a0664e9bbc62570d7a3ee51e9cdaeac95d53fa4 b/fuzz/corpus/packet_recv_client/3a0664e9bbc62570d7a3ee51e9cdaeac95d53fa4
new file mode 100644
index 0000000..8d74581
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/3a0664e9bbc62570d7a3ee51e9cdaeac95d53fa4
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/3b0f4ae7ec27393316553310031bebed37e56a20 b/fuzz/corpus/packet_recv_client/3b0f4ae7ec27393316553310031bebed37e56a20
new file mode 100644
index 0000000..32977be
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/3b0f4ae7ec27393316553310031bebed37e56a20
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/3b29728da5e078ab60f549248324409606f5fea4 b/fuzz/corpus/packet_recv_client/3b29728da5e078ab60f549248324409606f5fea4
new file mode 100644
index 0000000..4336ae8
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/3b29728da5e078ab60f549248324409606f5fea4
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/3b5ee036614ab41b3b06ef112f8347e501f3004d b/fuzz/corpus/packet_recv_client/3b5ee036614ab41b3b06ef112f8347e501f3004d
new file mode 100644
index 0000000..66f990e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/3b5ee036614ab41b3b06ef112f8347e501f3004d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/3b715c5d16ebde17a31b2bcb24f59769be125eed b/fuzz/corpus/packet_recv_client/3b715c5d16ebde17a31b2bcb24f59769be125eed
new file mode 100644
index 0000000..9ed680c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/3b715c5d16ebde17a31b2bcb24f59769be125eed
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/3bc5b99c562cb081c76e53eee22da5986ca7f3fd b/fuzz/corpus/packet_recv_client/3bc5b99c562cb081c76e53eee22da5986ca7f3fd
new file mode 100644
index 0000000..cddb7a8
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/3bc5b99c562cb081c76e53eee22da5986ca7f3fd
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/3de8a7f60e8f83b4f9fe48ad0a9db1ac2d841b24 b/fuzz/corpus/packet_recv_client/3de8a7f60e8f83b4f9fe48ad0a9db1ac2d841b24
new file mode 100644
index 0000000..ad64adb
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/3de8a7f60e8f83b4f9fe48ad0a9db1ac2d841b24
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/3e363d15afc613b4d55fb799189175203af5bde6 b/fuzz/corpus/packet_recv_client/3e363d15afc613b4d55fb799189175203af5bde6
new file mode 100644
index 0000000..3a75d03
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/3e363d15afc613b4d55fb799189175203af5bde6
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/3eb193689dfb6a570d2e7dd9c88d6171bd9d00b7 b/fuzz/corpus/packet_recv_client/3eb193689dfb6a570d2e7dd9c88d6171bd9d00b7
new file mode 100644
index 0000000..023db61
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/3eb193689dfb6a570d2e7dd9c88d6171bd9d00b7
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/3ffc44571cfefffd7f894f1c22987cef6eb05d94 b/fuzz/corpus/packet_recv_client/3ffc44571cfefffd7f894f1c22987cef6eb05d94
new file mode 100644
index 0000000..6c9c64b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/3ffc44571cfefffd7f894f1c22987cef6eb05d94
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/4006437bb2426754e2d5c3758ace99d198d030b7 b/fuzz/corpus/packet_recv_client/4006437bb2426754e2d5c3758ace99d198d030b7
new file mode 100644
index 0000000..1800fb6
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/4006437bb2426754e2d5c3758ace99d198d030b7
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/407b0ccc1fc3972e19c3ea74279faa055aafa6cd b/fuzz/corpus/packet_recv_client/407b0ccc1fc3972e19c3ea74279faa055aafa6cd
deleted file mode 100644
index d965c68..0000000
--- a/fuzz/corpus/packet_recv_client/407b0ccc1fc3972e19c3ea74279faa055aafa6cd
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/418de17fab3dffc191ec741fb04ca81b9288f9cd b/fuzz/corpus/packet_recv_client/418de17fab3dffc191ec741fb04ca81b9288f9cd
deleted file mode 100644
index e698f8e..0000000
--- a/fuzz/corpus/packet_recv_client/418de17fab3dffc191ec741fb04ca81b9288f9cd
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/4226b90b03f19f8f47cc853dbeed178f107b0381 b/fuzz/corpus/packet_recv_client/4226b90b03f19f8f47cc853dbeed178f107b0381
new file mode 100644
index 0000000..bc5e38b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/4226b90b03f19f8f47cc853dbeed178f107b0381
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/4239c9c3d1fbc347987116556b163bae019ede1d b/fuzz/corpus/packet_recv_client/4239c9c3d1fbc347987116556b163bae019ede1d
new file mode 100644
index 0000000..836a9a9
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/4239c9c3d1fbc347987116556b163bae019ede1d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/429c1b3273e105b577ec23fa54beb79ffa841e8b b/fuzz/corpus/packet_recv_client/429c1b3273e105b577ec23fa54beb79ffa841e8b
deleted file mode 100644
index ed86ed1..0000000
--- a/fuzz/corpus/packet_recv_client/429c1b3273e105b577ec23fa54beb79ffa841e8b
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/433508039572da9b6054e665ce773bc051b99700 b/fuzz/corpus/packet_recv_client/433508039572da9b6054e665ce773bc051b99700
new file mode 100644
index 0000000..1c622d6
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/433508039572da9b6054e665ce773bc051b99700
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/440bac7e8fa3cc4d84a900a154e2ad4abd3ae638 b/fuzz/corpus/packet_recv_client/440bac7e8fa3cc4d84a900a154e2ad4abd3ae638
new file mode 100644
index 0000000..a939c93
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/440bac7e8fa3cc4d84a900a154e2ad4abd3ae638
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/443f6fb5ee21d97b276c13bb877518b77277cd32 b/fuzz/corpus/packet_recv_client/443f6fb5ee21d97b276c13bb877518b77277cd32
new file mode 100644
index 0000000..873f095
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/443f6fb5ee21d97b276c13bb877518b77277cd32
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/444b2f8d632db7298a7c3bda153c0ee30013cb2e b/fuzz/corpus/packet_recv_client/444b2f8d632db7298a7c3bda153c0ee30013cb2e
new file mode 100644
index 0000000..998bcd0
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/444b2f8d632db7298a7c3bda153c0ee30013cb2e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/446eb024a773245176fc9d1e767fd7ae90c59d5b b/fuzz/corpus/packet_recv_client/446eb024a773245176fc9d1e767fd7ae90c59d5b
new file mode 100644
index 0000000..36cb4f7
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/446eb024a773245176fc9d1e767fd7ae90c59d5b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/44d0c9eab51c3f94fda614d18561afdd4131053e b/fuzz/corpus/packet_recv_client/44d0c9eab51c3f94fda614d18561afdd4131053e
new file mode 100644
index 0000000..64e1c8b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/44d0c9eab51c3f94fda614d18561afdd4131053e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/45261a957e2d738dcff2f6708c24284fd7f65b42 b/fuzz/corpus/packet_recv_client/45261a957e2d738dcff2f6708c24284fd7f65b42
new file mode 100644
index 0000000..b9b2c01
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/45261a957e2d738dcff2f6708c24284fd7f65b42
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/46e5b0e24d7c4cdd47d2c3fc815f4c3d8ef6549e b/fuzz/corpus/packet_recv_client/46e5b0e24d7c4cdd47d2c3fc815f4c3d8ef6549e
new file mode 100644
index 0000000..5e259d0
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/46e5b0e24d7c4cdd47d2c3fc815f4c3d8ef6549e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/46f10822da1ac56bc60decc25b68cd993ef647a0 b/fuzz/corpus/packet_recv_client/46f10822da1ac56bc60decc25b68cd993ef647a0
new file mode 100644
index 0000000..a2f18b8
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/46f10822da1ac56bc60decc25b68cd993ef647a0
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/47274e91c9e50305af79d12d06bfc66a69c562a0 b/fuzz/corpus/packet_recv_client/47274e91c9e50305af79d12d06bfc66a69c562a0
deleted file mode 100644
index 85a46b6..0000000
--- a/fuzz/corpus/packet_recv_client/47274e91c9e50305af79d12d06bfc66a69c562a0
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/4865ad2a8929298dad36d4363c86e2deb3f62b65 b/fuzz/corpus/packet_recv_client/4865ad2a8929298dad36d4363c86e2deb3f62b65
new file mode 100644
index 0000000..2405df9
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/4865ad2a8929298dad36d4363c86e2deb3f62b65
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/48aba89d253d4742360c6ddf10c7d680f42a6c13 b/fuzz/corpus/packet_recv_client/48aba89d253d4742360c6ddf10c7d680f42a6c13
new file mode 100644
index 0000000..f350c21
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/48aba89d253d4742360c6ddf10c7d680f42a6c13
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/49ac83c8009193c46523a00434e539acabbeacde b/fuzz/corpus/packet_recv_client/49ac83c8009193c46523a00434e539acabbeacde
deleted file mode 100644
index 094ebed..0000000
--- a/fuzz/corpus/packet_recv_client/49ac83c8009193c46523a00434e539acabbeacde
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/49bb0454803dd7f6cae579547e18cb316ed5f692 b/fuzz/corpus/packet_recv_client/49bb0454803dd7f6cae579547e18cb316ed5f692
new file mode 100644
index 0000000..a810050
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/49bb0454803dd7f6cae579547e18cb316ed5f692
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/4a29840ac2a91f0ce79f1a0ec629b7999055445a b/fuzz/corpus/packet_recv_client/4a29840ac2a91f0ce79f1a0ec629b7999055445a
new file mode 100644
index 0000000..723aa20
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/4a29840ac2a91f0ce79f1a0ec629b7999055445a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/4af98d21dbe1c6332381d30ed38fa20adc8b54ed b/fuzz/corpus/packet_recv_client/4af98d21dbe1c6332381d30ed38fa20adc8b54ed
new file mode 100644
index 0000000..a1b0c25
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/4af98d21dbe1c6332381d30ed38fa20adc8b54ed
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/4bafb719626082dcd0c37f3a73c1d37c97592067 b/fuzz/corpus/packet_recv_client/4bafb719626082dcd0c37f3a73c1d37c97592067
new file mode 100644
index 0000000..239e5a9
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/4bafb719626082dcd0c37f3a73c1d37c97592067
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/4bb1d4c15c4c4a3d4679101acc5cf5cda2cbbc77 b/fuzz/corpus/packet_recv_client/4bb1d4c15c4c4a3d4679101acc5cf5cda2cbbc77
new file mode 100644
index 0000000..f289bfe
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/4bb1d4c15c4c4a3d4679101acc5cf5cda2cbbc77
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/4c7267da453125f25102f6427da8b45b10b71b92 b/fuzz/corpus/packet_recv_client/4c7267da453125f25102f6427da8b45b10b71b92
deleted file mode 100644
index 72b8d10..0000000
--- a/fuzz/corpus/packet_recv_client/4c7267da453125f25102f6427da8b45b10b71b92
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/4c777cdb3b614793359189e5dbc5a6137bc2d695 b/fuzz/corpus/packet_recv_client/4c777cdb3b614793359189e5dbc5a6137bc2d695
new file mode 100644
index 0000000..66b62a0
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/4c777cdb3b614793359189e5dbc5a6137bc2d695
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/4cfd0dc4969a40d17f8ff6b17ce2d72a554bfc85 b/fuzz/corpus/packet_recv_client/4cfd0dc4969a40d17f8ff6b17ce2d72a554bfc85
new file mode 100644
index 0000000..37f6b2f
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/4cfd0dc4969a40d17f8ff6b17ce2d72a554bfc85
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/4d0a094835a332a6c0db501707df29ad4879da51 b/fuzz/corpus/packet_recv_client/4d0a094835a332a6c0db501707df29ad4879da51
new file mode 100644
index 0000000..180ebbc
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/4d0a094835a332a6c0db501707df29ad4879da51
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/4d195fb4b6323fd1e6b866cea32a13da4097e2e1 b/fuzz/corpus/packet_recv_client/4d195fb4b6323fd1e6b866cea32a13da4097e2e1
new file mode 100644
index 0000000..05c0720
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/4d195fb4b6323fd1e6b866cea32a13da4097e2e1
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/4d6ad5cea9c02cceba576ac895ff184cdfe293e3 b/fuzz/corpus/packet_recv_client/4d6ad5cea9c02cceba576ac895ff184cdfe293e3
deleted file mode 100644
index a972eb0..0000000
--- a/fuzz/corpus/packet_recv_client/4d6ad5cea9c02cceba576ac895ff184cdfe293e3
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/4d74122bfb93e5b94baabb5d8a0692df8b8bccd2 b/fuzz/corpus/packet_recv_client/4d74122bfb93e5b94baabb5d8a0692df8b8bccd2
new file mode 100644
index 0000000..ada32d1
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/4d74122bfb93e5b94baabb5d8a0692df8b8bccd2
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/4d97edae84272bfdab8f856d3d77be767900a5ce b/fuzz/corpus/packet_recv_client/4d97edae84272bfdab8f856d3d77be767900a5ce
new file mode 100644
index 0000000..013710b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/4d97edae84272bfdab8f856d3d77be767900a5ce
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/4da30b337ba835573914ff4528bb1f954142b66f b/fuzz/corpus/packet_recv_client/4da30b337ba835573914ff4528bb1f954142b66f
new file mode 100644
index 0000000..1a7aa82
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/4da30b337ba835573914ff4528bb1f954142b66f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/4dadbac52f202e115150de3277b1153423bc637a b/fuzz/corpus/packet_recv_client/4dadbac52f202e115150de3277b1153423bc637a
new file mode 100644
index 0000000..803d609
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/4dadbac52f202e115150de3277b1153423bc637a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/4e7846639682d5fa316aeb70782e53a248996fbc b/fuzz/corpus/packet_recv_client/4e7846639682d5fa316aeb70782e53a248996fbc
new file mode 100644
index 0000000..892a57d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/4e7846639682d5fa316aeb70782e53a248996fbc
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/4e8913964b7d1200697c62738b9546c39454cf16 b/fuzz/corpus/packet_recv_client/4e8913964b7d1200697c62738b9546c39454cf16
deleted file mode 100644
index c22b2a2..0000000
--- a/fuzz/corpus/packet_recv_client/4e8913964b7d1200697c62738b9546c39454cf16
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/4ecd86708766dc6baf988eeff46d0158aa15f188 b/fuzz/corpus/packet_recv_client/4ecd86708766dc6baf988eeff46d0158aa15f188
new file mode 100644
index 0000000..9404e25
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/4ecd86708766dc6baf988eeff46d0158aa15f188
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/500e8b896c878210ea9f86df17dc537ef827160b b/fuzz/corpus/packet_recv_client/500e8b896c878210ea9f86df17dc537ef827160b
new file mode 100644
index 0000000..d3ebae5
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/500e8b896c878210ea9f86df17dc537ef827160b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/503ef6846e39a5c98658437121964bf526d81fce b/fuzz/corpus/packet_recv_client/503ef6846e39a5c98658437121964bf526d81fce
deleted file mode 100644
index 6257d86..0000000
--- a/fuzz/corpus/packet_recv_client/503ef6846e39a5c98658437121964bf526d81fce
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/506300ce738d1022042fd13228e7df5cb9ae5073 b/fuzz/corpus/packet_recv_client/506300ce738d1022042fd13228e7df5cb9ae5073
new file mode 100644
index 0000000..1306bfa
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/506300ce738d1022042fd13228e7df5cb9ae5073
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/5075be5ce885c49c66f05feffb959a864b222fb7 b/fuzz/corpus/packet_recv_client/5075be5ce885c49c66f05feffb959a864b222fb7
new file mode 100644
index 0000000..18c8071
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/5075be5ce885c49c66f05feffb959a864b222fb7
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/5283910781991721d57fad0cf406310c79fac2b1 b/fuzz/corpus/packet_recv_client/5283910781991721d57fad0cf406310c79fac2b1
new file mode 100644
index 0000000..ed9966a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/5283910781991721d57fad0cf406310c79fac2b1
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/5329a66f680e4067b600fbd5a007b3c62d7b6f05 b/fuzz/corpus/packet_recv_client/5329a66f680e4067b600fbd5a007b3c62d7b6f05
new file mode 100644
index 0000000..c4a2f74
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/5329a66f680e4067b600fbd5a007b3c62d7b6f05
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/53f1924ecf8ee1c5160a8afdbd9cded342cfb697 b/fuzz/corpus/packet_recv_client/53f1924ecf8ee1c5160a8afdbd9cded342cfb697
new file mode 100644
index 0000000..efe8db4
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/53f1924ecf8ee1c5160a8afdbd9cded342cfb697
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/540130ca6e9a4d3ffcae865b70dd92f97633ef20 b/fuzz/corpus/packet_recv_client/540130ca6e9a4d3ffcae865b70dd92f97633ef20
new file mode 100644
index 0000000..45c529a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/540130ca6e9a4d3ffcae865b70dd92f97633ef20
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/5491bfbd48989f427c5471dc07c287426db7d885 b/fuzz/corpus/packet_recv_client/5491bfbd48989f427c5471dc07c287426db7d885
new file mode 100644
index 0000000..1108eae
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/5491bfbd48989f427c5471dc07c287426db7d885
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/54e5a9747864e328135558ed842049776a815470 b/fuzz/corpus/packet_recv_client/54e5a9747864e328135558ed842049776a815470
new file mode 100644
index 0000000..4fc89df
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/54e5a9747864e328135558ed842049776a815470
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/55695f3df849a3a42e871b08e0fb3b27db8f947a b/fuzz/corpus/packet_recv_client/55695f3df849a3a42e871b08e0fb3b27db8f947a
new file mode 100644
index 0000000..4842130
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/55695f3df849a3a42e871b08e0fb3b27db8f947a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/55830785592486a4a53bc4b8073b6fd932da8521 b/fuzz/corpus/packet_recv_client/55830785592486a4a53bc4b8073b6fd932da8521
deleted file mode 100644
index 36c8e90..0000000
--- a/fuzz/corpus/packet_recv_client/55830785592486a4a53bc4b8073b6fd932da8521
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/5647a046cd6da4b38e7973fc70e6aeccb88ab6c4 b/fuzz/corpus/packet_recv_client/5647a046cd6da4b38e7973fc70e6aeccb88ab6c4
new file mode 100644
index 0000000..8a1ee7d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/5647a046cd6da4b38e7973fc70e6aeccb88ab6c4
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/5665d8a33e694362ca1a22c418fe724df0910db8 b/fuzz/corpus/packet_recv_client/5665d8a33e694362ca1a22c418fe724df0910db8
new file mode 100644
index 0000000..df87cbe
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/5665d8a33e694362ca1a22c418fe724df0910db8
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/569b7b2d0a51c450fbd7616598582b3d87f57838 b/fuzz/corpus/packet_recv_client/569b7b2d0a51c450fbd7616598582b3d87f57838
new file mode 100644
index 0000000..0694103
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/569b7b2d0a51c450fbd7616598582b3d87f57838
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/56c70c840d313dd98facde9f33461e9be4e6851d b/fuzz/corpus/packet_recv_client/56c70c840d313dd98facde9f33461e9be4e6851d
new file mode 100644
index 0000000..5ae67bb
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/56c70c840d313dd98facde9f33461e9be4e6851d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/56d1efca20dfa4758baac38a291fc0c020eb51fb b/fuzz/corpus/packet_recv_client/56d1efca20dfa4758baac38a291fc0c020eb51fb
new file mode 100644
index 0000000..fc05bc1
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/56d1efca20dfa4758baac38a291fc0c020eb51fb
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/571805a6cdbd4c7774486d4c4b39c0edd5247e78 b/fuzz/corpus/packet_recv_client/571805a6cdbd4c7774486d4c4b39c0edd5247e78
deleted file mode 100644
index 863843c..0000000
--- a/fuzz/corpus/packet_recv_client/571805a6cdbd4c7774486d4c4b39c0edd5247e78
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/573106df8cbf9742eca6616357c825bdbfeeb0b4 b/fuzz/corpus/packet_recv_client/573106df8cbf9742eca6616357c825bdbfeeb0b4
deleted file mode 100644
index 0726425..0000000
--- a/fuzz/corpus/packet_recv_client/573106df8cbf9742eca6616357c825bdbfeeb0b4
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/5748e54dc5d60919dee5a44079085b7c9a04ee4b b/fuzz/corpus/packet_recv_client/5748e54dc5d60919dee5a44079085b7c9a04ee4b
new file mode 100644
index 0000000..9d643c5
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/5748e54dc5d60919dee5a44079085b7c9a04ee4b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/57778721f6da3c906f744eb96d96275d5ccc7e79 b/fuzz/corpus/packet_recv_client/57778721f6da3c906f744eb96d96275d5ccc7e79
new file mode 100644
index 0000000..1669a89
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/57778721f6da3c906f744eb96d96275d5ccc7e79
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/57d715b6d7cd6aec3910ecf0311d36208910f7ae b/fuzz/corpus/packet_recv_client/57d715b6d7cd6aec3910ecf0311d36208910f7ae
new file mode 100644
index 0000000..45dfe67
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/57d715b6d7cd6aec3910ecf0311d36208910f7ae
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/58b781ff81238051d09d28ed281872c3055de3ea b/fuzz/corpus/packet_recv_client/58b781ff81238051d09d28ed281872c3055de3ea
new file mode 100644
index 0000000..0dada44
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/58b781ff81238051d09d28ed281872c3055de3ea
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/5936bcc135b030bdfe45757976f9610f8f0c047a b/fuzz/corpus/packet_recv_client/5936bcc135b030bdfe45757976f9610f8f0c047a
new file mode 100644
index 0000000..15e445c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/5936bcc135b030bdfe45757976f9610f8f0c047a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/59b354e1be703f8532d12f9a57cdcb673f037dcc b/fuzz/corpus/packet_recv_client/59b354e1be703f8532d12f9a57cdcb673f037dcc
new file mode 100644
index 0000000..ac7bacc
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/59b354e1be703f8532d12f9a57cdcb673f037dcc
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/59e3ec10058814c57e6631d662a77394de15b6d6 b/fuzz/corpus/packet_recv_client/59e3ec10058814c57e6631d662a77394de15b6d6
deleted file mode 100644
index d922023..0000000
--- a/fuzz/corpus/packet_recv_client/59e3ec10058814c57e6631d662a77394de15b6d6
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/59f7611ec8f1371c2027c9b834b196163a7d418c b/fuzz/corpus/packet_recv_client/59f7611ec8f1371c2027c9b834b196163a7d418c
new file mode 100644
index 0000000..bd77bf1
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/59f7611ec8f1371c2027c9b834b196163a7d418c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/5bc9534a59d13c52c009cf389c2864f3793e87f4 b/fuzz/corpus/packet_recv_client/5bc9534a59d13c52c009cf389c2864f3793e87f4
new file mode 100644
index 0000000..6376851
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/5bc9534a59d13c52c009cf389c2864f3793e87f4
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/5cab5628ef88caf1fb3c2592c18d6df13edbb77b b/fuzz/corpus/packet_recv_client/5cab5628ef88caf1fb3c2592c18d6df13edbb77b
new file mode 100644
index 0000000..728db50
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/5cab5628ef88caf1fb3c2592c18d6df13edbb77b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/5cbb9fee5099b02333c132bdff4bec6cbe9ae585 b/fuzz/corpus/packet_recv_client/5cbb9fee5099b02333c132bdff4bec6cbe9ae585
deleted file mode 100644
index 2b7a7af..0000000
--- a/fuzz/corpus/packet_recv_client/5cbb9fee5099b02333c132bdff4bec6cbe9ae585
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/5d836c88f10f286f5447375195a5e56bd97784a3 b/fuzz/corpus/packet_recv_client/5d836c88f10f286f5447375195a5e56bd97784a3
new file mode 100644
index 0000000..65d2512
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/5d836c88f10f286f5447375195a5e56bd97784a3
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/5e8ef92640460ef35a694aeef6aac6dadf85b6e3 b/fuzz/corpus/packet_recv_client/5e8ef92640460ef35a694aeef6aac6dadf85b6e3
new file mode 100644
index 0000000..1b66f0c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/5e8ef92640460ef35a694aeef6aac6dadf85b6e3
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/5ee77ba71a0c33eeed29787fcaf8f18ebc63d7ff b/fuzz/corpus/packet_recv_client/5ee77ba71a0c33eeed29787fcaf8f18ebc63d7ff
new file mode 100644
index 0000000..277446b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/5ee77ba71a0c33eeed29787fcaf8f18ebc63d7ff
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/5f75cc4b3f04db9e6494a096bb91de32e6ef2ba2 b/fuzz/corpus/packet_recv_client/5f75cc4b3f04db9e6494a096bb91de32e6ef2ba2
new file mode 100644
index 0000000..e72d169
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/5f75cc4b3f04db9e6494a096bb91de32e6ef2ba2
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/5f922bd36d9db8c5b8ca422d057339569470494c b/fuzz/corpus/packet_recv_client/5f922bd36d9db8c5b8ca422d057339569470494c
new file mode 100644
index 0000000..a00304b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/5f922bd36d9db8c5b8ca422d057339569470494c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/5fa89d4c709cae01f061ed460b32004d48bead26 b/fuzz/corpus/packet_recv_client/5fa89d4c709cae01f061ed460b32004d48bead26
new file mode 100644
index 0000000..f370fc7
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/5fa89d4c709cae01f061ed460b32004d48bead26
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/5fd56948cd975b426615571f47ec82aba0a443d1 b/fuzz/corpus/packet_recv_client/5fd56948cd975b426615571f47ec82aba0a443d1
new file mode 100644
index 0000000..756aead
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/5fd56948cd975b426615571f47ec82aba0a443d1
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/5fe44ddd5f1ae47fb5ef06153b732d14508f3075 b/fuzz/corpus/packet_recv_client/5fe44ddd5f1ae47fb5ef06153b732d14508f3075
new file mode 100644
index 0000000..735f6c9
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/5fe44ddd5f1ae47fb5ef06153b732d14508f3075
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/5ff4116307f4158deb2835908c9b5a9fafed44b7 b/fuzz/corpus/packet_recv_client/5ff4116307f4158deb2835908c9b5a9fafed44b7
new file mode 100644
index 0000000..268a93a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/5ff4116307f4158deb2835908c9b5a9fafed44b7
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/5ffc1a1ab05a283eac848ae330229d833a2b28e4 b/fuzz/corpus/packet_recv_client/5ffc1a1ab05a283eac848ae330229d833a2b28e4
deleted file mode 100644
index 1680140..0000000
--- a/fuzz/corpus/packet_recv_client/5ffc1a1ab05a283eac848ae330229d833a2b28e4
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/6030e516e48bf6f6bada3bff7dfa8922f627a5e2 b/fuzz/corpus/packet_recv_client/6030e516e48bf6f6bada3bff7dfa8922f627a5e2
new file mode 100644
index 0000000..79fbaac
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/6030e516e48bf6f6bada3bff7dfa8922f627a5e2
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/6065f0af9ba1291d7b9b6ed01c4c66fa15e71ad3 b/fuzz/corpus/packet_recv_client/6065f0af9ba1291d7b9b6ed01c4c66fa15e71ad3
new file mode 100644
index 0000000..1ed180f
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/6065f0af9ba1291d7b9b6ed01c4c66fa15e71ad3
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/60d312e2d4af10d223469f71d00747573571549f b/fuzz/corpus/packet_recv_client/60d312e2d4af10d223469f71d00747573571549f
new file mode 100644
index 0000000..909478e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/60d312e2d4af10d223469f71d00747573571549f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/624c2ae30ea0bb0a73eb63706188a1a1fb208922 b/fuzz/corpus/packet_recv_client/624c2ae30ea0bb0a73eb63706188a1a1fb208922
new file mode 100644
index 0000000..560e5d5
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/624c2ae30ea0bb0a73eb63706188a1a1fb208922
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/62caf733b23cf10770b793aba1b82cce229a3c31 b/fuzz/corpus/packet_recv_client/62caf733b23cf10770b793aba1b82cce229a3c31
new file mode 100644
index 0000000..b1d8a54
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/62caf733b23cf10770b793aba1b82cce229a3c31
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/63542984c7d5d7c079395f5bc8a819195a7404fb b/fuzz/corpus/packet_recv_client/63542984c7d5d7c079395f5bc8a819195a7404fb
new file mode 100644
index 0000000..ac73679
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/63542984c7d5d7c079395f5bc8a819195a7404fb
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/636745c6d9d15ea071f5b4dfafe9eec34ed8fe7b b/fuzz/corpus/packet_recv_client/636745c6d9d15ea071f5b4dfafe9eec34ed8fe7b
new file mode 100644
index 0000000..ddeba88
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/636745c6d9d15ea071f5b4dfafe9eec34ed8fe7b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/63cb5dab8b69d5ea256fad0224852037a4d14e5a b/fuzz/corpus/packet_recv_client/63cb5dab8b69d5ea256fad0224852037a4d14e5a
deleted file mode 100644
index bc31c12..0000000
--- a/fuzz/corpus/packet_recv_client/63cb5dab8b69d5ea256fad0224852037a4d14e5a
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/647c1e1529a97c327287c9279eb68c4a44f0fd35 b/fuzz/corpus/packet_recv_client/647c1e1529a97c327287c9279eb68c4a44f0fd35
new file mode 100644
index 0000000..debf998
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/647c1e1529a97c327287c9279eb68c4a44f0fd35
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/649bc8201f94412f2f723dfb1257384a132e5540 b/fuzz/corpus/packet_recv_client/649bc8201f94412f2f723dfb1257384a132e5540
new file mode 100644
index 0000000..b12fec7
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/649bc8201f94412f2f723dfb1257384a132e5540
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/64fb9f18f712a1b39212214d525716b16ee45ad7 b/fuzz/corpus/packet_recv_client/64fb9f18f712a1b39212214d525716b16ee45ad7
new file mode 100644
index 0000000..3cf1ca7
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/64fb9f18f712a1b39212214d525716b16ee45ad7
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/65dfe7500cd44616301646392f9f7c58b216283b b/fuzz/corpus/packet_recv_client/65dfe7500cd44616301646392f9f7c58b216283b
new file mode 100644
index 0000000..d47b47b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/65dfe7500cd44616301646392f9f7c58b216283b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/666b9f5d260133c0b2de785983f9332bba047038 b/fuzz/corpus/packet_recv_client/666b9f5d260133c0b2de785983f9332bba047038
new file mode 100644
index 0000000..3fb0df9
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/666b9f5d260133c0b2de785983f9332bba047038
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/66ca385762f83f7034631bda00dc9a25a04763bb b/fuzz/corpus/packet_recv_client/66ca385762f83f7034631bda00dc9a25a04763bb
new file mode 100644
index 0000000..2576a69
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/66ca385762f83f7034631bda00dc9a25a04763bb
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/675b06225d0c43b84fffb071e94b9dab29d8999a b/fuzz/corpus/packet_recv_client/675b06225d0c43b84fffb071e94b9dab29d8999a
new file mode 100644
index 0000000..c137093
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/675b06225d0c43b84fffb071e94b9dab29d8999a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/67fd9182c5b2d1e14b4fbc3355b2bdbe95fd6b24 b/fuzz/corpus/packet_recv_client/67fd9182c5b2d1e14b4fbc3355b2bdbe95fd6b24
new file mode 100644
index 0000000..4b2d1ae
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/67fd9182c5b2d1e14b4fbc3355b2bdbe95fd6b24
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/682b2e7b7edd3be7105a70479a2c98af08d4b542 b/fuzz/corpus/packet_recv_client/682b2e7b7edd3be7105a70479a2c98af08d4b542
new file mode 100644
index 0000000..486fba9
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/682b2e7b7edd3be7105a70479a2c98af08d4b542
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/6839183b80590e09026335a1644c0ff809e83f83 b/fuzz/corpus/packet_recv_client/6839183b80590e09026335a1644c0ff809e83f83
new file mode 100644
index 0000000..0c3f43b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/6839183b80590e09026335a1644c0ff809e83f83
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/6874af6a8902e56f6e0a8143bfa52dd385bb74c9 b/fuzz/corpus/packet_recv_client/6874af6a8902e56f6e0a8143bfa52dd385bb74c9
new file mode 100644
index 0000000..1f53fc7
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/6874af6a8902e56f6e0a8143bfa52dd385bb74c9
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/6aee353b532246f4e53b2e9ef7935e13de92ef8e b/fuzz/corpus/packet_recv_client/6aee353b532246f4e53b2e9ef7935e13de92ef8e
new file mode 100644
index 0000000..9d7c656
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/6aee353b532246f4e53b2e9ef7935e13de92ef8e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/6ba9b232ae4a7981a94865177c4dabaca58359c9 b/fuzz/corpus/packet_recv_client/6ba9b232ae4a7981a94865177c4dabaca58359c9
new file mode 100644
index 0000000..29a9f3e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/6ba9b232ae4a7981a94865177c4dabaca58359c9
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/6d52e07de58b76e85b4c1c0c967fc5bc63756a47 b/fuzz/corpus/packet_recv_client/6d52e07de58b76e85b4c1c0c967fc5bc63756a47
new file mode 100644
index 0000000..26d718e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/6d52e07de58b76e85b4c1c0c967fc5bc63756a47
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/6de103d002884c69f13da535e66aecff12c5710c b/fuzz/corpus/packet_recv_client/6de103d002884c69f13da535e66aecff12c5710c
new file mode 100644
index 0000000..bd29cbd
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/6de103d002884c69f13da535e66aecff12c5710c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/6eb4169c77acd2bd4610beaa1479c0d60845911e b/fuzz/corpus/packet_recv_client/6eb4169c77acd2bd4610beaa1479c0d60845911e
new file mode 100644
index 0000000..b8e8852
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/6eb4169c77acd2bd4610beaa1479c0d60845911e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/6edbdb54d4071b0c324c544102f6a79526c59609 b/fuzz/corpus/packet_recv_client/6edbdb54d4071b0c324c544102f6a79526c59609
new file mode 100644
index 0000000..52d5559
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/6edbdb54d4071b0c324c544102f6a79526c59609
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/6f471b37c49ab09147d361887d123e4075e32b44 b/fuzz/corpus/packet_recv_client/6f471b37c49ab09147d361887d123e4075e32b44
new file mode 100644
index 0000000..b4b4271
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/6f471b37c49ab09147d361887d123e4075e32b44
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/6fa8d3e558cd3378300f5690fb6edf2e240542bc b/fuzz/corpus/packet_recv_client/6fa8d3e558cd3378300f5690fb6edf2e240542bc
new file mode 100644
index 0000000..df7b254
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/6fa8d3e558cd3378300f5690fb6edf2e240542bc
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/70b4a2089da47565b0f1a8c7ebb0b15c6048f477 b/fuzz/corpus/packet_recv_client/70b4a2089da47565b0f1a8c7ebb0b15c6048f477
new file mode 100644
index 0000000..644dc72
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/70b4a2089da47565b0f1a8c7ebb0b15c6048f477
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/70c101eaf1c79a224ca1b6322db49f171fcc3f31 b/fuzz/corpus/packet_recv_client/70c101eaf1c79a224ca1b6322db49f171fcc3f31
new file mode 100644
index 0000000..27bede1
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/70c101eaf1c79a224ca1b6322db49f171fcc3f31
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/71309ba1e4c27e178ecb756c6578a200b4063539 b/fuzz/corpus/packet_recv_client/71309ba1e4c27e178ecb756c6578a200b4063539
new file mode 100644
index 0000000..c125cce
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/71309ba1e4c27e178ecb756c6578a200b4063539
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/72e564aa8fb770cb77f8a4e57d12ce06ad6e38a6 b/fuzz/corpus/packet_recv_client/72e564aa8fb770cb77f8a4e57d12ce06ad6e38a6
new file mode 100644
index 0000000..26afcb4
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/72e564aa8fb770cb77f8a4e57d12ce06ad6e38a6
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/73c70083420c55d1f697b6cacf4e843f6b961038 b/fuzz/corpus/packet_recv_client/73c70083420c55d1f697b6cacf4e843f6b961038
new file mode 100644
index 0000000..5ca1ecc
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/73c70083420c55d1f697b6cacf4e843f6b961038
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/744560bef0aeaedaa3805eaf85a229cb3860c5bb b/fuzz/corpus/packet_recv_client/744560bef0aeaedaa3805eaf85a229cb3860c5bb
new file mode 100644
index 0000000..29c99aa
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/744560bef0aeaedaa3805eaf85a229cb3860c5bb
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/75b1f9970b1d4c92d1b485230e3c2d7e1d4fd282 b/fuzz/corpus/packet_recv_client/75b1f9970b1d4c92d1b485230e3c2d7e1d4fd282
new file mode 100644
index 0000000..9aad42c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/75b1f9970b1d4c92d1b485230e3c2d7e1d4fd282
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/75f6853aa02e618cd0e938cd3a66e4883fd716c4 b/fuzz/corpus/packet_recv_client/75f6853aa02e618cd0e938cd3a66e4883fd716c4
new file mode 100644
index 0000000..41ea291
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/75f6853aa02e618cd0e938cd3a66e4883fd716c4
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/76011c0c29f953f14a5d8e35e7d8300e84d37dee b/fuzz/corpus/packet_recv_client/76011c0c29f953f14a5d8e35e7d8300e84d37dee
new file mode 100644
index 0000000..7d3ca67
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/76011c0c29f953f14a5d8e35e7d8300e84d37dee
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/7635af3e20ce66da4852c098c48def6d8f021c3f b/fuzz/corpus/packet_recv_client/7635af3e20ce66da4852c098c48def6d8f021c3f
new file mode 100644
index 0000000..7162c39
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/7635af3e20ce66da4852c098c48def6d8f021c3f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/768f42ad73f447fe390227ca459210e9ce75a015 b/fuzz/corpus/packet_recv_client/768f42ad73f447fe390227ca459210e9ce75a015
new file mode 100644
index 0000000..26dee3a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/768f42ad73f447fe390227ca459210e9ce75a015
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/768f44fc30d3ee49514eae9241bbe84f0325cc14 b/fuzz/corpus/packet_recv_client/768f44fc30d3ee49514eae9241bbe84f0325cc14
new file mode 100644
index 0000000..d4dbdbc
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/768f44fc30d3ee49514eae9241bbe84f0325cc14
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/779b7a46edf2dedc257901480a6f85c08f08c2ba b/fuzz/corpus/packet_recv_client/779b7a46edf2dedc257901480a6f85c08f08c2ba
new file mode 100644
index 0000000..7d5e890
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/779b7a46edf2dedc257901480a6f85c08f08c2ba
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/77bdc119e5c1551540b5a327e725e6ed770a7aec b/fuzz/corpus/packet_recv_client/77bdc119e5c1551540b5a327e725e6ed770a7aec
new file mode 100644
index 0000000..36ceef3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/77bdc119e5c1551540b5a327e725e6ed770a7aec
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/77ee0863d0a53aeeb70539e97e5cfc39671ec1a1 b/fuzz/corpus/packet_recv_client/77ee0863d0a53aeeb70539e97e5cfc39671ec1a1
new file mode 100644
index 0000000..b944a7a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/77ee0863d0a53aeeb70539e97e5cfc39671ec1a1
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/7827d58139cceb646746098292954e93e11c9399 b/fuzz/corpus/packet_recv_client/7827d58139cceb646746098292954e93e11c9399
new file mode 100644
index 0000000..fc61d50
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/7827d58139cceb646746098292954e93e11c9399
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/78a3d2d32fbc937dfa37843cd292d4339afca9f0 b/fuzz/corpus/packet_recv_client/78a3d2d32fbc937dfa37843cd292d4339afca9f0
new file mode 100644
index 0000000..8cd40cb
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/78a3d2d32fbc937dfa37843cd292d4339afca9f0
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/79fc40b8ab13cc9414a88abc42c831bdc86c2318 b/fuzz/corpus/packet_recv_client/79fc40b8ab13cc9414a88abc42c831bdc86c2318
new file mode 100644
index 0000000..3217db3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/79fc40b8ab13cc9414a88abc42c831bdc86c2318
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/7afeda01b346097fd2a016f64d17698f79b3ea2f b/fuzz/corpus/packet_recv_client/7afeda01b346097fd2a016f64d17698f79b3ea2f
new file mode 100644
index 0000000..6be6450
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/7afeda01b346097fd2a016f64d17698f79b3ea2f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/7bd694f183029d6b857ec2cfa8bff2db5178ce1a b/fuzz/corpus/packet_recv_client/7bd694f183029d6b857ec2cfa8bff2db5178ce1a
new file mode 100644
index 0000000..69606b0
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/7bd694f183029d6b857ec2cfa8bff2db5178ce1a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/7cc85237e8e18642d82269ac95ec570df0168d60 b/fuzz/corpus/packet_recv_client/7cc85237e8e18642d82269ac95ec570df0168d60
new file mode 100644
index 0000000..da199b3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/7cc85237e8e18642d82269ac95ec570df0168d60
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/7ce83b5207edf57bee6f363324e1a50231a7a1af b/fuzz/corpus/packet_recv_client/7ce83b5207edf57bee6f363324e1a50231a7a1af
deleted file mode 100644
index ca3dc19..0000000
--- a/fuzz/corpus/packet_recv_client/7ce83b5207edf57bee6f363324e1a50231a7a1af
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/7d14faa35332d1b9a930c1164874b7d8b66ae2d0 b/fuzz/corpus/packet_recv_client/7d14faa35332d1b9a930c1164874b7d8b66ae2d0
new file mode 100644
index 0000000..a524f52
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/7d14faa35332d1b9a930c1164874b7d8b66ae2d0
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/7d5f66095a97da08c6c20db3af865dabe40c23a1 b/fuzz/corpus/packet_recv_client/7d5f66095a97da08c6c20db3af865dabe40c23a1
new file mode 100644
index 0000000..15e8a5b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/7d5f66095a97da08c6c20db3af865dabe40c23a1
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/7d8ffcd284242079e16106284fa7529d7ff018af b/fuzz/corpus/packet_recv_client/7d8ffcd284242079e16106284fa7529d7ff018af
new file mode 100644
index 0000000..1d7c80b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/7d8ffcd284242079e16106284fa7529d7ff018af
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/7dc975fa16a79758018cd77aa97ec4649ee171a3 b/fuzz/corpus/packet_recv_client/7dc975fa16a79758018cd77aa97ec4649ee171a3
new file mode 100644
index 0000000..8cd2848
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/7dc975fa16a79758018cd77aa97ec4649ee171a3
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/7e268a7f7de63816ce8e17e779f00d2ec229becb b/fuzz/corpus/packet_recv_client/7e268a7f7de63816ce8e17e779f00d2ec229becb
new file mode 100644
index 0000000..2a35151
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/7e268a7f7de63816ce8e17e779f00d2ec229becb
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/7e7e674cb7cba3090f830fe8568eb41f4607966f b/fuzz/corpus/packet_recv_client/7e7e674cb7cba3090f830fe8568eb41f4607966f
new file mode 100644
index 0000000..086da94
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/7e7e674cb7cba3090f830fe8568eb41f4607966f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/7fcf22b97cd6646959daca92f25d3713c05bd2c2 b/fuzz/corpus/packet_recv_client/7fcf22b97cd6646959daca92f25d3713c05bd2c2
new file mode 100644
index 0000000..168ccb9
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/7fcf22b97cd6646959daca92f25d3713c05bd2c2
@@ -0,0 +1 @@
+ˆ____
\ No newline at end of file
diff --git a/fuzz/corpus/packet_recv_client/804aa28c5a1673eb2ae9587e4a6cf9b2b025ff2d b/fuzz/corpus/packet_recv_client/804aa28c5a1673eb2ae9587e4a6cf9b2b025ff2d
new file mode 100644
index 0000000..c34d5ba
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/804aa28c5a1673eb2ae9587e4a6cf9b2b025ff2d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/808ae4633b369c340a0bcbd66ac9b62e973a1338 b/fuzz/corpus/packet_recv_client/808ae4633b369c340a0bcbd66ac9b62e973a1338
new file mode 100644
index 0000000..657c086
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/808ae4633b369c340a0bcbd66ac9b62e973a1338
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/80bd782fa72e8a3df41e0f8f1a2b9a5a971386a6 b/fuzz/corpus/packet_recv_client/80bd782fa72e8a3df41e0f8f1a2b9a5a971386a6
deleted file mode 100644
index 15b3ee3..0000000
--- a/fuzz/corpus/packet_recv_client/80bd782fa72e8a3df41e0f8f1a2b9a5a971386a6
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/80f7a9f08f61821cf89d40b5b338c543584ed492 b/fuzz/corpus/packet_recv_client/80f7a9f08f61821cf89d40b5b338c543584ed492
new file mode 100644
index 0000000..ccbbbd4
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/80f7a9f08f61821cf89d40b5b338c543584ed492
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/810159278b855fa97672518a857ddef62cbbc1af b/fuzz/corpus/packet_recv_client/810159278b855fa97672518a857ddef62cbbc1af
new file mode 100644
index 0000000..44fa7c1
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/810159278b855fa97672518a857ddef62cbbc1af
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/81d2a84e5b6f3f28cda4574bc11de0a076308d36 b/fuzz/corpus/packet_recv_client/81d2a84e5b6f3f28cda4574bc11de0a076308d36
new file mode 100644
index 0000000..9387c0d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/81d2a84e5b6f3f28cda4574bc11de0a076308d36
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/81dac68bcf1e24d1665da938b3c6cba405eb5849 b/fuzz/corpus/packet_recv_client/81dac68bcf1e24d1665da938b3c6cba405eb5849
new file mode 100644
index 0000000..7de57d1
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/81dac68bcf1e24d1665da938b3c6cba405eb5849
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/8220668e727165c01e984415f8221183f2f1e2a6 b/fuzz/corpus/packet_recv_client/8220668e727165c01e984415f8221183f2f1e2a6
new file mode 100644
index 0000000..08c7f94
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/8220668e727165c01e984415f8221183f2f1e2a6
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/823edd09dcb35d8feb282888dd9421ddb7fd52c8 b/fuzz/corpus/packet_recv_client/823edd09dcb35d8feb282888dd9421ddb7fd52c8
new file mode 100644
index 0000000..8663d41
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/823edd09dcb35d8feb282888dd9421ddb7fd52c8
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/824663443cb1df3afd77ad2f34f5077cfc12cf2b b/fuzz/corpus/packet_recv_client/824663443cb1df3afd77ad2f34f5077cfc12cf2b
new file mode 100644
index 0000000..6377552
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/824663443cb1df3afd77ad2f34f5077cfc12cf2b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/83141715a294e2e9ee015e25fe5d777de8fc2133 b/fuzz/corpus/packet_recv_client/83141715a294e2e9ee015e25fe5d777de8fc2133
new file mode 100644
index 0000000..5005387
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/83141715a294e2e9ee015e25fe5d777de8fc2133
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/831d5de9bb6d49216c5d2d235f0921ce1ba7e367 b/fuzz/corpus/packet_recv_client/831d5de9bb6d49216c5d2d235f0921ce1ba7e367
new file mode 100644
index 0000000..f2eedfb
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/831d5de9bb6d49216c5d2d235f0921ce1ba7e367
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/832e9a95acf57286b838e48cb49a548633764c26 b/fuzz/corpus/packet_recv_client/832e9a95acf57286b838e48cb49a548633764c26
deleted file mode 100644
index 926a3cb..0000000
--- a/fuzz/corpus/packet_recv_client/832e9a95acf57286b838e48cb49a548633764c26
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/832f7ab909d6fbcf1ea838478763a55f072924da b/fuzz/corpus/packet_recv_client/832f7ab909d6fbcf1ea838478763a55f072924da
deleted file mode 100644
index 8cdf53e..0000000
--- a/fuzz/corpus/packet_recv_client/832f7ab909d6fbcf1ea838478763a55f072924da
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/8385ba2a3beb97200981e5d0fc8d294ab87dadcc b/fuzz/corpus/packet_recv_client/8385ba2a3beb97200981e5d0fc8d294ab87dadcc
new file mode 100644
index 0000000..94f9e39
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/8385ba2a3beb97200981e5d0fc8d294ab87dadcc
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/83f3d8f8ceb5732d961c42894a61e4c6fbb4745c b/fuzz/corpus/packet_recv_client/83f3d8f8ceb5732d961c42894a61e4c6fbb4745c
new file mode 100644
index 0000000..60827a1
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/83f3d8f8ceb5732d961c42894a61e4c6fbb4745c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/844d2678f2ac0b868b5a26ea087fbb22dc6b16d7 b/fuzz/corpus/packet_recv_client/844d2678f2ac0b868b5a26ea087fbb22dc6b16d7
new file mode 100644
index 0000000..01d8f47
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/844d2678f2ac0b868b5a26ea087fbb22dc6b16d7
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/847bfbbe4c517635ad1446fa7e42a605940cafaa b/fuzz/corpus/packet_recv_client/847bfbbe4c517635ad1446fa7e42a605940cafaa
new file mode 100644
index 0000000..3ef8b84
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/847bfbbe4c517635ad1446fa7e42a605940cafaa
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/858f91efc3b4e4193bace1fc055aacfaf9d2e341 b/fuzz/corpus/packet_recv_client/858f91efc3b4e4193bace1fc055aacfaf9d2e341
new file mode 100644
index 0000000..c4677ce
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/858f91efc3b4e4193bace1fc055aacfaf9d2e341
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/86096ff2bc6cff213c691d62e2dc99c38028120a b/fuzz/corpus/packet_recv_client/86096ff2bc6cff213c691d62e2dc99c38028120a
deleted file mode 100644
index b263a01..0000000
--- a/fuzz/corpus/packet_recv_client/86096ff2bc6cff213c691d62e2dc99c38028120a
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/867ac8cfda943798ab8790cf08e40b1b686c330f b/fuzz/corpus/packet_recv_client/867ac8cfda943798ab8790cf08e40b1b686c330f
deleted file mode 100644
index 28a09ca..0000000
--- a/fuzz/corpus/packet_recv_client/867ac8cfda943798ab8790cf08e40b1b686c330f
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/86875bb9309fe67a067775f61b78bf1d2c298997 b/fuzz/corpus/packet_recv_client/86875bb9309fe67a067775f61b78bf1d2c298997
new file mode 100644
index 0000000..06d7030
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/86875bb9309fe67a067775f61b78bf1d2c298997
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/86c55b6d1ea0a987ef620dd98950801570c9b886 b/fuzz/corpus/packet_recv_client/86c55b6d1ea0a987ef620dd98950801570c9b886
new file mode 100644
index 0000000..b1185ad
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/86c55b6d1ea0a987ef620dd98950801570c9b886
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/86c6014a60f8a326eb1600a64967023e4177b032 b/fuzz/corpus/packet_recv_client/86c6014a60f8a326eb1600a64967023e4177b032
new file mode 100644
index 0000000..5610d4e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/86c6014a60f8a326eb1600a64967023e4177b032
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/86e6c137f6d4c993120489b2e4112a91fd769858 b/fuzz/corpus/packet_recv_client/86e6c137f6d4c993120489b2e4112a91fd769858
new file mode 100644
index 0000000..8ed112d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/86e6c137f6d4c993120489b2e4112a91fd769858
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/87050dfdc015dbdd1fd3fcbe961f74fb75c03d92 b/fuzz/corpus/packet_recv_client/87050dfdc015dbdd1fd3fcbe961f74fb75c03d92
new file mode 100644
index 0000000..951b841
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/87050dfdc015dbdd1fd3fcbe961f74fb75c03d92
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/87c8228161396dfe6c6b635c48b66928c74aa87a b/fuzz/corpus/packet_recv_client/87c8228161396dfe6c6b635c48b66928c74aa87a
new file mode 100644
index 0000000..91e62cd
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/87c8228161396dfe6c6b635c48b66928c74aa87a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/87f667a252d95f1f9c75ed74b063e122f3838a0d b/fuzz/corpus/packet_recv_client/87f667a252d95f1f9c75ed74b063e122f3838a0d
new file mode 100644
index 0000000..18100c6
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/87f667a252d95f1f9c75ed74b063e122f3838a0d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/88cad5c239822c8fe8a6bf180f0ddcb239690557 b/fuzz/corpus/packet_recv_client/88cad5c239822c8fe8a6bf180f0ddcb239690557
new file mode 100644
index 0000000..4f1b70a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/88cad5c239822c8fe8a6bf180f0ddcb239690557
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/892534207f53883f53165bc45e1447560133c47f b/fuzz/corpus/packet_recv_client/892534207f53883f53165bc45e1447560133c47f
new file mode 100644
index 0000000..36ef563
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/892534207f53883f53165bc45e1447560133c47f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/892f961737567ba08b765a9a82d47d4a0f713f7c b/fuzz/corpus/packet_recv_client/892f961737567ba08b765a9a82d47d4a0f713f7c
new file mode 100644
index 0000000..21ce2e2
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/892f961737567ba08b765a9a82d47d4a0f713f7c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/893872de223cc20579cd91fd4714090773e0b511 b/fuzz/corpus/packet_recv_client/893872de223cc20579cd91fd4714090773e0b511
new file mode 100644
index 0000000..56841b0
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/893872de223cc20579cd91fd4714090773e0b511
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/897e27dba16190923baa13abe21773ec2e043964 b/fuzz/corpus/packet_recv_client/897e27dba16190923baa13abe21773ec2e043964
new file mode 100644
index 0000000..f210c10
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/897e27dba16190923baa13abe21773ec2e043964
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/89a7a4f8324e411e4b4835c6663026a5f35c159c b/fuzz/corpus/packet_recv_client/89a7a4f8324e411e4b4835c6663026a5f35c159c
deleted file mode 100644
index 920fc4e..0000000
--- a/fuzz/corpus/packet_recv_client/89a7a4f8324e411e4b4835c6663026a5f35c159c
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/89ded7d678ff1847b7b4dc2c27a9066efadd0a65 b/fuzz/corpus/packet_recv_client/89ded7d678ff1847b7b4dc2c27a9066efadd0a65
new file mode 100644
index 0000000..7be4b57
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/89ded7d678ff1847b7b4dc2c27a9066efadd0a65
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/8a16e32f4ae939a9c7dffd2a6fa2b236f7443669 b/fuzz/corpus/packet_recv_client/8a16e32f4ae939a9c7dffd2a6fa2b236f7443669
new file mode 100644
index 0000000..0686bc4
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/8a16e32f4ae939a9c7dffd2a6fa2b236f7443669
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/8a4136b05ecf628dc60565a002ec9a53eaa78c53 b/fuzz/corpus/packet_recv_client/8a4136b05ecf628dc60565a002ec9a53eaa78c53
new file mode 100644
index 0000000..da530f3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/8a4136b05ecf628dc60565a002ec9a53eaa78c53
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/8a79eb3cacb005b0b019ed8b439b16b1124a9084 b/fuzz/corpus/packet_recv_client/8a79eb3cacb005b0b019ed8b439b16b1124a9084
new file mode 100644
index 0000000..e7de2ff
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/8a79eb3cacb005b0b019ed8b439b16b1124a9084
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/8c06284917d22b9eb57abec2d02b6030c63de3cb b/fuzz/corpus/packet_recv_client/8c06284917d22b9eb57abec2d02b6030c63de3cb
new file mode 100644
index 0000000..64f5517
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/8c06284917d22b9eb57abec2d02b6030c63de3cb
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/8c50bf6e2191f8bf4906fe3dc42f7253dc549a57 b/fuzz/corpus/packet_recv_client/8c50bf6e2191f8bf4906fe3dc42f7253dc549a57
new file mode 100644
index 0000000..63e0c53
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/8c50bf6e2191f8bf4906fe3dc42f7253dc549a57
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/8c5834ee7513c47a6fd5fc86e924c27b27a4ac45 b/fuzz/corpus/packet_recv_client/8c5834ee7513c47a6fd5fc86e924c27b27a4ac45
new file mode 100644
index 0000000..08743cb
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/8c5834ee7513c47a6fd5fc86e924c27b27a4ac45
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/8cf701a2261dd01d0d0de3a696f50e1c70b55989 b/fuzz/corpus/packet_recv_client/8cf701a2261dd01d0d0de3a696f50e1c70b55989
new file mode 100644
index 0000000..4d84d42
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/8cf701a2261dd01d0d0de3a696f50e1c70b55989
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/8d9c6216df4d181dc16140b9eb48ef7cd16136e9 b/fuzz/corpus/packet_recv_client/8d9c6216df4d181dc16140b9eb48ef7cd16136e9
new file mode 100644
index 0000000..a6b595e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/8d9c6216df4d181dc16140b9eb48ef7cd16136e9
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/8e489eb120aa6556115f9735423dd8a2bb07ae77 b/fuzz/corpus/packet_recv_client/8e489eb120aa6556115f9735423dd8a2bb07ae77
new file mode 100644
index 0000000..36545eb
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/8e489eb120aa6556115f9735423dd8a2bb07ae77
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/8e841abd9f12542e532e1a751349f9962a2beefc b/fuzz/corpus/packet_recv_client/8e841abd9f12542e532e1a751349f9962a2beefc
new file mode 100644
index 0000000..25d02ca
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/8e841abd9f12542e532e1a751349f9962a2beefc
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/8e9a078b54c779e36c633ee0e55edc60c3e42dbd b/fuzz/corpus/packet_recv_client/8e9a078b54c779e36c633ee0e55edc60c3e42dbd
new file mode 100644
index 0000000..290d7b9
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/8e9a078b54c779e36c633ee0e55edc60c3e42dbd
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/8f0951d524d77b70a0e1632eab3263ca023cf6a0 b/fuzz/corpus/packet_recv_client/8f0951d524d77b70a0e1632eab3263ca023cf6a0
new file mode 100644
index 0000000..3b5d490
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/8f0951d524d77b70a0e1632eab3263ca023cf6a0
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/8f14025277c0541f28ab0d6998b7d4dcb40c1335 b/fuzz/corpus/packet_recv_client/8f14025277c0541f28ab0d6998b7d4dcb40c1335
new file mode 100644
index 0000000..3140c4b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/8f14025277c0541f28ab0d6998b7d4dcb40c1335
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/8f5a00be8a927fa040ae2811cd30fcb80e82b62b b/fuzz/corpus/packet_recv_client/8f5a00be8a927fa040ae2811cd30fcb80e82b62b
deleted file mode 100644
index c8d2852..0000000
--- a/fuzz/corpus/packet_recv_client/8f5a00be8a927fa040ae2811cd30fcb80e82b62b
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/8f66465f525bcfafdf14f93bd27ea2542d292ecd b/fuzz/corpus/packet_recv_client/8f66465f525bcfafdf14f93bd27ea2542d292ecd
new file mode 100644
index 0000000..8b78ddd
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/8f66465f525bcfafdf14f93bd27ea2542d292ecd
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9105bbf68b549c00a0c72f9fef481ad785c92004 b/fuzz/corpus/packet_recv_client/9105bbf68b549c00a0c72f9fef481ad785c92004
deleted file mode 100644
index 20fd41c..0000000
--- a/fuzz/corpus/packet_recv_client/9105bbf68b549c00a0c72f9fef481ad785c92004
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/916d2e4b6f0e17f318ea3779af150ab54501d9f4 b/fuzz/corpus/packet_recv_client/916d2e4b6f0e17f318ea3779af150ab54501d9f4
new file mode 100644
index 0000000..13b1c8a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/916d2e4b6f0e17f318ea3779af150ab54501d9f4
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/922b0e40cf8045f28ddfd088ab657dec9f1fe0e6 b/fuzz/corpus/packet_recv_client/922b0e40cf8045f28ddfd088ab657dec9f1fe0e6
new file mode 100644
index 0000000..1ce90b8
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/922b0e40cf8045f28ddfd088ab657dec9f1fe0e6
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9265a4bdbd6da06ab028238eb82bfe1713b78cb0 b/fuzz/corpus/packet_recv_client/9265a4bdbd6da06ab028238eb82bfe1713b78cb0
new file mode 100644
index 0000000..43fa376
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9265a4bdbd6da06ab028238eb82bfe1713b78cb0
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9273416fc66c96ca1caf4ec58bfbd69a5b823149 b/fuzz/corpus/packet_recv_client/9273416fc66c96ca1caf4ec58bfbd69a5b823149
new file mode 100644
index 0000000..bdbae9b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9273416fc66c96ca1caf4ec58bfbd69a5b823149
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/929fc5673b96ad76696f56465b25e01ae6594114 b/fuzz/corpus/packet_recv_client/929fc5673b96ad76696f56465b25e01ae6594114
deleted file mode 100644
index 36c603e..0000000
--- a/fuzz/corpus/packet_recv_client/929fc5673b96ad76696f56465b25e01ae6594114
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/92f532815241f6243151fbe732a2ef986a4e6152 b/fuzz/corpus/packet_recv_client/92f532815241f6243151fbe732a2ef986a4e6152
new file mode 100644
index 0000000..1f66ee5
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/92f532815241f6243151fbe732a2ef986a4e6152
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/938e56e87301692af475c9a16319f66a35b4d42d b/fuzz/corpus/packet_recv_client/938e56e87301692af475c9a16319f66a35b4d42d
new file mode 100644
index 0000000..d4e5138
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/938e56e87301692af475c9a16319f66a35b4d42d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/93c4cb3f2193b404a2cd713434a5c4cc563151ba b/fuzz/corpus/packet_recv_client/93c4cb3f2193b404a2cd713434a5c4cc563151ba
new file mode 100644
index 0000000..9b66c7e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/93c4cb3f2193b404a2cd713434a5c4cc563151ba
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/94108451f94018cbaed1f4913759c9af14c6969f b/fuzz/corpus/packet_recv_client/94108451f94018cbaed1f4913759c9af14c6969f
new file mode 100644
index 0000000..f39519a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/94108451f94018cbaed1f4913759c9af14c6969f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9471130d176100fd98768678a0dbd8340a3f9f6c b/fuzz/corpus/packet_recv_client/9471130d176100fd98768678a0dbd8340a3f9f6c
new file mode 100644
index 0000000..6cda0a0
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9471130d176100fd98768678a0dbd8340a3f9f6c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/964e76782f36bd0776e2f7705fd9e60995628533 b/fuzz/corpus/packet_recv_client/964e76782f36bd0776e2f7705fd9e60995628533
new file mode 100644
index 0000000..83f8a7d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/964e76782f36bd0776e2f7705fd9e60995628533
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/967a149b40829ca2c5177d1c783671b97419fbd7 b/fuzz/corpus/packet_recv_client/967a149b40829ca2c5177d1c783671b97419fbd7
new file mode 100644
index 0000000..09b74a0
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/967a149b40829ca2c5177d1c783671b97419fbd7
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/976b970c0ff4acb71b2351744bbf516835c48183 b/fuzz/corpus/packet_recv_client/976b970c0ff4acb71b2351744bbf516835c48183
new file mode 100644
index 0000000..8f0fa94
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/976b970c0ff4acb71b2351744bbf516835c48183
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/97e2d2fd66924461656edb2088c68ca9a59cc971 b/fuzz/corpus/packet_recv_client/97e2d2fd66924461656edb2088c68ca9a59cc971
new file mode 100644
index 0000000..4bd5761
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/97e2d2fd66924461656edb2088c68ca9a59cc971
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/980d14e8ec8e0c17e6f19379e9f161dfe8b5c61c b/fuzz/corpus/packet_recv_client/980d14e8ec8e0c17e6f19379e9f161dfe8b5c61c
new file mode 100644
index 0000000..6b8f5d0
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/980d14e8ec8e0c17e6f19379e9f161dfe8b5c61c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/987ef52df37328c0ecd46944f46e0b69108a0192 b/fuzz/corpus/packet_recv_client/987ef52df37328c0ecd46944f46e0b69108a0192
new file mode 100644
index 0000000..1237f46
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/987ef52df37328c0ecd46944f46e0b69108a0192
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9991c5adae53b07d22f18eb7697442003c330ddf b/fuzz/corpus/packet_recv_client/9991c5adae53b07d22f18eb7697442003c330ddf
deleted file mode 100644
index 976284f..0000000
--- a/fuzz/corpus/packet_recv_client/9991c5adae53b07d22f18eb7697442003c330ddf
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/99989fdceb32c4464a9f192e6b71dcd1ca645663 b/fuzz/corpus/packet_recv_client/99989fdceb32c4464a9f192e6b71dcd1ca645663
new file mode 100644
index 0000000..feae363
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/99989fdceb32c4464a9f192e6b71dcd1ca645663
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/99e5351ed571ad594f1c5bf75f0a8e292836c319 b/fuzz/corpus/packet_recv_client/99e5351ed571ad594f1c5bf75f0a8e292836c319
new file mode 100644
index 0000000..8409eeb
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/99e5351ed571ad594f1c5bf75f0a8e292836c319
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/99e918c0ab7220e19a9dc7fd728ea83676b25577 b/fuzz/corpus/packet_recv_client/99e918c0ab7220e19a9dc7fd728ea83676b25577
new file mode 100644
index 0000000..c0eb6a6
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/99e918c0ab7220e19a9dc7fd728ea83676b25577
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9a01b915f4f6b4823d19b51320872b6a52564926 b/fuzz/corpus/packet_recv_client/9a01b915f4f6b4823d19b51320872b6a52564926
deleted file mode 100644
index 1e6d95e..0000000
--- a/fuzz/corpus/packet_recv_client/9a01b915f4f6b4823d19b51320872b6a52564926
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9a247d326c20f51a3388a17af2c6fce67d767b37 b/fuzz/corpus/packet_recv_client/9a247d326c20f51a3388a17af2c6fce67d767b37
new file mode 100644
index 0000000..fe7fdf4
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9a247d326c20f51a3388a17af2c6fce67d767b37
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9a56fdbd2549a91358f654b4452dffca5977b495 b/fuzz/corpus/packet_recv_client/9a56fdbd2549a91358f654b4452dffca5977b495
new file mode 100644
index 0000000..0e4526c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9a56fdbd2549a91358f654b4452dffca5977b495
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9a701ef182dff463149cd301a95f1334e28638bf b/fuzz/corpus/packet_recv_client/9a701ef182dff463149cd301a95f1334e28638bf
new file mode 100644
index 0000000..629d12c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9a701ef182dff463149cd301a95f1334e28638bf
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9ac9d569cce7d1201412854824662e18ed7c94f2 b/fuzz/corpus/packet_recv_client/9ac9d569cce7d1201412854824662e18ed7c94f2
new file mode 100644
index 0000000..42080c1
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9ac9d569cce7d1201412854824662e18ed7c94f2
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9adc3bc815d9ce46bcc53ad04e1d8ca282f21f8e b/fuzz/corpus/packet_recv_client/9adc3bc815d9ce46bcc53ad04e1d8ca282f21f8e
new file mode 100644
index 0000000..fc0948b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9adc3bc815d9ce46bcc53ad04e1d8ca282f21f8e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9adcda3acff57bcde9b6745b4f56741eee6a9782 b/fuzz/corpus/packet_recv_client/9adcda3acff57bcde9b6745b4f56741eee6a9782
new file mode 100644
index 0000000..e501cdd
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9adcda3acff57bcde9b6745b4f56741eee6a9782
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9b22f52f388b7f1277891ed225f8344c75d266db b/fuzz/corpus/packet_recv_client/9b22f52f388b7f1277891ed225f8344c75d266db
deleted file mode 100644
index b19983a..0000000
--- a/fuzz/corpus/packet_recv_client/9b22f52f388b7f1277891ed225f8344c75d266db
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9b3aa4e3d15ad40a756211c7d85a9bf8438f2c3c b/fuzz/corpus/packet_recv_client/9b3aa4e3d15ad40a756211c7d85a9bf8438f2c3c
new file mode 100644
index 0000000..488632e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9b3aa4e3d15ad40a756211c7d85a9bf8438f2c3c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9b488a21e19a6e17b338b055e47808ff8b2a546f b/fuzz/corpus/packet_recv_client/9b488a21e19a6e17b338b055e47808ff8b2a546f
new file mode 100644
index 0000000..5c09df7
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9b488a21e19a6e17b338b055e47808ff8b2a546f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9b5353b0bd8b03ff214692702081f48ec29eecc2 b/fuzz/corpus/packet_recv_client/9b5353b0bd8b03ff214692702081f48ec29eecc2
new file mode 100644
index 0000000..3bf6e4f
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9b5353b0bd8b03ff214692702081f48ec29eecc2
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9b99ec5a86500c7b218fe845de2c1cadf6ac097e b/fuzz/corpus/packet_recv_client/9b99ec5a86500c7b218fe845de2c1cadf6ac097e
new file mode 100644
index 0000000..45e5d04
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9b99ec5a86500c7b218fe845de2c1cadf6ac097e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9bdcf7f170a11a9db108ce1d27a5d32551b22408 b/fuzz/corpus/packet_recv_client/9bdcf7f170a11a9db108ce1d27a5d32551b22408
new file mode 100644
index 0000000..ce45c41
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9bdcf7f170a11a9db108ce1d27a5d32551b22408
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9c09a7d676ae6bcc7302079a103747d5d329a9e6 b/fuzz/corpus/packet_recv_client/9c09a7d676ae6bcc7302079a103747d5d329a9e6
new file mode 100644
index 0000000..71a8a61
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9c09a7d676ae6bcc7302079a103747d5d329a9e6
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9c68dee4925305f092c3a620310c1decd9489a48 b/fuzz/corpus/packet_recv_client/9c68dee4925305f092c3a620310c1decd9489a48
deleted file mode 100644
index 8bfd4a7..0000000
--- a/fuzz/corpus/packet_recv_client/9c68dee4925305f092c3a620310c1decd9489a48
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9c93dfc4a3caa54661acd00fb83e40fecf61456d b/fuzz/corpus/packet_recv_client/9c93dfc4a3caa54661acd00fb83e40fecf61456d
new file mode 100644
index 0000000..0117407
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9c93dfc4a3caa54661acd00fb83e40fecf61456d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9d3eb4737b7099861d89e75b3f8e5e86c3f63b3f b/fuzz/corpus/packet_recv_client/9d3eb4737b7099861d89e75b3f8e5e86c3f63b3f
new file mode 100644
index 0000000..3bf3c86
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9d3eb4737b7099861d89e75b3f8e5e86c3f63b3f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9d7133899b309e9ac250f9c76bad950fa45fb0a6 b/fuzz/corpus/packet_recv_client/9d7133899b309e9ac250f9c76bad950fa45fb0a6
new file mode 100644
index 0000000..4402b57
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9d7133899b309e9ac250f9c76bad950fa45fb0a6
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9e46dc5d65c47e78e1041e5884828cac543dc278 b/fuzz/corpus/packet_recv_client/9e46dc5d65c47e78e1041e5884828cac543dc278
new file mode 100644
index 0000000..b26492b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9e46dc5d65c47e78e1041e5884828cac543dc278
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9e5eccfa80f0be4d930609f92c8fb2e03bf6e345 b/fuzz/corpus/packet_recv_client/9e5eccfa80f0be4d930609f92c8fb2e03bf6e345
new file mode 100644
index 0000000..5a62855
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9e5eccfa80f0be4d930609f92c8fb2e03bf6e345
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9eaabe7e558158437dcd1f86b316a9e0a4197575 b/fuzz/corpus/packet_recv_client/9eaabe7e558158437dcd1f86b316a9e0a4197575
new file mode 100644
index 0000000..86594d4
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9eaabe7e558158437dcd1f86b316a9e0a4197575
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9f75f8ec9c52c90676ae1cded76a6b4722be5809 b/fuzz/corpus/packet_recv_client/9f75f8ec9c52c90676ae1cded76a6b4722be5809
new file mode 100644
index 0000000..6c5b980
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9f75f8ec9c52c90676ae1cded76a6b4722be5809
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9fb51a74fc35efbd6cbad970496f6329c9073c0d b/fuzz/corpus/packet_recv_client/9fb51a74fc35efbd6cbad970496f6329c9073c0d
new file mode 100644
index 0000000..7963d35
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9fb51a74fc35efbd6cbad970496f6329c9073c0d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/9fcadad244b5dc830f0849a4683c24695a643309 b/fuzz/corpus/packet_recv_client/9fcadad244b5dc830f0849a4683c24695a643309
new file mode 100644
index 0000000..64ff081
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/9fcadad244b5dc830f0849a4683c24695a643309
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/a0534291f8cf6cf39de4b2552a30994b4148e551 b/fuzz/corpus/packet_recv_client/a0534291f8cf6cf39de4b2552a30994b4148e551
new file mode 100644
index 0000000..75654a3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/a0534291f8cf6cf39de4b2552a30994b4148e551
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/a077587807b2bb8b72e42fb4a30639695e5990d7 b/fuzz/corpus/packet_recv_client/a077587807b2bb8b72e42fb4a30639695e5990d7
new file mode 100644
index 0000000..d2ba0f5
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/a077587807b2bb8b72e42fb4a30639695e5990d7
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/a12fb9c088abf60e98fdc28d049cd0077a443f41 b/fuzz/corpus/packet_recv_client/a12fb9c088abf60e98fdc28d049cd0077a443f41
new file mode 100644
index 0000000..f23278e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/a12fb9c088abf60e98fdc28d049cd0077a443f41
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/a1468518a0217537d50877cf7654c438102ce2da b/fuzz/corpus/packet_recv_client/a1468518a0217537d50877cf7654c438102ce2da
new file mode 100644
index 0000000..bd8d72a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/a1468518a0217537d50877cf7654c438102ce2da
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/a1cb0bb1185633270155044d31ba254f913b4a28 b/fuzz/corpus/packet_recv_client/a1cb0bb1185633270155044d31ba254f913b4a28
new file mode 100644
index 0000000..2b8c9a4
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/a1cb0bb1185633270155044d31ba254f913b4a28
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/a22d2a21d7a644091bb574cbe1b047d997b4fb4d b/fuzz/corpus/packet_recv_client/a22d2a21d7a644091bb574cbe1b047d997b4fb4d
new file mode 100644
index 0000000..0d1e1f3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/a22d2a21d7a644091bb574cbe1b047d997b4fb4d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/a2d3497cd301f28e2e90ccacd1fc877ff47396ab b/fuzz/corpus/packet_recv_client/a2d3497cd301f28e2e90ccacd1fc877ff47396ab
deleted file mode 100644
index 99c33ba..0000000
--- a/fuzz/corpus/packet_recv_client/a2d3497cd301f28e2e90ccacd1fc877ff47396ab
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/a2f280142a20d9d55e2fe27cad2a73936723688c b/fuzz/corpus/packet_recv_client/a2f280142a20d9d55e2fe27cad2a73936723688c
new file mode 100644
index 0000000..25983a2
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/a2f280142a20d9d55e2fe27cad2a73936723688c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/a382f3ae1a59b13533df96ab8899a11d0853cca3 b/fuzz/corpus/packet_recv_client/a382f3ae1a59b13533df96ab8899a11d0853cca3
new file mode 100644
index 0000000..26c54e1
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/a382f3ae1a59b13533df96ab8899a11d0853cca3
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/a45c6caf3de3f593587273aadb03aabc6428b291 b/fuzz/corpus/packet_recv_client/a45c6caf3de3f593587273aadb03aabc6428b291
new file mode 100644
index 0000000..964bedf
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/a45c6caf3de3f593587273aadb03aabc6428b291
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/a538609117f07c1a1f4e75ceecf57eb79f8f680d b/fuzz/corpus/packet_recv_client/a538609117f07c1a1f4e75ceecf57eb79f8f680d
new file mode 100644
index 0000000..08c1309
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/a538609117f07c1a1f4e75ceecf57eb79f8f680d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/a56c3287749adb9e79293705e0061a41db518788 b/fuzz/corpus/packet_recv_client/a56c3287749adb9e79293705e0061a41db518788
new file mode 100644
index 0000000..a017e02
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/a56c3287749adb9e79293705e0061a41db518788
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/a65ac7c719e43d53b4f07e0f1c1239b25fb73d5c b/fuzz/corpus/packet_recv_client/a65ac7c719e43d53b4f07e0f1c1239b25fb73d5c
new file mode 100644
index 0000000..df2a995
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/a65ac7c719e43d53b4f07e0f1c1239b25fb73d5c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/a7300997c810cdb8e9eaa2e39e5fd47cc89577b0 b/fuzz/corpus/packet_recv_client/a7300997c810cdb8e9eaa2e39e5fd47cc89577b0
deleted file mode 100644
index 1387d7b..0000000
--- a/fuzz/corpus/packet_recv_client/a7300997c810cdb8e9eaa2e39e5fd47cc89577b0
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/a79cc1138f372342e76046811b1d21298f9f6ada b/fuzz/corpus/packet_recv_client/a79cc1138f372342e76046811b1d21298f9f6ada
new file mode 100644
index 0000000..11ef4dc
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/a79cc1138f372342e76046811b1d21298f9f6ada
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/a7c00395b2d0f3b7fe9ee068d25d44e1cedf1301 b/fuzz/corpus/packet_recv_client/a7c00395b2d0f3b7fe9ee068d25d44e1cedf1301
new file mode 100644
index 0000000..3ce9a27
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/a7c00395b2d0f3b7fe9ee068d25d44e1cedf1301
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/a93984f6ff0834337ef7877b93ade88473cc4da4 b/fuzz/corpus/packet_recv_client/a93984f6ff0834337ef7877b93ade88473cc4da4
new file mode 100644
index 0000000..265834f
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/a93984f6ff0834337ef7877b93ade88473cc4da4
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/a9444c741b8d13d48afe9d8b84e6ef9925e9183e b/fuzz/corpus/packet_recv_client/a9444c741b8d13d48afe9d8b84e6ef9925e9183e
new file mode 100644
index 0000000..7e4c9c0
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/a9444c741b8d13d48afe9d8b84e6ef9925e9183e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/a983441251164046ae4c1fea35a791caaa4d9add b/fuzz/corpus/packet_recv_client/a983441251164046ae4c1fea35a791caaa4d9add
new file mode 100644
index 0000000..1368923
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/a983441251164046ae4c1fea35a791caaa4d9add
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/a9b86c6b90bfe3742740098eb9a43d5faf2a7cc2 b/fuzz/corpus/packet_recv_client/a9b86c6b90bfe3742740098eb9a43d5faf2a7cc2
deleted file mode 100644
index 1421260..0000000
--- a/fuzz/corpus/packet_recv_client/a9b86c6b90bfe3742740098eb9a43d5faf2a7cc2
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/aa29ba1720bcf4ecce12844858b7006b7b0cde93 b/fuzz/corpus/packet_recv_client/aa29ba1720bcf4ecce12844858b7006b7b0cde93
deleted file mode 100644
index 3b483de..0000000
--- a/fuzz/corpus/packet_recv_client/aa29ba1720bcf4ecce12844858b7006b7b0cde93
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/aa7ab9a72e95b702ec55a27984b9de4c5266a6b7 b/fuzz/corpus/packet_recv_client/aa7ab9a72e95b702ec55a27984b9de4c5266a6b7
new file mode 100644
index 0000000..f90b494
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/aa7ab9a72e95b702ec55a27984b9de4c5266a6b7
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ab2859c28acb13b95b803743a34960cd5ee27d98 b/fuzz/corpus/packet_recv_client/ab2859c28acb13b95b803743a34960cd5ee27d98
new file mode 100644
index 0000000..0c42eb0
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/ab2859c28acb13b95b803743a34960cd5ee27d98
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ab2a579113e608f9b115d48e6d570f20e9166400 b/fuzz/corpus/packet_recv_client/ab2a579113e608f9b115d48e6d570f20e9166400
new file mode 100644
index 0000000..20bf1e9
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/ab2a579113e608f9b115d48e6d570f20e9166400
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ac2a73194c6a0adf412451d2d0c25991e77dc13e b/fuzz/corpus/packet_recv_client/ac2a73194c6a0adf412451d2d0c25991e77dc13e
deleted file mode 100644
index 6a1fabb..0000000
--- a/fuzz/corpus/packet_recv_client/ac2a73194c6a0adf412451d2d0c25991e77dc13e
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ac3ce59daa5ceb181df7faac82cb3fc8fe97e33e b/fuzz/corpus/packet_recv_client/ac3ce59daa5ceb181df7faac82cb3fc8fe97e33e
new file mode 100644
index 0000000..07983c5
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/ac3ce59daa5ceb181df7faac82cb3fc8fe97e33e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ac409209cef96e6e50e42f57edcc364b2a725102 b/fuzz/corpus/packet_recv_client/ac409209cef96e6e50e42f57edcc364b2a725102
new file mode 100644
index 0000000..260d36d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/ac409209cef96e6e50e42f57edcc364b2a725102
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ace1474c0c5a6a5405bf11ef77f5ad1a62dc5056 b/fuzz/corpus/packet_recv_client/ace1474c0c5a6a5405bf11ef77f5ad1a62dc5056
new file mode 100644
index 0000000..a7b5b1f
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/ace1474c0c5a6a5405bf11ef77f5ad1a62dc5056
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ad04f905c99e7281be605015457d3a050c779b2a b/fuzz/corpus/packet_recv_client/ad04f905c99e7281be605015457d3a050c779b2a
new file mode 100644
index 0000000..817c049
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/ad04f905c99e7281be605015457d3a050c779b2a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ad1d839b9958bc7ab895924ffc12b7ad3d9eaa87 b/fuzz/corpus/packet_recv_client/ad1d839b9958bc7ab895924ffc12b7ad3d9eaa87
new file mode 100644
index 0000000..3721687
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/ad1d839b9958bc7ab895924ffc12b7ad3d9eaa87
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ad1df59fe9a1f4485582a42307709a941c9f371b b/fuzz/corpus/packet_recv_client/ad1df59fe9a1f4485582a42307709a941c9f371b
deleted file mode 100644
index 09363be..0000000
--- a/fuzz/corpus/packet_recv_client/ad1df59fe9a1f4485582a42307709a941c9f371b
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ad4cb1b4593b28295dc3c8ac73c1f268063a7ca7 b/fuzz/corpus/packet_recv_client/ad4cb1b4593b28295dc3c8ac73c1f268063a7ca7
new file mode 100644
index 0000000..16ab689
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/ad4cb1b4593b28295dc3c8ac73c1f268063a7ca7
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ad8472c7d3df66a0a6c23320e3f9ed390033dd43 b/fuzz/corpus/packet_recv_client/ad8472c7d3df66a0a6c23320e3f9ed390033dd43
new file mode 100644
index 0000000..529c71d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/ad8472c7d3df66a0a6c23320e3f9ed390033dd43
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/adc2624cfe116d7960159595ed99caf6bc9b2616 b/fuzz/corpus/packet_recv_client/adc2624cfe116d7960159595ed99caf6bc9b2616
new file mode 100644
index 0000000..d755720
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/adc2624cfe116d7960159595ed99caf6bc9b2616
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ae080824027e3eac2e5c9466b464fcbf0f970884 b/fuzz/corpus/packet_recv_client/ae080824027e3eac2e5c9466b464fcbf0f970884
new file mode 100644
index 0000000..fcee374
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/ae080824027e3eac2e5c9466b464fcbf0f970884
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ae60de2ef07f2f10548e33ea9a420561c8c32371 b/fuzz/corpus/packet_recv_client/ae60de2ef07f2f10548e33ea9a420561c8c32371
new file mode 100644
index 0000000..2c5591c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/ae60de2ef07f2f10548e33ea9a420561c8c32371
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/aedfe8977e9badf66ceee629107a064df947a556 b/fuzz/corpus/packet_recv_client/aedfe8977e9badf66ceee629107a064df947a556
new file mode 100644
index 0000000..40dcf5b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/aedfe8977e9badf66ceee629107a064df947a556
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/aeeac32f70496cfecabfc4abeae89bf0b5d2ae45 b/fuzz/corpus/packet_recv_client/aeeac32f70496cfecabfc4abeae89bf0b5d2ae45
new file mode 100644
index 0000000..6f23900
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/aeeac32f70496cfecabfc4abeae89bf0b5d2ae45
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/af6f01f368c5c50ec35ae82da38cd0eb2ae10f63 b/fuzz/corpus/packet_recv_client/af6f01f368c5c50ec35ae82da38cd0eb2ae10f63
new file mode 100644
index 0000000..6b28ea3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/af6f01f368c5c50ec35ae82da38cd0eb2ae10f63
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/b00df21b15be69b3d9439dc160e11f66f82e9606 b/fuzz/corpus/packet_recv_client/b00df21b15be69b3d9439dc160e11f66f82e9606
new file mode 100644
index 0000000..85187d0
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/b00df21b15be69b3d9439dc160e11f66f82e9606
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/b05f48e8bf8f224baf76415e93f5f95049855300 b/fuzz/corpus/packet_recv_client/b05f48e8bf8f224baf76415e93f5f95049855300
new file mode 100644
index 0000000..c0dcc6b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/b05f48e8bf8f224baf76415e93f5f95049855300
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/b094bfc273f56196f602551a1dc7b1a0461b417a b/fuzz/corpus/packet_recv_client/b094bfc273f56196f602551a1dc7b1a0461b417a
deleted file mode 100644
index e767820..0000000
--- a/fuzz/corpus/packet_recv_client/b094bfc273f56196f602551a1dc7b1a0461b417a
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/b1401904bcc5798781e21ed550aeb10af5da8abf b/fuzz/corpus/packet_recv_client/b1401904bcc5798781e21ed550aeb10af5da8abf
new file mode 100644
index 0000000..3ff2d70
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/b1401904bcc5798781e21ed550aeb10af5da8abf
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/b1ee90733364b5d4d275900a4ddae7650c3f5f01 b/fuzz/corpus/packet_recv_client/b1ee90733364b5d4d275900a4ddae7650c3f5f01
deleted file mode 100644
index 31808a3..0000000
--- a/fuzz/corpus/packet_recv_client/b1ee90733364b5d4d275900a4ddae7650c3f5f01
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/b1f667e7d55d6cca2173a9adeb67b7c34c9d9d88 b/fuzz/corpus/packet_recv_client/b1f667e7d55d6cca2173a9adeb67b7c34c9d9d88
new file mode 100644
index 0000000..b845648
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/b1f667e7d55d6cca2173a9adeb67b7c34c9d9d88
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/b2371fcb5d75e9849648cd992f943a08fc2b123f b/fuzz/corpus/packet_recv_client/b2371fcb5d75e9849648cd992f943a08fc2b123f
new file mode 100644
index 0000000..fd52798
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/b2371fcb5d75e9849648cd992f943a08fc2b123f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/b402e76b1527c966d8a32e9842009a568ab7ef15 b/fuzz/corpus/packet_recv_client/b402e76b1527c966d8a32e9842009a568ab7ef15
deleted file mode 100644
index f894e19..0000000
--- a/fuzz/corpus/packet_recv_client/b402e76b1527c966d8a32e9842009a568ab7ef15
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/b43497b4fae93508de7bf36e9d7a1d5fa02d4b6d b/fuzz/corpus/packet_recv_client/b43497b4fae93508de7bf36e9d7a1d5fa02d4b6d
deleted file mode 100644
index 7830fb6..0000000
--- a/fuzz/corpus/packet_recv_client/b43497b4fae93508de7bf36e9d7a1d5fa02d4b6d
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/b55ee72df32801c7725730dadf3bd97309389007 b/fuzz/corpus/packet_recv_client/b55ee72df32801c7725730dadf3bd97309389007
new file mode 100644
index 0000000..2cd737e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/b55ee72df32801c7725730dadf3bd97309389007
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/b5c33a06a139b0429d559f87739bbbc197bc4cd3 b/fuzz/corpus/packet_recv_client/b5c33a06a139b0429d559f87739bbbc197bc4cd3
new file mode 100644
index 0000000..10d5e17
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/b5c33a06a139b0429d559f87739bbbc197bc4cd3
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/b5e4e86fca23306088c1edbcd9c6737b23c9f25a b/fuzz/corpus/packet_recv_client/b5e4e86fca23306088c1edbcd9c6737b23c9f25a
new file mode 100644
index 0000000..2999c68
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/b5e4e86fca23306088c1edbcd9c6737b23c9f25a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/b6e2624d013a1443c76919127eefab976f084b11 b/fuzz/corpus/packet_recv_client/b6e2624d013a1443c76919127eefab976f084b11
new file mode 100644
index 0000000..8d37612
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/b6e2624d013a1443c76919127eefab976f084b11
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/b6e348466bf9d05182851881c46e9c368b2fc788 b/fuzz/corpus/packet_recv_client/b6e348466bf9d05182851881c46e9c368b2fc788
new file mode 100644
index 0000000..f400efb
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/b6e348466bf9d05182851881c46e9c368b2fc788
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/b7de96dcafc46f451c1790162060ce375091b29d b/fuzz/corpus/packet_recv_client/b7de96dcafc46f451c1790162060ce375091b29d
new file mode 100644
index 0000000..8e43cdd
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/b7de96dcafc46f451c1790162060ce375091b29d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/b88d71af89f1a0e4e60fa53b81ba98fc818d849c b/fuzz/corpus/packet_recv_client/b88d71af89f1a0e4e60fa53b81ba98fc818d849c
new file mode 100644
index 0000000..2f49631
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/b88d71af89f1a0e4e60fa53b81ba98fc818d849c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/b8a498390746f85ea9543f0ef8fa116b77949e8a b/fuzz/corpus/packet_recv_client/b8a498390746f85ea9543f0ef8fa116b77949e8a
deleted file mode 100644
index a223eaa..0000000
--- a/fuzz/corpus/packet_recv_client/b8a498390746f85ea9543f0ef8fa116b77949e8a
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ba585cc81c33b784dd023c5e9c690a52893c6748 b/fuzz/corpus/packet_recv_client/ba585cc81c33b784dd023c5e9c690a52893c6748
new file mode 100644
index 0000000..f6f19fc
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/ba585cc81c33b784dd023c5e9c690a52893c6748
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ba600445e42612b6e8c63868f43ef601440d7a1d b/fuzz/corpus/packet_recv_client/ba600445e42612b6e8c63868f43ef601440d7a1d
new file mode 100644
index 0000000..f1b476f
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/ba600445e42612b6e8c63868f43ef601440d7a1d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/baca97ad2b3e440226a67e197f0f2e026f8c0a14 b/fuzz/corpus/packet_recv_client/baca97ad2b3e440226a67e197f0f2e026f8c0a14
new file mode 100644
index 0000000..1e58999
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/baca97ad2b3e440226a67e197f0f2e026f8c0a14
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/bb380976d7ad444c7988f201c8f4010d23dea3a1 b/fuzz/corpus/packet_recv_client/bb380976d7ad444c7988f201c8f4010d23dea3a1
new file mode 100644
index 0000000..1eae6e7
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/bb380976d7ad444c7988f201c8f4010d23dea3a1
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/bc2ecc86dc5cc916bf57f94a7827d7c887edc58a b/fuzz/corpus/packet_recv_client/bc2ecc86dc5cc916bf57f94a7827d7c887edc58a
new file mode 100644
index 0000000..0afb422
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/bc2ecc86dc5cc916bf57f94a7827d7c887edc58a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/bc31ed728ee5f134004f08f0379422ba3f7c69ed b/fuzz/corpus/packet_recv_client/bc31ed728ee5f134004f08f0379422ba3f7c69ed
deleted file mode 100644
index ddddb39..0000000
--- a/fuzz/corpus/packet_recv_client/bc31ed728ee5f134004f08f0379422ba3f7c69ed
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/bd4b3c5a7eb0e7b2aa0f6e2133e358333719775a b/fuzz/corpus/packet_recv_client/bd4b3c5a7eb0e7b2aa0f6e2133e358333719775a
new file mode 100644
index 0000000..fa50946
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/bd4b3c5a7eb0e7b2aa0f6e2133e358333719775a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/bd6e0afb108faba056e07d6a7b2b21cfbef02303 b/fuzz/corpus/packet_recv_client/bd6e0afb108faba056e07d6a7b2b21cfbef02303
new file mode 100644
index 0000000..1ead96b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/bd6e0afb108faba056e07d6a7b2b21cfbef02303
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/bdc1078b66bc0a85c3442b221c596cf5c438bd24 b/fuzz/corpus/packet_recv_client/bdc1078b66bc0a85c3442b221c596cf5c438bd24
new file mode 100644
index 0000000..8e03368
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/bdc1078b66bc0a85c3442b221c596cf5c438bd24
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/be6ba42b9a6887e47da6e084f2d27a1de0e2f387 b/fuzz/corpus/packet_recv_client/be6ba42b9a6887e47da6e084f2d27a1de0e2f387
new file mode 100644
index 0000000..78fd2ae
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/be6ba42b9a6887e47da6e084f2d27a1de0e2f387
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/bee1d006930bec4b90dd03b1825de927a791de3b b/fuzz/corpus/packet_recv_client/bee1d006930bec4b90dd03b1825de927a791de3b
new file mode 100644
index 0000000..0afbab9
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/bee1d006930bec4b90dd03b1825de927a791de3b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/bf19313a1bc5877b0dc0629d15a03851cf6632d2 b/fuzz/corpus/packet_recv_client/bf19313a1bc5877b0dc0629d15a03851cf6632d2
new file mode 100644
index 0000000..1e9a36f
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/bf19313a1bc5877b0dc0629d15a03851cf6632d2
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/c0387bc12b74735fb66aa163506a9ceb16485677 b/fuzz/corpus/packet_recv_client/c0387bc12b74735fb66aa163506a9ceb16485677
new file mode 100644
index 0000000..f3e6d46
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/c0387bc12b74735fb66aa163506a9ceb16485677
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/c078fef5ae298e80e6d418cef973157ee1ed6438 b/fuzz/corpus/packet_recv_client/c078fef5ae298e80e6d418cef973157ee1ed6438
new file mode 100644
index 0000000..14891bb
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/c078fef5ae298e80e6d418cef973157ee1ed6438
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/c0a845fa747ef71a915de15e4ea4827302ba39f7 b/fuzz/corpus/packet_recv_client/c0a845fa747ef71a915de15e4ea4827302ba39f7
new file mode 100644
index 0000000..b4160a7
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/c0a845fa747ef71a915de15e4ea4827302ba39f7
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/c0c35274c156edae950aad163fcb47251b21df9c b/fuzz/corpus/packet_recv_client/c0c35274c156edae950aad163fcb47251b21df9c
new file mode 100644
index 0000000..6cc4b84
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/c0c35274c156edae950aad163fcb47251b21df9c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/c151ea3dee683fba5623fd13fd51f5fde5e77c37 b/fuzz/corpus/packet_recv_client/c151ea3dee683fba5623fd13fd51f5fde5e77c37
new file mode 100644
index 0000000..6720435
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/c151ea3dee683fba5623fd13fd51f5fde5e77c37
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/c1750029fb473e9123e78ccb9840ba42e8f94ab9 b/fuzz/corpus/packet_recv_client/c1750029fb473e9123e78ccb9840ba42e8f94ab9
new file mode 100644
index 0000000..0b94bb3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/c1750029fb473e9123e78ccb9840ba42e8f94ab9
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/c1cee0ae2ba91fd9f0c9b9a8100af6b11dd96305 b/fuzz/corpus/packet_recv_client/c1cee0ae2ba91fd9f0c9b9a8100af6b11dd96305
deleted file mode 100644
index 9bfae58..0000000
--- a/fuzz/corpus/packet_recv_client/c1cee0ae2ba91fd9f0c9b9a8100af6b11dd96305
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/c3e3e743b5746d5cbb13e025bf76e57e2b4dc245 b/fuzz/corpus/packet_recv_client/c3e3e743b5746d5cbb13e025bf76e57e2b4dc245
new file mode 100644
index 0000000..45543d5
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/c3e3e743b5746d5cbb13e025bf76e57e2b4dc245
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/c3fe284c71378f1dc50f1588544990fbcd122f58 b/fuzz/corpus/packet_recv_client/c3fe284c71378f1dc50f1588544990fbcd122f58
new file mode 100644
index 0000000..1415979
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/c3fe284c71378f1dc50f1588544990fbcd122f58
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/c4249b457490a0ef8e7760e846e8b94159bc0d31 b/fuzz/corpus/packet_recv_client/c4249b457490a0ef8e7760e846e8b94159bc0d31
new file mode 100644
index 0000000..93c000f
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/c4249b457490a0ef8e7760e846e8b94159bc0d31
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/c441ad0fee7ff3fd21914fc1e270c9b2ea0d8c07 b/fuzz/corpus/packet_recv_client/c441ad0fee7ff3fd21914fc1e270c9b2ea0d8c07
new file mode 100644
index 0000000..2b27f92
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/c441ad0fee7ff3fd21914fc1e270c9b2ea0d8c07
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/c4762a1899aa3923efea41da3d3ff77aa71e3b11 b/fuzz/corpus/packet_recv_client/c4762a1899aa3923efea41da3d3ff77aa71e3b11
new file mode 100644
index 0000000..9254de4
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/c4762a1899aa3923efea41da3d3ff77aa71e3b11
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/c6310d482113618f6dcdce1c79cc2fc33a78b2cb b/fuzz/corpus/packet_recv_client/c6310d482113618f6dcdce1c79cc2fc33a78b2cb
new file mode 100644
index 0000000..cdf82ac
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/c6310d482113618f6dcdce1c79cc2fc33a78b2cb
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/c6d2632afd20a12e03f084698ff82dd859bd7c80 b/fuzz/corpus/packet_recv_client/c6d2632afd20a12e03f084698ff82dd859bd7c80
deleted file mode 100644
index ab6fa9e..0000000
--- a/fuzz/corpus/packet_recv_client/c6d2632afd20a12e03f084698ff82dd859bd7c80
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/c70acd32db9bc3ee8f5567703267b50f99e3436b b/fuzz/corpus/packet_recv_client/c70acd32db9bc3ee8f5567703267b50f99e3436b
new file mode 100644
index 0000000..a6f523d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/c70acd32db9bc3ee8f5567703267b50f99e3436b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/c7b013fdfd7977bf492d4f945941f2346f66c011 b/fuzz/corpus/packet_recv_client/c7b013fdfd7977bf492d4f945941f2346f66c011
new file mode 100644
index 0000000..ab41b79
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/c7b013fdfd7977bf492d4f945941f2346f66c011
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/c942b2a5cbe691a247677ce608786849a71f07d7 b/fuzz/corpus/packet_recv_client/c942b2a5cbe691a247677ce608786849a71f07d7
new file mode 100644
index 0000000..5a8f894
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/c942b2a5cbe691a247677ce608786849a71f07d7
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ca1874998f41af7c6a4680077150764f45bed778 b/fuzz/corpus/packet_recv_client/ca1874998f41af7c6a4680077150764f45bed778
new file mode 100644
index 0000000..c5f6c04
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/ca1874998f41af7c6a4680077150764f45bed778
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/cb63f7fc4b915aeff6776cf96b7f20140bc11675 b/fuzz/corpus/packet_recv_client/cb63f7fc4b915aeff6776cf96b7f20140bc11675
deleted file mode 100644
index 5c4788b..0000000
--- a/fuzz/corpus/packet_recv_client/cb63f7fc4b915aeff6776cf96b7f20140bc11675
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/cbb918c437f9e3ec49eaf1680afc8191c082c920 b/fuzz/corpus/packet_recv_client/cbb918c437f9e3ec49eaf1680afc8191c082c920
new file mode 100644
index 0000000..3b7a60e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/cbb918c437f9e3ec49eaf1680afc8191c082c920
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/cc13361958aea790b433589784ccf242c0e220a2 b/fuzz/corpus/packet_recv_client/cc13361958aea790b433589784ccf242c0e220a2
new file mode 100644
index 0000000..934239d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/cc13361958aea790b433589784ccf242c0e220a2
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/cc71a1c2453ab9dcfd8f5b2f8177dbaacf50ba49 b/fuzz/corpus/packet_recv_client/cc71a1c2453ab9dcfd8f5b2f8177dbaacf50ba49
new file mode 100644
index 0000000..9252d5c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/cc71a1c2453ab9dcfd8f5b2f8177dbaacf50ba49
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/cc7a5873b3f2fd972c5c14714f02f52aab40bd8f b/fuzz/corpus/packet_recv_client/cc7a5873b3f2fd972c5c14714f02f52aab40bd8f
deleted file mode 100644
index 797e904..0000000
--- a/fuzz/corpus/packet_recv_client/cc7a5873b3f2fd972c5c14714f02f52aab40bd8f
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/cd032000137faf302fd4a76310546d49e6a3dd46 b/fuzz/corpus/packet_recv_client/cd032000137faf302fd4a76310546d49e6a3dd46
new file mode 100644
index 0000000..50ff3bc
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/cd032000137faf302fd4a76310546d49e6a3dd46
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/cebb03f8bc60cbe6c88669f454e9b4fe75f5981d b/fuzz/corpus/packet_recv_client/cebb03f8bc60cbe6c88669f454e9b4fe75f5981d
new file mode 100644
index 0000000..4e3e622
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/cebb03f8bc60cbe6c88669f454e9b4fe75f5981d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/cf1541f07febec4c6a740d0b3c273e0e8a1f2ce8 b/fuzz/corpus/packet_recv_client/cf1541f07febec4c6a740d0b3c273e0e8a1f2ce8
new file mode 100644
index 0000000..39a0409
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/cf1541f07febec4c6a740d0b3c273e0e8a1f2ce8
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/cf6fa7b9a5b06c6a1215a511a83b2f3dccea1874 b/fuzz/corpus/packet_recv_client/cf6fa7b9a5b06c6a1215a511a83b2f3dccea1874
new file mode 100644
index 0000000..fe46020
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/cf6fa7b9a5b06c6a1215a511a83b2f3dccea1874
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/cfcabad68c558500025471a4c8fe9361114d8730 b/fuzz/corpus/packet_recv_client/cfcabad68c558500025471a4c8fe9361114d8730
new file mode 100644
index 0000000..f9db810
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/cfcabad68c558500025471a4c8fe9361114d8730
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/d1655e1405fd0698d4ab4846e000a5832cf97b62 b/fuzz/corpus/packet_recv_client/d1655e1405fd0698d4ab4846e000a5832cf97b62
new file mode 100644
index 0000000..4ae3e33
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/d1655e1405fd0698d4ab4846e000a5832cf97b62
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/d1abe3529c0858af4591fa8fbee3aab33e32f27f b/fuzz/corpus/packet_recv_client/d1abe3529c0858af4591fa8fbee3aab33e32f27f
deleted file mode 100644
index 0146c6b..0000000
--- a/fuzz/corpus/packet_recv_client/d1abe3529c0858af4591fa8fbee3aab33e32f27f
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/d24f73f8ea2435aeb7423418649d4e6bb47d9dd1 b/fuzz/corpus/packet_recv_client/d24f73f8ea2435aeb7423418649d4e6bb47d9dd1
new file mode 100644
index 0000000..bdc5676
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/d24f73f8ea2435aeb7423418649d4e6bb47d9dd1
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/d2b378891ac6d97b4aa44d10470abaf3b19d85db b/fuzz/corpus/packet_recv_client/d2b378891ac6d97b4aa44d10470abaf3b19d85db
new file mode 100644
index 0000000..fc865ea
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/d2b378891ac6d97b4aa44d10470abaf3b19d85db
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/d40d80951ff1edd11a333bf99762928f9a3c5963 b/fuzz/corpus/packet_recv_client/d40d80951ff1edd11a333bf99762928f9a3c5963
new file mode 100644
index 0000000..74727cf
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/d40d80951ff1edd11a333bf99762928f9a3c5963
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/d412e5edd2f23a5bd11ea5b733029b2ce5e8be06 b/fuzz/corpus/packet_recv_client/d412e5edd2f23a5bd11ea5b733029b2ce5e8be06
new file mode 100644
index 0000000..29770d7
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/d412e5edd2f23a5bd11ea5b733029b2ce5e8be06
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/d4171f0688ed9062903ffa95cc0634d63b9a0402 b/fuzz/corpus/packet_recv_client/d4171f0688ed9062903ffa95cc0634d63b9a0402
new file mode 100644
index 0000000..83dca3a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/d4171f0688ed9062903ffa95cc0634d63b9a0402
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/d43c8a394b2c3a3b2521d08eb3d5d32d30c49c94 b/fuzz/corpus/packet_recv_client/d43c8a394b2c3a3b2521d08eb3d5d32d30c49c94
new file mode 100644
index 0000000..c8147e2
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/d43c8a394b2c3a3b2521d08eb3d5d32d30c49c94
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/d485061e0abe713b726a600c6511f3182e2d62f7 b/fuzz/corpus/packet_recv_client/d485061e0abe713b726a600c6511f3182e2d62f7
new file mode 100644
index 0000000..6ad2f40
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/d485061e0abe713b726a600c6511f3182e2d62f7
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/d4b16ecbcf59e17a3139ec4d4ed05ef67f1b9426 b/fuzz/corpus/packet_recv_client/d4b16ecbcf59e17a3139ec4d4ed05ef67f1b9426
new file mode 100644
index 0000000..65f0e25
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/d4b16ecbcf59e17a3139ec4d4ed05ef67f1b9426
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/d57eef31b0e59fa4be08bb6b6f2254a253e21acb b/fuzz/corpus/packet_recv_client/d57eef31b0e59fa4be08bb6b6f2254a253e21acb
new file mode 100644
index 0000000..f857138
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/d57eef31b0e59fa4be08bb6b6f2254a253e21acb
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/d5d74b3e04253b978f33cb2be1e19fe0beb53efd b/fuzz/corpus/packet_recv_client/d5d74b3e04253b978f33cb2be1e19fe0beb53efd
new file mode 100644
index 0000000..3e92ec4
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/d5d74b3e04253b978f33cb2be1e19fe0beb53efd
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/d6c4bd07b5d032e22446ee75b4b03a13ce446663 b/fuzz/corpus/packet_recv_client/d6c4bd07b5d032e22446ee75b4b03a13ce446663
new file mode 100644
index 0000000..db440fa
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/d6c4bd07b5d032e22446ee75b4b03a13ce446663
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/d6fdfa7747dd50cb2ba1693b522ec109d6cfc339 b/fuzz/corpus/packet_recv_client/d6fdfa7747dd50cb2ba1693b522ec109d6cfc339
new file mode 100644
index 0000000..b8f237c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/d6fdfa7747dd50cb2ba1693b522ec109d6cfc339
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/d7f22cf1c82413958c07251425aaeff4bacb4334 b/fuzz/corpus/packet_recv_client/d7f22cf1c82413958c07251425aaeff4bacb4334
new file mode 100644
index 0000000..f843cac
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/d7f22cf1c82413958c07251425aaeff4bacb4334
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/d825d2646b1ad3bf320ba152f0a8d23aa7f7db13 b/fuzz/corpus/packet_recv_client/d825d2646b1ad3bf320ba152f0a8d23aa7f7db13
new file mode 100644
index 0000000..288a333
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/d825d2646b1ad3bf320ba152f0a8d23aa7f7db13
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/d9412e3408d5fc77ccaffbb5a0e313e67bc48ecd b/fuzz/corpus/packet_recv_client/d9412e3408d5fc77ccaffbb5a0e313e67bc48ecd
deleted file mode 100644
index 96d4326..0000000
--- a/fuzz/corpus/packet_recv_client/d9412e3408d5fc77ccaffbb5a0e313e67bc48ecd
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/d9635fd3e6c5b428559436675f33dea05ac08a08 b/fuzz/corpus/packet_recv_client/d9635fd3e6c5b428559436675f33dea05ac08a08
new file mode 100644
index 0000000..02dbeb3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/d9635fd3e6c5b428559436675f33dea05ac08a08
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/d97d67729a9aa525dd083625c0cc00d55cdc46b1 b/fuzz/corpus/packet_recv_client/d97d67729a9aa525dd083625c0cc00d55cdc46b1
new file mode 100644
index 0000000..e394870
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/d97d67729a9aa525dd083625c0cc00d55cdc46b1
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/d980459920f521e991ccf8c175dcbdfa6de11131 b/fuzz/corpus/packet_recv_client/d980459920f521e991ccf8c175dcbdfa6de11131
new file mode 100644
index 0000000..22abc9d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/d980459920f521e991ccf8c175dcbdfa6de11131
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/dac845f8133402659cf86f1e1b8323b246387de8 b/fuzz/corpus/packet_recv_client/dac845f8133402659cf86f1e1b8323b246387de8
new file mode 100644
index 0000000..4e474cb
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/dac845f8133402659cf86f1e1b8323b246387de8
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/dad42d5aa7cf45257ad404da71ff5b96dfcefdc0 b/fuzz/corpus/packet_recv_client/dad42d5aa7cf45257ad404da71ff5b96dfcefdc0
new file mode 100644
index 0000000..ef11298
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/dad42d5aa7cf45257ad404da71ff5b96dfcefdc0
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/db241b3ab5cb1da3bd3d897f0b6316c75236022c b/fuzz/corpus/packet_recv_client/db241b3ab5cb1da3bd3d897f0b6316c75236022c
new file mode 100644
index 0000000..8c7188e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/db241b3ab5cb1da3bd3d897f0b6316c75236022c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/db42045af1d3ee0d1e04c03333e9b9521041391b b/fuzz/corpus/packet_recv_client/db42045af1d3ee0d1e04c03333e9b9521041391b
new file mode 100644
index 0000000..ebcefa2
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/db42045af1d3ee0d1e04c03333e9b9521041391b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/dbc08bd4aa074b1cd7bc891c1d0f25af8929af55 b/fuzz/corpus/packet_recv_client/dbc08bd4aa074b1cd7bc891c1d0f25af8929af55
new file mode 100644
index 0000000..246818f
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/dbc08bd4aa074b1cd7bc891c1d0f25af8929af55
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/dbf21dcc431b769c80526a02db1431177711cfa9 b/fuzz/corpus/packet_recv_client/dbf21dcc431b769c80526a02db1431177711cfa9
new file mode 100644
index 0000000..6a57ae2
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/dbf21dcc431b769c80526a02db1431177711cfa9
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/dd3c1ffacdf4a04e5c688e81a9aa449f5bc96148 b/fuzz/corpus/packet_recv_client/dd3c1ffacdf4a04e5c688e81a9aa449f5bc96148
new file mode 100644
index 0000000..e78f290
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/dd3c1ffacdf4a04e5c688e81a9aa449f5bc96148
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/dd6b02127e2a5d5cee916075fc66fc731e4aa530 b/fuzz/corpus/packet_recv_client/dd6b02127e2a5d5cee916075fc66fc731e4aa530
new file mode 100644
index 0000000..d1ee60a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/dd6b02127e2a5d5cee916075fc66fc731e4aa530
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ddc28524eaf52387d83edccd12a3b09d3599b1cb b/fuzz/corpus/packet_recv_client/ddc28524eaf52387d83edccd12a3b09d3599b1cb
new file mode 100644
index 0000000..fd465bb
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/ddc28524eaf52387d83edccd12a3b09d3599b1cb
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/dea3590e77f0203754bb9f107ee8b4f472acdacc b/fuzz/corpus/packet_recv_client/dea3590e77f0203754bb9f107ee8b4f472acdacc
new file mode 100644
index 0000000..d8b4587
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/dea3590e77f0203754bb9f107ee8b4f472acdacc
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/debdc336317023de9cf7a91e63068313f33b21be b/fuzz/corpus/packet_recv_client/debdc336317023de9cf7a91e63068313f33b21be
new file mode 100644
index 0000000..8fea33c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/debdc336317023de9cf7a91e63068313f33b21be
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/df5a6210c8f70017b1762fe703fca926c199383d b/fuzz/corpus/packet_recv_client/df5a6210c8f70017b1762fe703fca926c199383d
deleted file mode 100644
index 4d8fd4a..0000000
--- a/fuzz/corpus/packet_recv_client/df5a6210c8f70017b1762fe703fca926c199383d
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/df645e4cd4406bcf1cce1eb23c408541de19feb4 b/fuzz/corpus/packet_recv_client/df645e4cd4406bcf1cce1eb23c408541de19feb4
new file mode 100644
index 0000000..bb73e0e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/df645e4cd4406bcf1cce1eb23c408541de19feb4
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/dfe294334f056cbc3904575f70d1fb82dc55e0d6 b/fuzz/corpus/packet_recv_client/dfe294334f056cbc3904575f70d1fb82dc55e0d6
new file mode 100644
index 0000000..9db23ee
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/dfe294334f056cbc3904575f70d1fb82dc55e0d6
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/e10aea33c6eb0f228c70fd4332c39b04c7134c01 b/fuzz/corpus/packet_recv_client/e10aea33c6eb0f228c70fd4332c39b04c7134c01
new file mode 100644
index 0000000..b465efd
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/e10aea33c6eb0f228c70fd4332c39b04c7134c01
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/e1f8a7e8b9fa6bad8ab0429a0b3c7980a4827c57 b/fuzz/corpus/packet_recv_client/e1f8a7e8b9fa6bad8ab0429a0b3c7980a4827c57
new file mode 100644
index 0000000..2f5ec5d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/e1f8a7e8b9fa6bad8ab0429a0b3c7980a4827c57
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/e20ca424ed8a40edd2ae710bbc5518d73a2c18bc b/fuzz/corpus/packet_recv_client/e20ca424ed8a40edd2ae710bbc5518d73a2c18bc
new file mode 100644
index 0000000..7e650dd
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/e20ca424ed8a40edd2ae710bbc5518d73a2c18bc
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/e3070c5c1e37b9a4be20dca2509eadab14fa1835 b/fuzz/corpus/packet_recv_client/e3070c5c1e37b9a4be20dca2509eadab14fa1835
deleted file mode 100644
index 916a8b1..0000000
--- a/fuzz/corpus/packet_recv_client/e3070c5c1e37b9a4be20dca2509eadab14fa1835
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/e3135d5a9d4d3b6f09aaa5fdb8dad71293d0e491 b/fuzz/corpus/packet_recv_client/e3135d5a9d4d3b6f09aaa5fdb8dad71293d0e491
deleted file mode 100644
index 6c4d1cb..0000000
--- a/fuzz/corpus/packet_recv_client/e3135d5a9d4d3b6f09aaa5fdb8dad71293d0e491
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/e35d2e3372cd7ea90afcd841ba292cc9b7b51205 b/fuzz/corpus/packet_recv_client/e35d2e3372cd7ea90afcd841ba292cc9b7b51205
new file mode 100644
index 0000000..7c62080
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/e35d2e3372cd7ea90afcd841ba292cc9b7b51205
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/e4183a1b50e0a7096b517d6e27a570c6630e4076 b/fuzz/corpus/packet_recv_client/e4183a1b50e0a7096b517d6e27a570c6630e4076
new file mode 100644
index 0000000..9719245
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/e4183a1b50e0a7096b517d6e27a570c6630e4076
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/e44d8e3445cdf8b6b00d504c686bb23fb5daf96b b/fuzz/corpus/packet_recv_client/e44d8e3445cdf8b6b00d504c686bb23fb5daf96b
new file mode 100644
index 0000000..6779525
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/e44d8e3445cdf8b6b00d504c686bb23fb5daf96b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/e4c8f5af2ea8ae69d5504ca2bab3c3fb6cd9329b b/fuzz/corpus/packet_recv_client/e4c8f5af2ea8ae69d5504ca2bab3c3fb6cd9329b
new file mode 100644
index 0000000..3b583f3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/e4c8f5af2ea8ae69d5504ca2bab3c3fb6cd9329b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/e4f9440a9b9ef37b7c800d0be7c29eb0b8bcad69 b/fuzz/corpus/packet_recv_client/e4f9440a9b9ef37b7c800d0be7c29eb0b8bcad69
new file mode 100644
index 0000000..deb8f62
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/e4f9440a9b9ef37b7c800d0be7c29eb0b8bcad69
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/e5ff1501b8a69bb01e5666d7d5b828468b550d46 b/fuzz/corpus/packet_recv_client/e5ff1501b8a69bb01e5666d7d5b828468b550d46
new file mode 100644
index 0000000..01e83ea
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/e5ff1501b8a69bb01e5666d7d5b828468b550d46
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/e62a5dc4e675ff055ea40c5eb331f551d5f66d02 b/fuzz/corpus/packet_recv_client/e62a5dc4e675ff055ea40c5eb331f551d5f66d02
new file mode 100644
index 0000000..8be79db
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/e62a5dc4e675ff055ea40c5eb331f551d5f66d02
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/e65f6a840dfcebfc82db10118fc9a5c0c3051737 b/fuzz/corpus/packet_recv_client/e65f6a840dfcebfc82db10118fc9a5c0c3051737
new file mode 100644
index 0000000..fa6cdea
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/e65f6a840dfcebfc82db10118fc9a5c0c3051737
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/e6623d4499b4d6f847b7777d7a7a771ce000ca40 b/fuzz/corpus/packet_recv_client/e6623d4499b4d6f847b7777d7a7a771ce000ca40
new file mode 100644
index 0000000..141ead4
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/e6623d4499b4d6f847b7777d7a7a771ce000ca40
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/e67a239d5c53a31bf482477d8d7aefa015c9b9f7 b/fuzz/corpus/packet_recv_client/e67a239d5c53a31bf482477d8d7aefa015c9b9f7
new file mode 100644
index 0000000..9b5ca12
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/e67a239d5c53a31bf482477d8d7aefa015c9b9f7
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/e6899e6dbdab10fd2d103ac3330948044fed5ce9 b/fuzz/corpus/packet_recv_client/e6899e6dbdab10fd2d103ac3330948044fed5ce9
new file mode 100644
index 0000000..b8a27b6
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/e6899e6dbdab10fd2d103ac3330948044fed5ce9
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/e7196d8bfbd208a23e835699b6f4209840c4a683 b/fuzz/corpus/packet_recv_client/e7196d8bfbd208a23e835699b6f4209840c4a683
deleted file mode 100644
index d5d57fc..0000000
--- a/fuzz/corpus/packet_recv_client/e7196d8bfbd208a23e835699b6f4209840c4a683
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/e79e5a56cc749ec26540b40d9261e1222436136f b/fuzz/corpus/packet_recv_client/e79e5a56cc749ec26540b40d9261e1222436136f
new file mode 100644
index 0000000..ae65cc8
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/e79e5a56cc749ec26540b40d9261e1222436136f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/e85100114a0783a715edd552117de05adcc9fa47 b/fuzz/corpus/packet_recv_client/e85100114a0783a715edd552117de05adcc9fa47
new file mode 100644
index 0000000..20a5b1b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/e85100114a0783a715edd552117de05adcc9fa47
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/e92fb449b2a6469e493c0a3625df412f6e3925cb b/fuzz/corpus/packet_recv_client/e92fb449b2a6469e493c0a3625df412f6e3925cb
new file mode 100644
index 0000000..d8108af
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/e92fb449b2a6469e493c0a3625df412f6e3925cb
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/e9596e7b64799448e81531acf38e2078f318b044 b/fuzz/corpus/packet_recv_client/e9596e7b64799448e81531acf38e2078f318b044
deleted file mode 100644
index c486819..0000000
--- a/fuzz/corpus/packet_recv_client/e9596e7b64799448e81531acf38e2078f318b044
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/e983ff1e34e76c57d58bb33bde4a6d8ee8ff5dc8 b/fuzz/corpus/packet_recv_client/e983ff1e34e76c57d58bb33bde4a6d8ee8ff5dc8
new file mode 100644
index 0000000..735c199
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/e983ff1e34e76c57d58bb33bde4a6d8ee8ff5dc8
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/e9c13d4f73bd98a4be075649b5c5550b49189542 b/fuzz/corpus/packet_recv_client/e9c13d4f73bd98a4be075649b5c5550b49189542
new file mode 100644
index 0000000..33dcb25
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/e9c13d4f73bd98a4be075649b5c5550b49189542
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/eabd6501819738c004cb53727a27ec9de76b99e4 b/fuzz/corpus/packet_recv_client/eabd6501819738c004cb53727a27ec9de76b99e4
new file mode 100644
index 0000000..c098928
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/eabd6501819738c004cb53727a27ec9de76b99e4
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/eb9f91792070ffc65990a522439d7854999aaae2 b/fuzz/corpus/packet_recv_client/eb9f91792070ffc65990a522439d7854999aaae2
new file mode 100644
index 0000000..0b881c7
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/eb9f91792070ffc65990a522439d7854999aaae2
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ebd7ce6920930317642b50640cf35d57fe4b836d b/fuzz/corpus/packet_recv_client/ebd7ce6920930317642b50640cf35d57fe4b836d
new file mode 100644
index 0000000..3803cd8
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/ebd7ce6920930317642b50640cf35d57fe4b836d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ec767f40ac4e414bbe79f319457909e7197b8529 b/fuzz/corpus/packet_recv_client/ec767f40ac4e414bbe79f319457909e7197b8529
new file mode 100644
index 0000000..44da729
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/ec767f40ac4e414bbe79f319457909e7197b8529
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ecbd4815b359c919acc6b5e431a2350979b69a92 b/fuzz/corpus/packet_recv_client/ecbd4815b359c919acc6b5e431a2350979b69a92
new file mode 100644
index 0000000..c445bfd
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/ecbd4815b359c919acc6b5e431a2350979b69a92
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ecc9928ced516610601d3474c4191fdf74d0df13 b/fuzz/corpus/packet_recv_client/ecc9928ced516610601d3474c4191fdf74d0df13
new file mode 100644
index 0000000..7d1c3a8
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/ecc9928ced516610601d3474c4191fdf74d0df13
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ecf6d4413fa6446c4da4ce1d2400166a7d2684c3 b/fuzz/corpus/packet_recv_client/ecf6d4413fa6446c4da4ce1d2400166a7d2684c3
deleted file mode 100644
index bf6ba72..0000000
--- a/fuzz/corpus/packet_recv_client/ecf6d4413fa6446c4da4ce1d2400166a7d2684c3
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ed781528311a51c6d20f5cbbcf2a195903d37917 b/fuzz/corpus/packet_recv_client/ed781528311a51c6d20f5cbbcf2a195903d37917
new file mode 100644
index 0000000..b742a46
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/ed781528311a51c6d20f5cbbcf2a195903d37917
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/edc3c986d42ca7f6df19efc4f64814302f900b76 b/fuzz/corpus/packet_recv_client/edc3c986d42ca7f6df19efc4f64814302f900b76
deleted file mode 100644
index f437b36..0000000
--- a/fuzz/corpus/packet_recv_client/edc3c986d42ca7f6df19efc4f64814302f900b76
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ee85ed146173a9d09aac6584c9320545333cc214 b/fuzz/corpus/packet_recv_client/ee85ed146173a9d09aac6584c9320545333cc214
new file mode 100644
index 0000000..c1f028c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/ee85ed146173a9d09aac6584c9320545333cc214
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ee8fe120d1cb039e89277b1377775b532edd6241 b/fuzz/corpus/packet_recv_client/ee8fe120d1cb039e89277b1377775b532edd6241
new file mode 100644
index 0000000..9014379
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/ee8fe120d1cb039e89277b1377775b532edd6241
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/eeaa8c429c4c8a75d2dcdccd3f6007097108b81a b/fuzz/corpus/packet_recv_client/eeaa8c429c4c8a75d2dcdccd3f6007097108b81a
new file mode 100644
index 0000000..954f926
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/eeaa8c429c4c8a75d2dcdccd3f6007097108b81a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f0168ca87c8a5d6b89028a51dd77800525a76bec b/fuzz/corpus/packet_recv_client/f0168ca87c8a5d6b89028a51dd77800525a76bec
new file mode 100644
index 0000000..e12be4e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/f0168ca87c8a5d6b89028a51dd77800525a76bec
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f04802d2b93328b00bc62eda167378b034d59a7a b/fuzz/corpus/packet_recv_client/f04802d2b93328b00bc62eda167378b034d59a7a
deleted file mode 100644
index f315ae8..0000000
--- a/fuzz/corpus/packet_recv_client/f04802d2b93328b00bc62eda167378b034d59a7a
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f0b7f331d1a54f58b2afdd6e8f2b5f568a7c59db b/fuzz/corpus/packet_recv_client/f0b7f331d1a54f58b2afdd6e8f2b5f568a7c59db
new file mode 100644
index 0000000..d040f5a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/f0b7f331d1a54f58b2afdd6e8f2b5f568a7c59db
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f0efdc46d450194d96ba94e926fd98cbb16de592 b/fuzz/corpus/packet_recv_client/f0efdc46d450194d96ba94e926fd98cbb16de592
new file mode 100644
index 0000000..ca82602
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/f0efdc46d450194d96ba94e926fd98cbb16de592
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f18975cf7f08a4979248e6dfc8e985a3e606fa78 b/fuzz/corpus/packet_recv_client/f18975cf7f08a4979248e6dfc8e985a3e606fa78
deleted file mode 100644
index 9a5a4cc..0000000
--- a/fuzz/corpus/packet_recv_client/f18975cf7f08a4979248e6dfc8e985a3e606fa78
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f1aa0cc7029a930bdb70a9d8977db850fa4d1cb5 b/fuzz/corpus/packet_recv_client/f1aa0cc7029a930bdb70a9d8977db850fa4d1cb5
deleted file mode 100644
index eeccc12..0000000
--- a/fuzz/corpus/packet_recv_client/f1aa0cc7029a930bdb70a9d8977db850fa4d1cb5
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f22b9dc85769dc4c988ce3b057480dcc7f0c0bad b/fuzz/corpus/packet_recv_client/f22b9dc85769dc4c988ce3b057480dcc7f0c0bad
new file mode 100644
index 0000000..c32b01a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/f22b9dc85769dc4c988ce3b057480dcc7f0c0bad
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f31f3e763ee09a9ecaff6acdd2d63f9124972fba b/fuzz/corpus/packet_recv_client/f31f3e763ee09a9ecaff6acdd2d63f9124972fba
new file mode 100644
index 0000000..7fed9dd
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/f31f3e763ee09a9ecaff6acdd2d63f9124972fba
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f32c16fb68c80b01fe5cf8d32c8f5f579fd9f75b b/fuzz/corpus/packet_recv_client/f32c16fb68c80b01fe5cf8d32c8f5f579fd9f75b
new file mode 100644
index 0000000..411931a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/f32c16fb68c80b01fe5cf8d32c8f5f579fd9f75b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f39d31b31c7b3b3735faef77b19e3f95afa9ed1b b/fuzz/corpus/packet_recv_client/f39d31b31c7b3b3735faef77b19e3f95afa9ed1b
new file mode 100644
index 0000000..87bc21c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/f39d31b31c7b3b3735faef77b19e3f95afa9ed1b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f5336cf0535bc0dfa07dc0129a4cd4f2cd11496f b/fuzz/corpus/packet_recv_client/f5336cf0535bc0dfa07dc0129a4cd4f2cd11496f
new file mode 100644
index 0000000..a74ef13
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/f5336cf0535bc0dfa07dc0129a4cd4f2cd11496f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f542364ba06fda31160c385d6ca8c71d55fb5637 b/fuzz/corpus/packet_recv_client/f542364ba06fda31160c385d6ca8c71d55fb5637
new file mode 100644
index 0000000..2c457f2
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/f542364ba06fda31160c385d6ca8c71d55fb5637
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f56f7fec6dd8ed065570daf869ee8c471cc5a9bb b/fuzz/corpus/packet_recv_client/f56f7fec6dd8ed065570daf869ee8c471cc5a9bb
new file mode 100644
index 0000000..9c0c03e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/f56f7fec6dd8ed065570daf869ee8c471cc5a9bb
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f5ba9a18e2854656031df9114b0b56daf9102560 b/fuzz/corpus/packet_recv_client/f5ba9a18e2854656031df9114b0b56daf9102560
new file mode 100644
index 0000000..7997cdc
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/f5ba9a18e2854656031df9114b0b56daf9102560
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f695301a8fce82285299c907092dcbafc2218da0 b/fuzz/corpus/packet_recv_client/f695301a8fce82285299c907092dcbafc2218da0
new file mode 100644
index 0000000..6dea856
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/f695301a8fce82285299c907092dcbafc2218da0
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f6ca834d5ac1e3b6985b7b8f0177ea2512c0f932 b/fuzz/corpus/packet_recv_client/f6ca834d5ac1e3b6985b7b8f0177ea2512c0f932
new file mode 100644
index 0000000..7c8861c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/f6ca834d5ac1e3b6985b7b8f0177ea2512c0f932
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f72713da6a1e8300133621a587f77546a050f187 b/fuzz/corpus/packet_recv_client/f72713da6a1e8300133621a587f77546a050f187
new file mode 100644
index 0000000..248bf0a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/f72713da6a1e8300133621a587f77546a050f187
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f7dee35435dc8f5f9eef96c6dc0ddfbd941bb271 b/fuzz/corpus/packet_recv_client/f7dee35435dc8f5f9eef96c6dc0ddfbd941bb271
new file mode 100644
index 0000000..92114ee
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/f7dee35435dc8f5f9eef96c6dc0ddfbd941bb271
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f832e64f0d8f588bae2ebaa4b34db9096c56eea4 b/fuzz/corpus/packet_recv_client/f832e64f0d8f588bae2ebaa4b34db9096c56eea4
new file mode 100644
index 0000000..2c085ba
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/f832e64f0d8f588bae2ebaa4b34db9096c56eea4
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f8ab0e28e0a520851e5b59a747f4babab8566f27 b/fuzz/corpus/packet_recv_client/f8ab0e28e0a520851e5b59a747f4babab8566f27
new file mode 100644
index 0000000..27e312c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/f8ab0e28e0a520851e5b59a747f4babab8566f27
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f91a99b78a5dc2823d7c35177243a3070082cbe3 b/fuzz/corpus/packet_recv_client/f91a99b78a5dc2823d7c35177243a3070082cbe3
deleted file mode 100644
index 73d8e85..0000000
--- a/fuzz/corpus/packet_recv_client/f91a99b78a5dc2823d7c35177243a3070082cbe3
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f9458ba5fa65691a8b704a92cf8deb676b99c863 b/fuzz/corpus/packet_recv_client/f9458ba5fa65691a8b704a92cf8deb676b99c863
new file mode 100644
index 0000000..b2e54ca
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/f9458ba5fa65691a8b704a92cf8deb676b99c863
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f983490954e7822d263230a767771e78a7b0df44 b/fuzz/corpus/packet_recv_client/f983490954e7822d263230a767771e78a7b0df44
new file mode 100644
index 0000000..e667743
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/f983490954e7822d263230a767771e78a7b0df44
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f9b23caee1dc6bc62c7c2ed37d035a11ec3122e4 b/fuzz/corpus/packet_recv_client/f9b23caee1dc6bc62c7c2ed37d035a11ec3122e4
new file mode 100644
index 0000000..bcf1681
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/f9b23caee1dc6bc62c7c2ed37d035a11ec3122e4
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f9c193a3e7fd71b97ef7585241f23d3bfb07871d b/fuzz/corpus/packet_recv_client/f9c193a3e7fd71b97ef7585241f23d3bfb07871d
new file mode 100644
index 0000000..084817e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/f9c193a3e7fd71b97ef7585241f23d3bfb07871d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/f9f05ded1712e4fc8443a10e3b7770c36fd611e4 b/fuzz/corpus/packet_recv_client/f9f05ded1712e4fc8443a10e3b7770c36fd611e4
new file mode 100644
index 0000000..8c7e779
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/f9f05ded1712e4fc8443a10e3b7770c36fd611e4
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/fa1ffe86b92730081aa8aad3e984ea347446c848 b/fuzz/corpus/packet_recv_client/fa1ffe86b92730081aa8aad3e984ea347446c848
new file mode 100644
index 0000000..d6c35fe
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/fa1ffe86b92730081aa8aad3e984ea347446c848
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/fa6770fb324b4ac10a7a44640bc04dd39b665fb3 b/fuzz/corpus/packet_recv_client/fa6770fb324b4ac10a7a44640bc04dd39b665fb3
new file mode 100644
index 0000000..ccb86b6
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/fa6770fb324b4ac10a7a44640bc04dd39b665fb3
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/fa99e1093626318770820c33bf0b8908737503eb b/fuzz/corpus/packet_recv_client/fa99e1093626318770820c33bf0b8908737503eb
new file mode 100644
index 0000000..417c8aa
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/fa99e1093626318770820c33bf0b8908737503eb
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/fb05736e543421239574767eb6e905c065d31490 b/fuzz/corpus/packet_recv_client/fb05736e543421239574767eb6e905c065d31490
new file mode 100644
index 0000000..4547dcc
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/fb05736e543421239574767eb6e905c065d31490
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/fb418c31091ff2b92ad23456330203facdb307f6 b/fuzz/corpus/packet_recv_client/fb418c31091ff2b92ad23456330203facdb307f6
new file mode 100644
index 0000000..fe29db7
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/fb418c31091ff2b92ad23456330203facdb307f6
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/fb5a657ad07120d7ca2d1c67a5c23b01c4af4709 b/fuzz/corpus/packet_recv_client/fb5a657ad07120d7ca2d1c67a5c23b01c4af4709
new file mode 100644
index 0000000..a66c9ab
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/fb5a657ad07120d7ca2d1c67a5c23b01c4af4709
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/fc0b5a9bbcb75416da8443661aa6bef49ae175e2 b/fuzz/corpus/packet_recv_client/fc0b5a9bbcb75416da8443661aa6bef49ae175e2
new file mode 100644
index 0000000..4409a59
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/fc0b5a9bbcb75416da8443661aa6bef49ae175e2
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/fc377c02504cf833f07e03e0d2ed7c3cf2c6ce8a b/fuzz/corpus/packet_recv_client/fc377c02504cf833f07e03e0d2ed7c3cf2c6ce8a
new file mode 100644
index 0000000..a6843fd
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/fc377c02504cf833f07e03e0d2ed7c3cf2c6ce8a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/fc9d55c1225386cd62feccf437c18fccfe108f4c b/fuzz/corpus/packet_recv_client/fc9d55c1225386cd62feccf437c18fccfe108f4c
new file mode 100644
index 0000000..fb05e88
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/fc9d55c1225386cd62feccf437c18fccfe108f4c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/fce7dbf8e8b7f805321f81b030249fee34f80239 b/fuzz/corpus/packet_recv_client/fce7dbf8e8b7f805321f81b030249fee34f80239
new file mode 100644
index 0000000..0818fc3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/fce7dbf8e8b7f805321f81b030249fee34f80239
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/fd3bb954f432a5eefade7811238ffdc2ec115611 b/fuzz/corpus/packet_recv_client/fd3bb954f432a5eefade7811238ffdc2ec115611
new file mode 100644
index 0000000..f13934b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/fd3bb954f432a5eefade7811238ffdc2ec115611
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/fd4eb543aea393fa9872c63a5bba5e94e052e39b b/fuzz/corpus/packet_recv_client/fd4eb543aea393fa9872c63a5bba5e94e052e39b
new file mode 100644
index 0000000..b3a9a1d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/fd4eb543aea393fa9872c63a5bba5e94e052e39b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/fd54567dce80d6a21fc95ff0e3602fad8267b73c b/fuzz/corpus/packet_recv_client/fd54567dce80d6a21fc95ff0e3602fad8267b73c
new file mode 100644
index 0000000..e31ee8d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/fd54567dce80d6a21fc95ff0e3602fad8267b73c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/fd9fb08c8656cda728d647d5ea3ca06999521725 b/fuzz/corpus/packet_recv_client/fd9fb08c8656cda728d647d5ea3ca06999521725
new file mode 100644
index 0000000..4a6f52f
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/fd9fb08c8656cda728d647d5ea3ca06999521725
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/fe063b180ddace4fde9f2bedd9bf6bd80d4ae42a b/fuzz/corpus/packet_recv_client/fe063b180ddace4fde9f2bedd9bf6bd80d4ae42a
new file mode 100644
index 0000000..19705ea
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/fe063b180ddace4fde9f2bedd9bf6bd80d4ae42a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/fe2874f9087bab8faa34fa3818d9657935f694b7 b/fuzz/corpus/packet_recv_client/fe2874f9087bab8faa34fa3818d9657935f694b7
new file mode 100644
index 0000000..d7cf813
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/fe2874f9087bab8faa34fa3818d9657935f694b7
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ff37e2b7677b1cffb1657fe81dde95812c54686c b/fuzz/corpus/packet_recv_client/ff37e2b7677b1cffb1657fe81dde95812c54686c
new file mode 100644
index 0000000..5826d08
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/ff37e2b7677b1cffb1657fe81dde95812c54686c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ff38d421772c88784a9ca113e446bfc69287b6a1 b/fuzz/corpus/packet_recv_client/ff38d421772c88784a9ca113e446bfc69287b6a1
deleted file mode 100644
index 48b83c8..0000000
--- a/fuzz/corpus/packet_recv_client/ff38d421772c88784a9ca113e446bfc69287b6a1
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/ffb2466045d355847f75cfb0d3ce7e6647eaff57 b/fuzz/corpus/packet_recv_client/ffb2466045d355847f75cfb0d3ce7e6647eaff57
deleted file mode 100644
index 989d699..0000000
--- a/fuzz/corpus/packet_recv_client/ffb2466045d355847f75cfb0d3ce7e6647eaff57
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_client/fffc784aeb57733989dbc010a55e06ee73d2597d b/fuzz/corpus/packet_recv_client/fffc784aeb57733989dbc010a55e06ee73d2597d
new file mode 100644
index 0000000..ad951e0
--- /dev/null
+++ b/fuzz/corpus/packet_recv_client/fffc784aeb57733989dbc010a55e06ee73d2597d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/006f733fdd4beeb77737153d604aa39ee3ed7e01 b/fuzz/corpus/packet_recv_server/006f733fdd4beeb77737153d604aa39ee3ed7e01
new file mode 100644
index 0000000..1ff1150
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/006f733fdd4beeb77737153d604aa39ee3ed7e01
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/02005da57df0cf7588b86059408cb8f31432d9af b/fuzz/corpus/packet_recv_server/02005da57df0cf7588b86059408cb8f31432d9af
new file mode 100644
index 0000000..3c5dee7
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/02005da57df0cf7588b86059408cb8f31432d9af
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/02772c9108a2600a74c7778dfd61d7804b438917 b/fuzz/corpus/packet_recv_server/02772c9108a2600a74c7778dfd61d7804b438917
new file mode 100644
index 0000000..488e28b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/02772c9108a2600a74c7778dfd61d7804b438917
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/03fee22f9ad121e98e02b5d43f690801fc6f683e b/fuzz/corpus/packet_recv_server/03fee22f9ad121e98e02b5d43f690801fc6f683e
new file mode 100644
index 0000000..847912a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/03fee22f9ad121e98e02b5d43f690801fc6f683e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/04e8fc5137711019865e01bbc29cc511e95d32f3 b/fuzz/corpus/packet_recv_server/04e8fc5137711019865e01bbc29cc511e95d32f3
deleted file mode 100644
index 759b2be..0000000
--- a/fuzz/corpus/packet_recv_server/04e8fc5137711019865e01bbc29cc511e95d32f3
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/04ff12f7b202f019757225a2f0f07dfbe8a1e3f4 b/fuzz/corpus/packet_recv_server/04ff12f7b202f019757225a2f0f07dfbe8a1e3f4
deleted file mode 100644
index c630a58..0000000
--- a/fuzz/corpus/packet_recv_server/04ff12f7b202f019757225a2f0f07dfbe8a1e3f4
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/057210de102f9247bd092eda4029768c8c71ed4a b/fuzz/corpus/packet_recv_server/057210de102f9247bd092eda4029768c8c71ed4a
deleted file mode 100644
index 65e23c9..0000000
--- a/fuzz/corpus/packet_recv_server/057210de102f9247bd092eda4029768c8c71ed4a
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/0615eb2181d5c0af9406f5629fe23a76c5ada17a b/fuzz/corpus/packet_recv_server/0615eb2181d5c0af9406f5629fe23a76c5ada17a
deleted file mode 100644
index f9655ac..0000000
--- a/fuzz/corpus/packet_recv_server/0615eb2181d5c0af9406f5629fe23a76c5ada17a
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/06639cb89f27d9da25cfc2e1ac66664af6176212 b/fuzz/corpus/packet_recv_server/06639cb89f27d9da25cfc2e1ac66664af6176212
new file mode 100644
index 0000000..73394fb
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/06639cb89f27d9da25cfc2e1ac66664af6176212
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/06c68f039283b90598d91bfee557e4aee39cfb1d b/fuzz/corpus/packet_recv_server/06c68f039283b90598d91bfee557e4aee39cfb1d
new file mode 100644
index 0000000..45d9ba1
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/06c68f039283b90598d91bfee557e4aee39cfb1d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/06db51daff604fea4da23171737de42e59891aac b/fuzz/corpus/packet_recv_server/06db51daff604fea4da23171737de42e59891aac
new file mode 100644
index 0000000..440c6bf
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/06db51daff604fea4da23171737de42e59891aac
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/072f500f925288d1423b079267e63161cb681894 b/fuzz/corpus/packet_recv_server/072f500f925288d1423b079267e63161cb681894
deleted file mode 100644
index 06f7843..0000000
--- a/fuzz/corpus/packet_recv_server/072f500f925288d1423b079267e63161cb681894
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/07e51a131fb2c622cf6f458bc7aee9df328f8e12 b/fuzz/corpus/packet_recv_server/07e51a131fb2c622cf6f458bc7aee9df328f8e12
deleted file mode 100644
index 105bb55..0000000
--- a/fuzz/corpus/packet_recv_server/07e51a131fb2c622cf6f458bc7aee9df328f8e12
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/0804c843b634ae7703c1cfa16219b20eb20abb4c b/fuzz/corpus/packet_recv_server/0804c843b634ae7703c1cfa16219b20eb20abb4c
new file mode 100644
index 0000000..c8d911c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/0804c843b634ae7703c1cfa16219b20eb20abb4c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/082d0902f2e56a89b56b22665c6dd83ff1b264a5 b/fuzz/corpus/packet_recv_server/082d0902f2e56a89b56b22665c6dd83ff1b264a5
new file mode 100644
index 0000000..c613957
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/082d0902f2e56a89b56b22665c6dd83ff1b264a5
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/08ac11a43cb77d29119f6d0b8332f2ae7a16200a b/fuzz/corpus/packet_recv_server/08ac11a43cb77d29119f6d0b8332f2ae7a16200a
new file mode 100644
index 0000000..2e7c359
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/08ac11a43cb77d29119f6d0b8332f2ae7a16200a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/08cb3911bbb7b54cc56b01e87c40400e3fed9372 b/fuzz/corpus/packet_recv_server/08cb3911bbb7b54cc56b01e87c40400e3fed9372
new file mode 100644
index 0000000..89ee9f7
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/08cb3911bbb7b54cc56b01e87c40400e3fed9372
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/0ae32550eb145b6bdac1fffdccd10e4294d1b547 b/fuzz/corpus/packet_recv_server/0ae32550eb145b6bdac1fffdccd10e4294d1b547
new file mode 100644
index 0000000..16753be
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/0ae32550eb145b6bdac1fffdccd10e4294d1b547
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/0b758984ecac2ce6410aa84264153f0b89319d05 b/fuzz/corpus/packet_recv_server/0b758984ecac2ce6410aa84264153f0b89319d05
deleted file mode 100644
index 64db443..0000000
--- a/fuzz/corpus/packet_recv_server/0b758984ecac2ce6410aa84264153f0b89319d05
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/0b8df50b441c6c78cc785695760c2b3029cb901f b/fuzz/corpus/packet_recv_server/0b8df50b441c6c78cc785695760c2b3029cb901f
new file mode 100644
index 0000000..50f07f8
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/0b8df50b441c6c78cc785695760c2b3029cb901f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/0b9c9dbf3d2f49e3f35be94513e1feed667b96bb b/fuzz/corpus/packet_recv_server/0b9c9dbf3d2f49e3f35be94513e1feed667b96bb
new file mode 100644
index 0000000..7a566cb
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/0b9c9dbf3d2f49e3f35be94513e1feed667b96bb
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/0bcbb35bda4d792d9c514e9b20cc8766b0e70652 b/fuzz/corpus/packet_recv_server/0bcbb35bda4d792d9c514e9b20cc8766b0e70652
new file mode 100644
index 0000000..0ef0494
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/0bcbb35bda4d792d9c514e9b20cc8766b0e70652
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/0c1a614dbd7370168a721594db6377403e8c31b8 b/fuzz/corpus/packet_recv_server/0c1a614dbd7370168a721594db6377403e8c31b8
new file mode 100644
index 0000000..634fd64
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/0c1a614dbd7370168a721594db6377403e8c31b8
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/0c1ac8bba16e0e5cbc1751762b31fde300cabe95 b/fuzz/corpus/packet_recv_server/0c1ac8bba16e0e5cbc1751762b31fde300cabe95
new file mode 100644
index 0000000..bb4c562
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/0c1ac8bba16e0e5cbc1751762b31fde300cabe95
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/0d5acee8dc10da22cb0d00fc873af4e6dfad177a b/fuzz/corpus/packet_recv_server/0d5acee8dc10da22cb0d00fc873af4e6dfad177a
deleted file mode 100644
index dad63c6..0000000
--- a/fuzz/corpus/packet_recv_server/0d5acee8dc10da22cb0d00fc873af4e6dfad177a
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/0daf10b1dbec5c5d823378db7a2f2bdbf5fd1f88 b/fuzz/corpus/packet_recv_server/0daf10b1dbec5c5d823378db7a2f2bdbf5fd1f88
new file mode 100644
index 0000000..2dda258
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/0daf10b1dbec5c5d823378db7a2f2bdbf5fd1f88
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/0dc0d0d8eee71027e5918b25d1666740c40a2626 b/fuzz/corpus/packet_recv_server/0dc0d0d8eee71027e5918b25d1666740c40a2626
new file mode 100644
index 0000000..3e1fd31
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/0dc0d0d8eee71027e5918b25d1666740c40a2626
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/0e2bbcd5a9895bcadd24c621320dbcd7b60207de b/fuzz/corpus/packet_recv_server/0e2bbcd5a9895bcadd24c621320dbcd7b60207de
new file mode 100644
index 0000000..a056afb
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/0e2bbcd5a9895bcadd24c621320dbcd7b60207de
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/0e5ce5721285d603ab00245eb795b1564f5c6d50 b/fuzz/corpus/packet_recv_server/0e5ce5721285d603ab00245eb795b1564f5c6d50
new file mode 100644
index 0000000..a8e9938
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/0e5ce5721285d603ab00245eb795b1564f5c6d50
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/0f463d038b3944222ac1eb017267c21bf6b9d9dd b/fuzz/corpus/packet_recv_server/0f463d038b3944222ac1eb017267c21bf6b9d9dd
new file mode 100644
index 0000000..c13160d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/0f463d038b3944222ac1eb017267c21bf6b9d9dd
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/1055f709442e8190da77676531a888f9e5e66797 b/fuzz/corpus/packet_recv_server/1055f709442e8190da77676531a888f9e5e66797
new file mode 100644
index 0000000..a9ac0c3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/1055f709442e8190da77676531a888f9e5e66797
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/1261c02479cd5e2c121c61177e46e946e26ce6d2 b/fuzz/corpus/packet_recv_server/1261c02479cd5e2c121c61177e46e946e26ce6d2
new file mode 100644
index 0000000..48bb355
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/1261c02479cd5e2c121c61177e46e946e26ce6d2
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/12dfe4e2371f5f2b16c33f35ca573a935cce1e87 b/fuzz/corpus/packet_recv_server/12dfe4e2371f5f2b16c33f35ca573a935cce1e87
new file mode 100644
index 0000000..dadec21
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/12dfe4e2371f5f2b16c33f35ca573a935cce1e87
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/13979cdb6ab6d75b64974f742f242602e2ad672e b/fuzz/corpus/packet_recv_server/13979cdb6ab6d75b64974f742f242602e2ad672e
new file mode 100644
index 0000000..bd46d3c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/13979cdb6ab6d75b64974f742f242602e2ad672e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/13eac251776cc9f2ab42358832f11904c1329d3c b/fuzz/corpus/packet_recv_server/13eac251776cc9f2ab42358832f11904c1329d3c
new file mode 100644
index 0000000..721894a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/13eac251776cc9f2ab42358832f11904c1329d3c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/14f0e4ad7a542a254548e8db1d58eceb8e2cd191 b/fuzz/corpus/packet_recv_server/14f0e4ad7a542a254548e8db1d58eceb8e2cd191
new file mode 100644
index 0000000..b96f9f8
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/14f0e4ad7a542a254548e8db1d58eceb8e2cd191
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/1563c2b62d55f4d19f7fec338070b6a1023bc9b4 b/fuzz/corpus/packet_recv_server/1563c2b62d55f4d19f7fec338070b6a1023bc9b4
deleted file mode 100644
index db49338..0000000
--- a/fuzz/corpus/packet_recv_server/1563c2b62d55f4d19f7fec338070b6a1023bc9b4
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/159b7a3831c60e9396de3dcd3e220816485f1aec b/fuzz/corpus/packet_recv_server/159b7a3831c60e9396de3dcd3e220816485f1aec
deleted file mode 100644
index 80cfc54..0000000
--- a/fuzz/corpus/packet_recv_server/159b7a3831c60e9396de3dcd3e220816485f1aec
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/15c7a57add110f4a98be440024a16766ba34b5fe b/fuzz/corpus/packet_recv_server/15c7a57add110f4a98be440024a16766ba34b5fe
deleted file mode 100644
index d6b0a8c..0000000
--- a/fuzz/corpus/packet_recv_server/15c7a57add110f4a98be440024a16766ba34b5fe
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/15e46c7efc031ed5c6fceccd12fde67ac9633fe2 b/fuzz/corpus/packet_recv_server/15e46c7efc031ed5c6fceccd12fde67ac9633fe2
new file mode 100644
index 0000000..ab2ced5
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/15e46c7efc031ed5c6fceccd12fde67ac9633fe2
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/16eedfacb9f6d5288c06143f8e6c54c0066ac85f b/fuzz/corpus/packet_recv_server/16eedfacb9f6d5288c06143f8e6c54c0066ac85f
deleted file mode 100644
index f8d4784..0000000
--- a/fuzz/corpus/packet_recv_server/16eedfacb9f6d5288c06143f8e6c54c0066ac85f
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/1750514038c4e2f26d3e41622e573932d99d15a6 b/fuzz/corpus/packet_recv_server/1750514038c4e2f26d3e41622e573932d99d15a6
new file mode 100644
index 0000000..31ce950
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/1750514038c4e2f26d3e41622e573932d99d15a6
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/17839c6c74e2e311050b97858fd41ae734104098 b/fuzz/corpus/packet_recv_server/17839c6c74e2e311050b97858fd41ae734104098
new file mode 100644
index 0000000..1ec0fa0
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/17839c6c74e2e311050b97858fd41ae734104098
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/17dee5b19cc54271d38c9354b220b924a67fd2d2 b/fuzz/corpus/packet_recv_server/17dee5b19cc54271d38c9354b220b924a67fd2d2
deleted file mode 100644
index 41589cf..0000000
--- a/fuzz/corpus/packet_recv_server/17dee5b19cc54271d38c9354b220b924a67fd2d2
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/1bc0d30286445054c7c82a833758b6387e8653a9 b/fuzz/corpus/packet_recv_server/1bc0d30286445054c7c82a833758b6387e8653a9
new file mode 100644
index 0000000..4bd38a6
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/1bc0d30286445054c7c82a833758b6387e8653a9
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/1c1d79eb028cff78f658a4f6b74111899d8c4fa2 b/fuzz/corpus/packet_recv_server/1c1d79eb028cff78f658a4f6b74111899d8c4fa2
new file mode 100644
index 0000000..6b9c5c1
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/1c1d79eb028cff78f658a4f6b74111899d8c4fa2
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/1cf979121f60c805cde979b1a90f126c8e3ef64e b/fuzz/corpus/packet_recv_server/1cf979121f60c805cde979b1a90f126c8e3ef64e
deleted file mode 100644
index 0ebd577..0000000
--- a/fuzz/corpus/packet_recv_server/1cf979121f60c805cde979b1a90f126c8e3ef64e
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/1cfdf21a5d6453ca91c42e9eecda7ea68e891bcd b/fuzz/corpus/packet_recv_server/1cfdf21a5d6453ca91c42e9eecda7ea68e891bcd
deleted file mode 100644
index 0b4d5da..0000000
--- a/fuzz/corpus/packet_recv_server/1cfdf21a5d6453ca91c42e9eecda7ea68e891bcd
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/1dc17b1222f2ea2b8054054cf4ab9492b5f358b0 b/fuzz/corpus/packet_recv_server/1dc17b1222f2ea2b8054054cf4ab9492b5f358b0
new file mode 100644
index 0000000..76bbbf3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/1dc17b1222f2ea2b8054054cf4ab9492b5f358b0
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/1e0071c3cd56a0750ac04b83dd2392d59e02c5cf b/fuzz/corpus/packet_recv_server/1e0071c3cd56a0750ac04b83dd2392d59e02c5cf
deleted file mode 100644
index 1faa18c..0000000
--- a/fuzz/corpus/packet_recv_server/1e0071c3cd56a0750ac04b83dd2392d59e02c5cf
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/1e402049afe63d00eb720a4ac2e905fa8a477cf5 b/fuzz/corpus/packet_recv_server/1e402049afe63d00eb720a4ac2e905fa8a477cf5
new file mode 100644
index 0000000..1b5be40
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/1e402049afe63d00eb720a4ac2e905fa8a477cf5
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/1fb50f19512f7d18302ad0ddd7d65162d7a25e64 b/fuzz/corpus/packet_recv_server/1fb50f19512f7d18302ad0ddd7d65162d7a25e64
new file mode 100644
index 0000000..37108f9
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/1fb50f19512f7d18302ad0ddd7d65162d7a25e64
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/1fb8b35a27ec38bc6a11af0a83186c0899d83683 b/fuzz/corpus/packet_recv_server/1fb8b35a27ec38bc6a11af0a83186c0899d83683
deleted file mode 100644
index d458ef5..0000000
--- a/fuzz/corpus/packet_recv_server/1fb8b35a27ec38bc6a11af0a83186c0899d83683
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/1fcc4d7915dd7bad468b4867be74460e8a1fe076 b/fuzz/corpus/packet_recv_server/1fcc4d7915dd7bad468b4867be74460e8a1fe076
deleted file mode 100644
index 2d2f3bd..0000000
--- a/fuzz/corpus/packet_recv_server/1fcc4d7915dd7bad468b4867be74460e8a1fe076
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/2033997b0a84c03f3bb4d8c5afb19d824a174664 b/fuzz/corpus/packet_recv_server/2033997b0a84c03f3bb4d8c5afb19d824a174664
new file mode 100644
index 0000000..b038b13
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/2033997b0a84c03f3bb4d8c5afb19d824a174664
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/20a9de2bad1cdfee855bf137715d6401dd05c6f2 b/fuzz/corpus/packet_recv_server/20a9de2bad1cdfee855bf137715d6401dd05c6f2
new file mode 100644
index 0000000..dae142a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/20a9de2bad1cdfee855bf137715d6401dd05c6f2
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/2180c55bdcef537ca41035b5d18b5de88398d77d b/fuzz/corpus/packet_recv_server/2180c55bdcef537ca41035b5d18b5de88398d77d
deleted file mode 100644
index 30f8231..0000000
--- a/fuzz/corpus/packet_recv_server/2180c55bdcef537ca41035b5d18b5de88398d77d
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/218f58e83a424c1f9c38e7ff3146f13ed79f63aa b/fuzz/corpus/packet_recv_server/218f58e83a424c1f9c38e7ff3146f13ed79f63aa
deleted file mode 100644
index 62d888f..0000000
--- a/fuzz/corpus/packet_recv_server/218f58e83a424c1f9c38e7ff3146f13ed79f63aa
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/2202628398b3051ecfdc19de3cdbc327f00526f6 b/fuzz/corpus/packet_recv_server/2202628398b3051ecfdc19de3cdbc327f00526f6
deleted file mode 100644
index 21a1100..0000000
--- a/fuzz/corpus/packet_recv_server/2202628398b3051ecfdc19de3cdbc327f00526f6
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/22a3f8827266e7f4acf795487fafdcd9dcf3e2a9 b/fuzz/corpus/packet_recv_server/22a3f8827266e7f4acf795487fafdcd9dcf3e2a9
deleted file mode 100644
index 16d06c9..0000000
--- a/fuzz/corpus/packet_recv_server/22a3f8827266e7f4acf795487fafdcd9dcf3e2a9
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/22c86be5ddc079d6cecc9a9ee763ea4eb3d8914a b/fuzz/corpus/packet_recv_server/22c86be5ddc079d6cecc9a9ee763ea4eb3d8914a
new file mode 100644
index 0000000..51e6347
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/22c86be5ddc079d6cecc9a9ee763ea4eb3d8914a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/235f2b4632f8b9d3dc005463f53e0db62b29ecff b/fuzz/corpus/packet_recv_server/235f2b4632f8b9d3dc005463f53e0db62b29ecff
deleted file mode 100644
index 02f620c..0000000
--- a/fuzz/corpus/packet_recv_server/235f2b4632f8b9d3dc005463f53e0db62b29ecff
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/23a338fa66194d59ce1326cefd65bdb4f00c5920 b/fuzz/corpus/packet_recv_server/23a338fa66194d59ce1326cefd65bdb4f00c5920
deleted file mode 100644
index af5ff6c..0000000
--- a/fuzz/corpus/packet_recv_server/23a338fa66194d59ce1326cefd65bdb4f00c5920
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/24b4c15c7be14a2ca73a41066bdfff88be3dec96 b/fuzz/corpus/packet_recv_server/24b4c15c7be14a2ca73a41066bdfff88be3dec96
deleted file mode 100644
index 8e53d35..0000000
--- a/fuzz/corpus/packet_recv_server/24b4c15c7be14a2ca73a41066bdfff88be3dec96
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/251c60cde32f5c5be1e630f19f1b8afec752741c b/fuzz/corpus/packet_recv_server/251c60cde32f5c5be1e630f19f1b8afec752741c
deleted file mode 100644
index ffbdbe6..0000000
--- a/fuzz/corpus/packet_recv_server/251c60cde32f5c5be1e630f19f1b8afec752741c
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/273ab1abd2226e1f71080413bf1d185f37024c6b b/fuzz/corpus/packet_recv_server/273ab1abd2226e1f71080413bf1d185f37024c6b
new file mode 100644
index 0000000..c02b636
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/273ab1abd2226e1f71080413bf1d185f37024c6b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/276e791ed39af784cb62402b85ebd04c2c6ddb74 b/fuzz/corpus/packet_recv_server/276e791ed39af784cb62402b85ebd04c2c6ddb74
deleted file mode 100644
index 004dec9..0000000
--- a/fuzz/corpus/packet_recv_server/276e791ed39af784cb62402b85ebd04c2c6ddb74
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/27ac973fb8f84b3130c2049e13c4f58d9802f35e b/fuzz/corpus/packet_recv_server/27ac973fb8f84b3130c2049e13c4f58d9802f35e
deleted file mode 100644
index 61cdcc6..0000000
--- a/fuzz/corpus/packet_recv_server/27ac973fb8f84b3130c2049e13c4f58d9802f35e
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/27d60415ea72b29b568ccac386d516c91facc553 b/fuzz/corpus/packet_recv_server/27d60415ea72b29b568ccac386d516c91facc553
new file mode 100644
index 0000000..209fe78
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/27d60415ea72b29b568ccac386d516c91facc553
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/2802e1a53c369b9365cd148029cfba37ad289a4f b/fuzz/corpus/packet_recv_server/2802e1a53c369b9365cd148029cfba37ad289a4f
new file mode 100644
index 0000000..7677545
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/2802e1a53c369b9365cd148029cfba37ad289a4f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/285e00988b2eda1ba6537e4782b69dc6f238ff7b b/fuzz/corpus/packet_recv_server/285e00988b2eda1ba6537e4782b69dc6f238ff7b
deleted file mode 100644
index 59abf67..0000000
--- a/fuzz/corpus/packet_recv_server/285e00988b2eda1ba6537e4782b69dc6f238ff7b
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/29c6ec9ed46d9d9d16660bc2da725c7ff6d910d0 b/fuzz/corpus/packet_recv_server/29c6ec9ed46d9d9d16660bc2da725c7ff6d910d0
deleted file mode 100644
index 4d08767..0000000
--- a/fuzz/corpus/packet_recv_server/29c6ec9ed46d9d9d16660bc2da725c7ff6d910d0
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/29e05b13743dd8c7ba45d3ed4378c1f0a5564884 b/fuzz/corpus/packet_recv_server/29e05b13743dd8c7ba45d3ed4378c1f0a5564884
new file mode 100644
index 0000000..b0b0295
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/29e05b13743dd8c7ba45d3ed4378c1f0a5564884
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/2a194dbb52a2427983fe01f82180b7bdb58c520d b/fuzz/corpus/packet_recv_server/2a194dbb52a2427983fe01f82180b7bdb58c520d
deleted file mode 100644
index 67fbb25..0000000
--- a/fuzz/corpus/packet_recv_server/2a194dbb52a2427983fe01f82180b7bdb58c520d
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/2a488173a1ad57b605d0f4b0909910d97dbd4dc2 b/fuzz/corpus/packet_recv_server/2a488173a1ad57b605d0f4b0909910d97dbd4dc2
new file mode 100644
index 0000000..81e673f
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/2a488173a1ad57b605d0f4b0909910d97dbd4dc2
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/2aa5a2dbf7cb9530269f825b76175a4bfe3e49f9 b/fuzz/corpus/packet_recv_server/2aa5a2dbf7cb9530269f825b76175a4bfe3e49f9
deleted file mode 100644
index 1a65e94..0000000
--- a/fuzz/corpus/packet_recv_server/2aa5a2dbf7cb9530269f825b76175a4bfe3e49f9
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/2b15ca79bc4397e15f1f1e86448cec04271631c4 b/fuzz/corpus/packet_recv_server/2b15ca79bc4397e15f1f1e86448cec04271631c4
new file mode 100644
index 0000000..3070c25
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/2b15ca79bc4397e15f1f1e86448cec04271631c4
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/2c87d45591e98dce53d0148d144ac1ffe5e8684e b/fuzz/corpus/packet_recv_server/2c87d45591e98dce53d0148d144ac1ffe5e8684e
new file mode 100644
index 0000000..3592a9d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/2c87d45591e98dce53d0148d144ac1ffe5e8684e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/2cccbb9c5500ad367dcb50f245a975d915e86c0c b/fuzz/corpus/packet_recv_server/2cccbb9c5500ad367dcb50f245a975d915e86c0c
deleted file mode 100644
index 93d559e..0000000
--- a/fuzz/corpus/packet_recv_server/2cccbb9c5500ad367dcb50f245a975d915e86c0c
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/2cffaf7a90a86b0aade651cc87a7e74bc6396282 b/fuzz/corpus/packet_recv_server/2cffaf7a90a86b0aade651cc87a7e74bc6396282
new file mode 100644
index 0000000..52516f6
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/2cffaf7a90a86b0aade651cc87a7e74bc6396282
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/2e8592dc8bdea279eddd81e1edaf520839e83dd7 b/fuzz/corpus/packet_recv_server/2e8592dc8bdea279eddd81e1edaf520839e83dd7
new file mode 100644
index 0000000..2d66c51
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/2e8592dc8bdea279eddd81e1edaf520839e83dd7
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/2f44c0579e1fe62dbb89ba44dc6b59a3760af84a b/fuzz/corpus/packet_recv_server/2f44c0579e1fe62dbb89ba44dc6b59a3760af84a
new file mode 100644
index 0000000..4c9292d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/2f44c0579e1fe62dbb89ba44dc6b59a3760af84a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/2f82a8ce161b9ec15308e19dcc9a3362e4c93a43 b/fuzz/corpus/packet_recv_server/2f82a8ce161b9ec15308e19dcc9a3362e4c93a43
new file mode 100644
index 0000000..f00f438
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/2f82a8ce161b9ec15308e19dcc9a3362e4c93a43
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/2fb98c88bd4eac0ec920c5a9988608cd5c2c81ca b/fuzz/corpus/packet_recv_server/2fb98c88bd4eac0ec920c5a9988608cd5c2c81ca
deleted file mode 100644
index 2cb3657..0000000
--- a/fuzz/corpus/packet_recv_server/2fb98c88bd4eac0ec920c5a9988608cd5c2c81ca
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/2fbd0b44fddea8e977a281c7d43573fcc0141967 b/fuzz/corpus/packet_recv_server/2fbd0b44fddea8e977a281c7d43573fcc0141967
new file mode 100644
index 0000000..61309c2
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/2fbd0b44fddea8e977a281c7d43573fcc0141967
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/3148473f4151375c89a451957f5e642aced41a02 b/fuzz/corpus/packet_recv_server/3148473f4151375c89a451957f5e642aced41a02
new file mode 100644
index 0000000..674634d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/3148473f4151375c89a451957f5e642aced41a02
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/320e47d3028ef7ad58316963b54a46089bbe00ea b/fuzz/corpus/packet_recv_server/320e47d3028ef7ad58316963b54a46089bbe00ea
deleted file mode 100644
index b404b17..0000000
--- a/fuzz/corpus/packet_recv_server/320e47d3028ef7ad58316963b54a46089bbe00ea
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/32efd1eed046720e7d9bbfe2242b8997f94073ed b/fuzz/corpus/packet_recv_server/32efd1eed046720e7d9bbfe2242b8997f94073ed
new file mode 100644
index 0000000..fa7be0c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/32efd1eed046720e7d9bbfe2242b8997f94073ed
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/3333e86c3636bb0231e547c80ac42bfba3f44a48 b/fuzz/corpus/packet_recv_server/3333e86c3636bb0231e547c80ac42bfba3f44a48
new file mode 100644
index 0000000..71a8a90
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/3333e86c3636bb0231e547c80ac42bfba3f44a48
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/357fa125a610635f526e1795d18f0be73c5e0dba b/fuzz/corpus/packet_recv_server/357fa125a610635f526e1795d18f0be73c5e0dba
new file mode 100644
index 0000000..971a086
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/357fa125a610635f526e1795d18f0be73c5e0dba
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/35ebca2f9ef21fc8eac8b3e528ececbf6bf4cb2b b/fuzz/corpus/packet_recv_server/35ebca2f9ef21fc8eac8b3e528ececbf6bf4cb2b
new file mode 100644
index 0000000..f3aa9a3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/35ebca2f9ef21fc8eac8b3e528ececbf6bf4cb2b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/361214e9f59e82f778f56f77fb0a9162cd293d88 b/fuzz/corpus/packet_recv_server/361214e9f59e82f778f56f77fb0a9162cd293d88
new file mode 100644
index 0000000..602c827
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/361214e9f59e82f778f56f77fb0a9162cd293d88
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/379af09cb4d04356f3a9cf6633e445f137b5cf3a b/fuzz/corpus/packet_recv_server/379af09cb4d04356f3a9cf6633e445f137b5cf3a
new file mode 100644
index 0000000..a158a47
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/379af09cb4d04356f3a9cf6633e445f137b5cf3a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/3966365577bef4abfa346c6fa6c0fa986f8dd60e b/fuzz/corpus/packet_recv_server/3966365577bef4abfa346c6fa6c0fa986f8dd60e
deleted file mode 100644
index a1d9cd8..0000000
--- a/fuzz/corpus/packet_recv_server/3966365577bef4abfa346c6fa6c0fa986f8dd60e
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/39ad88a64cbd8ab10f6343ca59ada33dd663dfae b/fuzz/corpus/packet_recv_server/39ad88a64cbd8ab10f6343ca59ada33dd663dfae
deleted file mode 100644
index 57f9fd7..0000000
--- a/fuzz/corpus/packet_recv_server/39ad88a64cbd8ab10f6343ca59ada33dd663dfae
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/3a624d585a790004e8c0cd5ec7db8bc7c1d267cb b/fuzz/corpus/packet_recv_server/3a624d585a790004e8c0cd5ec7db8bc7c1d267cb
new file mode 100644
index 0000000..929e271
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/3a624d585a790004e8c0cd5ec7db8bc7c1d267cb
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/3b7ea41191991d62e37a293f35cd60ced88726e9 b/fuzz/corpus/packet_recv_server/3b7ea41191991d62e37a293f35cd60ced88726e9
new file mode 100644
index 0000000..b5b7a5b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/3b7ea41191991d62e37a293f35cd60ced88726e9
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/3f60738d93e54a30dfa908fe423df39db22ff1a8 b/fuzz/corpus/packet_recv_server/3f60738d93e54a30dfa908fe423df39db22ff1a8
new file mode 100644
index 0000000..60a74d5
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/3f60738d93e54a30dfa908fe423df39db22ff1a8
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/3ff29d38069d2a61cfd465445f91d04758d61e88 b/fuzz/corpus/packet_recv_server/3ff29d38069d2a61cfd465445f91d04758d61e88
new file mode 100644
index 0000000..b5db846
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/3ff29d38069d2a61cfd465445f91d04758d61e88
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/407b0ccc1fc3972e19c3ea74279faa055aafa6cd b/fuzz/corpus/packet_recv_server/407b0ccc1fc3972e19c3ea74279faa055aafa6cd
deleted file mode 100644
index d965c68..0000000
--- a/fuzz/corpus/packet_recv_server/407b0ccc1fc3972e19c3ea74279faa055aafa6cd
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/41c33cb6c5ccffbf81c1826e7b5ef27b260d6a0f b/fuzz/corpus/packet_recv_server/41c33cb6c5ccffbf81c1826e7b5ef27b260d6a0f
new file mode 100644
index 0000000..ea4e835
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/41c33cb6c5ccffbf81c1826e7b5ef27b260d6a0f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/422f4257042b2f95b9b9d3765dbb7344ae7f0c39 b/fuzz/corpus/packet_recv_server/422f4257042b2f95b9b9d3765dbb7344ae7f0c39
new file mode 100644
index 0000000..d13f77c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/422f4257042b2f95b9b9d3765dbb7344ae7f0c39
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/42d262b169ad6c90da08d3aeec2781934976f798 b/fuzz/corpus/packet_recv_server/42d262b169ad6c90da08d3aeec2781934976f798
deleted file mode 100644
index a5b0c3e..0000000
--- a/fuzz/corpus/packet_recv_server/42d262b169ad6c90da08d3aeec2781934976f798
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/43495d55416d943d912edd01a36328c28a3d64e8 b/fuzz/corpus/packet_recv_server/43495d55416d943d912edd01a36328c28a3d64e8
new file mode 100644
index 0000000..b0bea4b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/43495d55416d943d912edd01a36328c28a3d64e8
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/43a9bc36b0672481b29411f5fae892627073eb97 b/fuzz/corpus/packet_recv_server/43a9bc36b0672481b29411f5fae892627073eb97
new file mode 100644
index 0000000..ed70e7e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/43a9bc36b0672481b29411f5fae892627073eb97
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/44611aeb2932decb94947ce84884a85f46cd1e81 b/fuzz/corpus/packet_recv_server/44611aeb2932decb94947ce84884a85f46cd1e81
new file mode 100644
index 0000000..5b5ccb6
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/44611aeb2932decb94947ce84884a85f46cd1e81
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/461fb1a1c61b1417d216c2f55d63200f1a70b4a6 b/fuzz/corpus/packet_recv_server/461fb1a1c61b1417d216c2f55d63200f1a70b4a6
new file mode 100644
index 0000000..c917c44
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/461fb1a1c61b1417d216c2f55d63200f1a70b4a6
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/4661ca12f3c8b3cbbc732ce213b4a0ea7aaf1ecb b/fuzz/corpus/packet_recv_server/4661ca12f3c8b3cbbc732ce213b4a0ea7aaf1ecb
deleted file mode 100644
index 9ba634e..0000000
--- a/fuzz/corpus/packet_recv_server/4661ca12f3c8b3cbbc732ce213b4a0ea7aaf1ecb
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/47e8d073626ad373833672473a89a0d2d473eb91 b/fuzz/corpus/packet_recv_server/47e8d073626ad373833672473a89a0d2d473eb91
new file mode 100644
index 0000000..f7eee29
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/47e8d073626ad373833672473a89a0d2d473eb91
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/4892f8d74190bc9b730c5d35d924c753bad77360 b/fuzz/corpus/packet_recv_server/4892f8d74190bc9b730c5d35d924c753bad77360
deleted file mode 100644
index 534faa1..0000000
--- a/fuzz/corpus/packet_recv_server/4892f8d74190bc9b730c5d35d924c753bad77360
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/4920bbe1d2411072ea1fff48b94523c281b4508f b/fuzz/corpus/packet_recv_server/4920bbe1d2411072ea1fff48b94523c281b4508f
new file mode 100644
index 0000000..833fa6c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/4920bbe1d2411072ea1fff48b94523c281b4508f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/4ae614ac89cd7fa2bb9054095764404ed6414005 b/fuzz/corpus/packet_recv_server/4ae614ac89cd7fa2bb9054095764404ed6414005
deleted file mode 100644
index 700dcd6..0000000
--- a/fuzz/corpus/packet_recv_server/4ae614ac89cd7fa2bb9054095764404ed6414005
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/4bb333cd4ce665dbb989081816e601ae1380de47 b/fuzz/corpus/packet_recv_server/4bb333cd4ce665dbb989081816e601ae1380de47
new file mode 100644
index 0000000..8f67a43
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/4bb333cd4ce665dbb989081816e601ae1380de47
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/4c7267da453125f25102f6427da8b45b10b71b92 b/fuzz/corpus/packet_recv_server/4c7267da453125f25102f6427da8b45b10b71b92
deleted file mode 100644
index 72b8d10..0000000
--- a/fuzz/corpus/packet_recv_server/4c7267da453125f25102f6427da8b45b10b71b92
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/4c76bcc07eb5274258da1976b205a321d910ae33 b/fuzz/corpus/packet_recv_server/4c76bcc07eb5274258da1976b205a321d910ae33
deleted file mode 100644
index bbcb20e..0000000
--- a/fuzz/corpus/packet_recv_server/4c76bcc07eb5274258da1976b205a321d910ae33
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/4ce847da5450ce9c4a8137709e7ba677f265bb5b b/fuzz/corpus/packet_recv_server/4ce847da5450ce9c4a8137709e7ba677f265bb5b
new file mode 100644
index 0000000..76ac4d9
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/4ce847da5450ce9c4a8137709e7ba677f265bb5b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/4cf631f150d7c4ce50142b66ce7a64da14ac79f8 b/fuzz/corpus/packet_recv_server/4cf631f150d7c4ce50142b66ce7a64da14ac79f8
new file mode 100644
index 0000000..f99e950
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/4cf631f150d7c4ce50142b66ce7a64da14ac79f8
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/4e2a15e052b6e0f566d766828731abf9e4b0c324 b/fuzz/corpus/packet_recv_server/4e2a15e052b6e0f566d766828731abf9e4b0c324
new file mode 100644
index 0000000..70756b4
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/4e2a15e052b6e0f566d766828731abf9e4b0c324
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/4e81c945e8e32c8bfef0cea637174f8c2abd69ad b/fuzz/corpus/packet_recv_server/4e81c945e8e32c8bfef0cea637174f8c2abd69ad
deleted file mode 100644
index b5d27eb..0000000
--- a/fuzz/corpus/packet_recv_server/4e81c945e8e32c8bfef0cea637174f8c2abd69ad
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/4ef0963f38ebd30573661094f159c2c024f4f5ad b/fuzz/corpus/packet_recv_server/4ef0963f38ebd30573661094f159c2c024f4f5ad
deleted file mode 100644
index f69e157..0000000
--- a/fuzz/corpus/packet_recv_server/4ef0963f38ebd30573661094f159c2c024f4f5ad
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/4fbcc4be528a31adf048a1d5bb283c9209fbb0f7 b/fuzz/corpus/packet_recv_server/4fbcc4be528a31adf048a1d5bb283c9209fbb0f7
new file mode 100644
index 0000000..0d26713
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/4fbcc4be528a31adf048a1d5bb283c9209fbb0f7
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/4fe35ffeeff64f18c11a727b8a6e82b025014115 b/fuzz/corpus/packet_recv_server/4fe35ffeeff64f18c11a727b8a6e82b025014115
deleted file mode 100644
index 1665c3e..0000000
--- a/fuzz/corpus/packet_recv_server/4fe35ffeeff64f18c11a727b8a6e82b025014115
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/503319b362bc59af0f54b8ce480610b22973363f b/fuzz/corpus/packet_recv_server/503319b362bc59af0f54b8ce480610b22973363f
deleted file mode 100644
index 2fa0dbb..0000000
--- a/fuzz/corpus/packet_recv_server/503319b362bc59af0f54b8ce480610b22973363f
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/503ef6846e39a5c98658437121964bf526d81fce b/fuzz/corpus/packet_recv_server/503ef6846e39a5c98658437121964bf526d81fce
deleted file mode 100644
index 6257d86..0000000
--- a/fuzz/corpus/packet_recv_server/503ef6846e39a5c98658437121964bf526d81fce
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/5050efe2a5f981721f896a9da007f536d7669e17 b/fuzz/corpus/packet_recv_server/5050efe2a5f981721f896a9da007f536d7669e17
new file mode 100644
index 0000000..826c54d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/5050efe2a5f981721f896a9da007f536d7669e17
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/507a15ea494d0f33f36f73093dd7e800bd6a9832 b/fuzz/corpus/packet_recv_server/507a15ea494d0f33f36f73093dd7e800bd6a9832
new file mode 100644
index 0000000..2255346
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/507a15ea494d0f33f36f73093dd7e800bd6a9832
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/50ae6884e3d9aadddfc027e56b04fedbfeea65a8 b/fuzz/corpus/packet_recv_server/50ae6884e3d9aadddfc027e56b04fedbfeea65a8
deleted file mode 100644
index b4719d5..0000000
--- a/fuzz/corpus/packet_recv_server/50ae6884e3d9aadddfc027e56b04fedbfeea65a8
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/50ddffb796cc0b7857293ed296b5666165d082a9 b/fuzz/corpus/packet_recv_server/50ddffb796cc0b7857293ed296b5666165d082a9
deleted file mode 100644
index 2dd70ed..0000000
--- a/fuzz/corpus/packet_recv_server/50ddffb796cc0b7857293ed296b5666165d082a9
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/51a2d12759be8aa648a271fa32f55d859de12801 b/fuzz/corpus/packet_recv_server/51a2d12759be8aa648a271fa32f55d859de12801
deleted file mode 100644
index 259a6ce..0000000
--- a/fuzz/corpus/packet_recv_server/51a2d12759be8aa648a271fa32f55d859de12801
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/51c1805cbcaaf036f842085327e381ae385ff5ea b/fuzz/corpus/packet_recv_server/51c1805cbcaaf036f842085327e381ae385ff5ea
new file mode 100644
index 0000000..b9538ec
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/51c1805cbcaaf036f842085327e381ae385ff5ea
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/52a968754d56aaf1cacde27c25c1279c00dd87db b/fuzz/corpus/packet_recv_server/52a968754d56aaf1cacde27c25c1279c00dd87db
deleted file mode 100644
index d46d08d..0000000
--- a/fuzz/corpus/packet_recv_server/52a968754d56aaf1cacde27c25c1279c00dd87db
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/52ba512ddaf73aba4c5b477c54e38e0b0ee65b1c b/fuzz/corpus/packet_recv_server/52ba512ddaf73aba4c5b477c54e38e0b0ee65b1c
new file mode 100644
index 0000000..f87a3ce
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/52ba512ddaf73aba4c5b477c54e38e0b0ee65b1c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/53168c1452823771b600d319ee12f045e0d7614f b/fuzz/corpus/packet_recv_server/53168c1452823771b600d319ee12f045e0d7614f
new file mode 100644
index 0000000..a8cb3e1
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/53168c1452823771b600d319ee12f045e0d7614f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/534e6575b3ba3867e3da96a4265e629e16940e43 b/fuzz/corpus/packet_recv_server/534e6575b3ba3867e3da96a4265e629e16940e43
deleted file mode 100644
index 8775664..0000000
--- a/fuzz/corpus/packet_recv_server/534e6575b3ba3867e3da96a4265e629e16940e43
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/548e1f60299392c62630bcd8d5bbcc1676c9841f b/fuzz/corpus/packet_recv_server/548e1f60299392c62630bcd8d5bbcc1676c9841f
deleted file mode 100644
index f76c7c9..0000000
--- a/fuzz/corpus/packet_recv_server/548e1f60299392c62630bcd8d5bbcc1676c9841f
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/54f442ebfc4c7a41d26189cd349396c96fa14830 b/fuzz/corpus/packet_recv_server/54f442ebfc4c7a41d26189cd349396c96fa14830
deleted file mode 100644
index 6d0f28b..0000000
--- a/fuzz/corpus/packet_recv_server/54f442ebfc4c7a41d26189cd349396c96fa14830
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/556f917afb99b7ad017813312182c432142ae217 b/fuzz/corpus/packet_recv_server/556f917afb99b7ad017813312182c432142ae217
new file mode 100644
index 0000000..5151a6b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/556f917afb99b7ad017813312182c432142ae217
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/56e74c950af3ab0bfe2057fd1d8b6a1a2f505f14 b/fuzz/corpus/packet_recv_server/56e74c950af3ab0bfe2057fd1d8b6a1a2f505f14
new file mode 100644
index 0000000..0e59391
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/56e74c950af3ab0bfe2057fd1d8b6a1a2f505f14
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/571bd43f7c16b55f4839e9c5a3b45b8d0639a8e8 b/fuzz/corpus/packet_recv_server/571bd43f7c16b55f4839e9c5a3b45b8d0639a8e8
new file mode 100644
index 0000000..603b2d6
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/571bd43f7c16b55f4839e9c5a3b45b8d0639a8e8
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/58c543a50bf9d98467427480e02fce83e7861a9b b/fuzz/corpus/packet_recv_server/58c543a50bf9d98467427480e02fce83e7861a9b
new file mode 100644
index 0000000..c861bce
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/58c543a50bf9d98467427480e02fce83e7861a9b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/5a361beb099e24574236a77e955f46734fd5ca71 b/fuzz/corpus/packet_recv_server/5a361beb099e24574236a77e955f46734fd5ca71
deleted file mode 100644
index e45d64e..0000000
--- a/fuzz/corpus/packet_recv_server/5a361beb099e24574236a77e955f46734fd5ca71
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/5b87c0d735b25e11e2821ed708ba8f8a476c6909 b/fuzz/corpus/packet_recv_server/5b87c0d735b25e11e2821ed708ba8f8a476c6909
deleted file mode 100644
index 895d8a8..0000000
--- a/fuzz/corpus/packet_recv_server/5b87c0d735b25e11e2821ed708ba8f8a476c6909
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/5c2c0572f5c2df47360329ce641ff5bf7f23ad26 b/fuzz/corpus/packet_recv_server/5c2c0572f5c2df47360329ce641ff5bf7f23ad26
deleted file mode 100644
index 3566d15..0000000
--- a/fuzz/corpus/packet_recv_server/5c2c0572f5c2df47360329ce641ff5bf7f23ad26
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/5cbb9fee5099b02333c132bdff4bec6cbe9ae585 b/fuzz/corpus/packet_recv_server/5cbb9fee5099b02333c132bdff4bec6cbe9ae585
deleted file mode 100644
index 2b7a7af..0000000
--- a/fuzz/corpus/packet_recv_server/5cbb9fee5099b02333c132bdff4bec6cbe9ae585
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/5de2d2ac03c3401937075ab6f631df2e64ae4cdd b/fuzz/corpus/packet_recv_server/5de2d2ac03c3401937075ab6f631df2e64ae4cdd
deleted file mode 100644
index 2adfbe0..0000000
--- a/fuzz/corpus/packet_recv_server/5de2d2ac03c3401937075ab6f631df2e64ae4cdd
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/5e1da251e72e1d34a8c67880b95582886aaec63c b/fuzz/corpus/packet_recv_server/5e1da251e72e1d34a8c67880b95582886aaec63c
deleted file mode 100644
index 2ef2e00..0000000
--- a/fuzz/corpus/packet_recv_server/5e1da251e72e1d34a8c67880b95582886aaec63c
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/5e59875bccc420c5b43eb63c028d85896b525513 b/fuzz/corpus/packet_recv_server/5e59875bccc420c5b43eb63c028d85896b525513
new file mode 100644
index 0000000..e1021ea
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/5e59875bccc420c5b43eb63c028d85896b525513
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/5f5a33e5bd4feb923251c7cfa44aecc065e2e1cb b/fuzz/corpus/packet_recv_server/5f5a33e5bd4feb923251c7cfa44aecc065e2e1cb
deleted file mode 100644
index b8837df..0000000
--- a/fuzz/corpus/packet_recv_server/5f5a33e5bd4feb923251c7cfa44aecc065e2e1cb
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/5f6e71a4be6a4937fc2d29e93ab2d901fe4dbe8d b/fuzz/corpus/packet_recv_server/5f6e71a4be6a4937fc2d29e93ab2d901fe4dbe8d
new file mode 100644
index 0000000..19d52de
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/5f6e71a4be6a4937fc2d29e93ab2d901fe4dbe8d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/5f773fde7e93c4c9d137a6fdcfa4e20676ccef1e b/fuzz/corpus/packet_recv_server/5f773fde7e93c4c9d137a6fdcfa4e20676ccef1e
new file mode 100644
index 0000000..7fc0a6c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/5f773fde7e93c4c9d137a6fdcfa4e20676ccef1e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/5fcb90043594f16b64d46c3b4a05a2d69ff2a693 b/fuzz/corpus/packet_recv_server/5fcb90043594f16b64d46c3b4a05a2d69ff2a693
new file mode 100644
index 0000000..5566bc3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/5fcb90043594f16b64d46c3b4a05a2d69ff2a693
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/600c0ae2ba0382865b04d17290ee46264d224aa7 b/fuzz/corpus/packet_recv_server/600c0ae2ba0382865b04d17290ee46264d224aa7
deleted file mode 100644
index 747b5a7..0000000
--- a/fuzz/corpus/packet_recv_server/600c0ae2ba0382865b04d17290ee46264d224aa7
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/621031a58e63f390f59aed217ac9a987aeab8284 b/fuzz/corpus/packet_recv_server/621031a58e63f390f59aed217ac9a987aeab8284
new file mode 100644
index 0000000..f5084c8
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/621031a58e63f390f59aed217ac9a987aeab8284
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/635b9fc5a9002db5c2ebc696a85fdad87034a114 b/fuzz/corpus/packet_recv_server/635b9fc5a9002db5c2ebc696a85fdad87034a114
new file mode 100644
index 0000000..0ec0789
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/635b9fc5a9002db5c2ebc696a85fdad87034a114
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/63cb5dab8b69d5ea256fad0224852037a4d14e5a b/fuzz/corpus/packet_recv_server/63cb5dab8b69d5ea256fad0224852037a4d14e5a
deleted file mode 100644
index bc31c12..0000000
--- a/fuzz/corpus/packet_recv_server/63cb5dab8b69d5ea256fad0224852037a4d14e5a
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/63eb202f8b5b609ed8049dfe576e71c5c7c141be b/fuzz/corpus/packet_recv_server/63eb202f8b5b609ed8049dfe576e71c5c7c141be
new file mode 100644
index 0000000..0f84aa1
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/63eb202f8b5b609ed8049dfe576e71c5c7c141be
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/63ed9f718bb67808a9fe151dd431af35519d95d7 b/fuzz/corpus/packet_recv_server/63ed9f718bb67808a9fe151dd431af35519d95d7
deleted file mode 100644
index 563407e..0000000
--- a/fuzz/corpus/packet_recv_server/63ed9f718bb67808a9fe151dd431af35519d95d7
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/64495f4501a4f1c5fc147066f761ba38eaff0f81 b/fuzz/corpus/packet_recv_server/64495f4501a4f1c5fc147066f761ba38eaff0f81
deleted file mode 100644
index b1f7bf4..0000000
--- a/fuzz/corpus/packet_recv_server/64495f4501a4f1c5fc147066f761ba38eaff0f81
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/64bf53d87a149d956ffe13baa30f320f10f78e63 b/fuzz/corpus/packet_recv_server/64bf53d87a149d956ffe13baa30f320f10f78e63
deleted file mode 100644
index 9518d15..0000000
--- a/fuzz/corpus/packet_recv_server/64bf53d87a149d956ffe13baa30f320f10f78e63
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/64cedd994a6d50b8390c215468ee53e996be9932 b/fuzz/corpus/packet_recv_server/64cedd994a6d50b8390c215468ee53e996be9932
deleted file mode 100644
index 49dcde5..0000000
--- a/fuzz/corpus/packet_recv_server/64cedd994a6d50b8390c215468ee53e996be9932
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/6603b077a5a9c2713692d153f65ff1b0b29c1f86 b/fuzz/corpus/packet_recv_server/6603b077a5a9c2713692d153f65ff1b0b29c1f86
new file mode 100644
index 0000000..ae6ffac
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/6603b077a5a9c2713692d153f65ff1b0b29c1f86
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/66682a0877549513bde3ccf4d6ba8502930f609a b/fuzz/corpus/packet_recv_server/66682a0877549513bde3ccf4d6ba8502930f609a
deleted file mode 100644
index fae7e04..0000000
--- a/fuzz/corpus/packet_recv_server/66682a0877549513bde3ccf4d6ba8502930f609a
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/66e1d0a7b5160c39001ed8822103d244ee5e47fd b/fuzz/corpus/packet_recv_server/66e1d0a7b5160c39001ed8822103d244ee5e47fd
new file mode 100644
index 0000000..6aa55bb
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/66e1d0a7b5160c39001ed8822103d244ee5e47fd
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/67eb24c62496df8dca2e56dc7198c1e77eb460b2 b/fuzz/corpus/packet_recv_server/67eb24c62496df8dca2e56dc7198c1e77eb460b2
deleted file mode 100644
index 5b8d8ff..0000000
--- a/fuzz/corpus/packet_recv_server/67eb24c62496df8dca2e56dc7198c1e77eb460b2
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/6992fd6a01d00f7cc4657e018bfe57955978c0a8 b/fuzz/corpus/packet_recv_server/6992fd6a01d00f7cc4657e018bfe57955978c0a8
new file mode 100644
index 0000000..7513199
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/6992fd6a01d00f7cc4657e018bfe57955978c0a8
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/6a2845fbabfc19e1face037397938c60a899bc07 b/fuzz/corpus/packet_recv_server/6a2845fbabfc19e1face037397938c60a899bc07
new file mode 100644
index 0000000..16e69f6
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/6a2845fbabfc19e1face037397938c60a899bc07
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/6b1380329265e84eff8ea9c6a95443f055dc6438 b/fuzz/corpus/packet_recv_server/6b1380329265e84eff8ea9c6a95443f055dc6438
new file mode 100644
index 0000000..55337df
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/6b1380329265e84eff8ea9c6a95443f055dc6438
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/6bca6b79b02cebd764d04699b47da4251ab876d4 b/fuzz/corpus/packet_recv_server/6bca6b79b02cebd764d04699b47da4251ab876d4
new file mode 100644
index 0000000..0a57ec3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/6bca6b79b02cebd764d04699b47da4251ab876d4
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/6c72df5af98d2a17d8d2bde5c190b8fcda2d592e b/fuzz/corpus/packet_recv_server/6c72df5af98d2a17d8d2bde5c190b8fcda2d592e
new file mode 100644
index 0000000..7faed30
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/6c72df5af98d2a17d8d2bde5c190b8fcda2d592e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/6fb225e5e21fcaf48117f849a4a56c7d07b4a388 b/fuzz/corpus/packet_recv_server/6fb225e5e21fcaf48117f849a4a56c7d07b4a388
new file mode 100644
index 0000000..1cee196
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/6fb225e5e21fcaf48117f849a4a56c7d07b4a388
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/70ceaf3890368a7dae0472c3444851defd86288c b/fuzz/corpus/packet_recv_server/70ceaf3890368a7dae0472c3444851defd86288c
deleted file mode 100644
index e1f6b3f..0000000
--- a/fuzz/corpus/packet_recv_server/70ceaf3890368a7dae0472c3444851defd86288c
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/723b0298fb58faa02ba4ab75a2e7cb0f13ed898f b/fuzz/corpus/packet_recv_server/723b0298fb58faa02ba4ab75a2e7cb0f13ed898f
new file mode 100644
index 0000000..a213ab2
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/723b0298fb58faa02ba4ab75a2e7cb0f13ed898f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/72d8c5ff7fe4c1597d4cfaed551e56aea62690b9 b/fuzz/corpus/packet_recv_server/72d8c5ff7fe4c1597d4cfaed551e56aea62690b9
new file mode 100644
index 0000000..c85d830
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/72d8c5ff7fe4c1597d4cfaed551e56aea62690b9
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/7318ebc37f7e9e6ae3ef13b82d35b4c94270a12e b/fuzz/corpus/packet_recv_server/7318ebc37f7e9e6ae3ef13b82d35b4c94270a12e
new file mode 100644
index 0000000..d7dbd8c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/7318ebc37f7e9e6ae3ef13b82d35b4c94270a12e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/731cb98e2ca24a9fb0af89ef082a8ae7b4a8c506 b/fuzz/corpus/packet_recv_server/731cb98e2ca24a9fb0af89ef082a8ae7b4a8c506
new file mode 100644
index 0000000..31a6d4e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/731cb98e2ca24a9fb0af89ef082a8ae7b4a8c506
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/7359f9c582a51110dfe68d8732264ab4504b1647 b/fuzz/corpus/packet_recv_server/7359f9c582a51110dfe68d8732264ab4504b1647
deleted file mode 100644
index 5b4fb0f..0000000
--- a/fuzz/corpus/packet_recv_server/7359f9c582a51110dfe68d8732264ab4504b1647
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/73b60fb3c76f7fd8efd2768ad7663d78680745c7 b/fuzz/corpus/packet_recv_server/73b60fb3c76f7fd8efd2768ad7663d78680745c7
deleted file mode 100644
index 6703d01..0000000
--- a/fuzz/corpus/packet_recv_server/73b60fb3c76f7fd8efd2768ad7663d78680745c7
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/749c41b9a4bfd3df3e915dd9918b49e104283bf2 b/fuzz/corpus/packet_recv_server/749c41b9a4bfd3df3e915dd9918b49e104283bf2
deleted file mode 100644
index cd3f673..0000000
--- a/fuzz/corpus/packet_recv_server/749c41b9a4bfd3df3e915dd9918b49e104283bf2
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/7540dff20e86dcfa4f2298e708f5beb6d5678987 b/fuzz/corpus/packet_recv_server/7540dff20e86dcfa4f2298e708f5beb6d5678987
deleted file mode 100644
index 04ded07..0000000
--- a/fuzz/corpus/packet_recv_server/7540dff20e86dcfa4f2298e708f5beb6d5678987
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/75d14c6a174e39d85bd64373b211555cfd1fb9ef b/fuzz/corpus/packet_recv_server/75d14c6a174e39d85bd64373b211555cfd1fb9ef
new file mode 100644
index 0000000..d781adc
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/75d14c6a174e39d85bd64373b211555cfd1fb9ef
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/76aebe7346ce60500933bc23187158c9c0a054c1 b/fuzz/corpus/packet_recv_server/76aebe7346ce60500933bc23187158c9c0a054c1
new file mode 100644
index 0000000..727955b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/76aebe7346ce60500933bc23187158c9c0a054c1
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/76fc345a915d74c44d5ef590cd03bfbb5592433f b/fuzz/corpus/packet_recv_server/76fc345a915d74c44d5ef590cd03bfbb5592433f
new file mode 100644
index 0000000..62f4909
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/76fc345a915d74c44d5ef590cd03bfbb5592433f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/7700120d43a4757701f207b3c444c41f6239111d b/fuzz/corpus/packet_recv_server/7700120d43a4757701f207b3c444c41f6239111d
new file mode 100644
index 0000000..19c5648
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/7700120d43a4757701f207b3c444c41f6239111d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/772206293f93f3a47178c977e8770335de0b603b b/fuzz/corpus/packet_recv_server/772206293f93f3a47178c977e8770335de0b603b
new file mode 100644
index 0000000..90cd3b3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/772206293f93f3a47178c977e8770335de0b603b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/781494e5916bf3c94813f5153a1d6c099c86ef98 b/fuzz/corpus/packet_recv_server/781494e5916bf3c94813f5153a1d6c099c86ef98
deleted file mode 100644
index ce47bce..0000000
--- a/fuzz/corpus/packet_recv_server/781494e5916bf3c94813f5153a1d6c099c86ef98
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/78de9af318ea79e7b922d517b687fe2c743a539e b/fuzz/corpus/packet_recv_server/78de9af318ea79e7b922d517b687fe2c743a539e
new file mode 100644
index 0000000..ed1277b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/78de9af318ea79e7b922d517b687fe2c743a539e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/7906992edf4bf39047b77472815b3fe54102218f b/fuzz/corpus/packet_recv_server/7906992edf4bf39047b77472815b3fe54102218f
new file mode 100644
index 0000000..b986366
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/7906992edf4bf39047b77472815b3fe54102218f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/79fe774cc14a60b3c8a898f3021beaac4654ff0a b/fuzz/corpus/packet_recv_server/79fe774cc14a60b3c8a898f3021beaac4654ff0a
new file mode 100644
index 0000000..b7b6f4e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/79fe774cc14a60b3c8a898f3021beaac4654ff0a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/7a46fa48d6219a6995f52c99cf6e2cfb56699c4b b/fuzz/corpus/packet_recv_server/7a46fa48d6219a6995f52c99cf6e2cfb56699c4b
deleted file mode 100644
index 35c37f6..0000000
--- a/fuzz/corpus/packet_recv_server/7a46fa48d6219a6995f52c99cf6e2cfb56699c4b
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/7b7d7440ae4732cece87a64379fe2cb8e4b39fd7 b/fuzz/corpus/packet_recv_server/7b7d7440ae4732cece87a64379fe2cb8e4b39fd7
new file mode 100644
index 0000000..6e326bf
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/7b7d7440ae4732cece87a64379fe2cb8e4b39fd7
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/7bfebdba2c2e7b1746d57b86d21c9094ac320792 b/fuzz/corpus/packet_recv_server/7bfebdba2c2e7b1746d57b86d21c9094ac320792
new file mode 100644
index 0000000..ccdca64
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/7bfebdba2c2e7b1746d57b86d21c9094ac320792
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/7ce83b5207edf57bee6f363324e1a50231a7a1af b/fuzz/corpus/packet_recv_server/7ce83b5207edf57bee6f363324e1a50231a7a1af
deleted file mode 100644
index ca3dc19..0000000
--- a/fuzz/corpus/packet_recv_server/7ce83b5207edf57bee6f363324e1a50231a7a1af
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/7cfd8065e6dfa763e9332b79660f74e8ecb3aded b/fuzz/corpus/packet_recv_server/7cfd8065e6dfa763e9332b79660f74e8ecb3aded
new file mode 100644
index 0000000..b1355db
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/7cfd8065e6dfa763e9332b79660f74e8ecb3aded
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/7e0ad1d78cfeff5220f48cc0d0a2661b4a9bdb8e b/fuzz/corpus/packet_recv_server/7e0ad1d78cfeff5220f48cc0d0a2661b4a9bdb8e
new file mode 100644
index 0000000..0b24f70
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/7e0ad1d78cfeff5220f48cc0d0a2661b4a9bdb8e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/7ea10613d75f5c42bf318157202ce131fbb6fef8 b/fuzz/corpus/packet_recv_server/7ea10613d75f5c42bf318157202ce131fbb6fef8
deleted file mode 100644
index 7d1110e..0000000
--- a/fuzz/corpus/packet_recv_server/7ea10613d75f5c42bf318157202ce131fbb6fef8
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/804359a090e40aedc99998b05a96167794712648 b/fuzz/corpus/packet_recv_server/804359a090e40aedc99998b05a96167794712648
deleted file mode 100644
index e285ee6..0000000
--- a/fuzz/corpus/packet_recv_server/804359a090e40aedc99998b05a96167794712648
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/80bd782fa72e8a3df41e0f8f1a2b9a5a971386a6 b/fuzz/corpus/packet_recv_server/80bd782fa72e8a3df41e0f8f1a2b9a5a971386a6
deleted file mode 100644
index 15b3ee3..0000000
--- a/fuzz/corpus/packet_recv_server/80bd782fa72e8a3df41e0f8f1a2b9a5a971386a6
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/80c2e08d2148dfdefcf90125478b836b5fe5107f b/fuzz/corpus/packet_recv_server/80c2e08d2148dfdefcf90125478b836b5fe5107f
new file mode 100644
index 0000000..4e998e3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/80c2e08d2148dfdefcf90125478b836b5fe5107f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/81099db5e4dd5a9c297d5bcdad953daf658ea2d0 b/fuzz/corpus/packet_recv_server/81099db5e4dd5a9c297d5bcdad953daf658ea2d0
new file mode 100644
index 0000000..67c40c4
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/81099db5e4dd5a9c297d5bcdad953daf658ea2d0
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/82a82acbebf3050a00e53e64f518c32dd485c776 b/fuzz/corpus/packet_recv_server/82a82acbebf3050a00e53e64f518c32dd485c776
new file mode 100644
index 0000000..cd73490
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/82a82acbebf3050a00e53e64f518c32dd485c776
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/832e9a95acf57286b838e48cb49a548633764c26 b/fuzz/corpus/packet_recv_server/832e9a95acf57286b838e48cb49a548633764c26
deleted file mode 100644
index 926a3cb..0000000
--- a/fuzz/corpus/packet_recv_server/832e9a95acf57286b838e48cb49a548633764c26
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/832f7ab909d6fbcf1ea838478763a55f072924da b/fuzz/corpus/packet_recv_server/832f7ab909d6fbcf1ea838478763a55f072924da
deleted file mode 100644
index 8cdf53e..0000000
--- a/fuzz/corpus/packet_recv_server/832f7ab909d6fbcf1ea838478763a55f072924da
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/834da67094946f0f753d609bd08ad0f43fe30fef b/fuzz/corpus/packet_recv_server/834da67094946f0f753d609bd08ad0f43fe30fef
deleted file mode 100644
index fed4ac7..0000000
--- a/fuzz/corpus/packet_recv_server/834da67094946f0f753d609bd08ad0f43fe30fef
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/83edbb51895d612ef5cdde3818242f6584f4a18d b/fuzz/corpus/packet_recv_server/83edbb51895d612ef5cdde3818242f6584f4a18d
new file mode 100644
index 0000000..e534d49
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/83edbb51895d612ef5cdde3818242f6584f4a18d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/83f04d2fe775fe4b22db9de8ef5467283a27ee79 b/fuzz/corpus/packet_recv_server/83f04d2fe775fe4b22db9de8ef5467283a27ee79
deleted file mode 100644
index e11ab02..0000000
--- a/fuzz/corpus/packet_recv_server/83f04d2fe775fe4b22db9de8ef5467283a27ee79
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/842e61f9fd51b26cc9d07641ac6cbb8b46a2f107 b/fuzz/corpus/packet_recv_server/842e61f9fd51b26cc9d07641ac6cbb8b46a2f107
new file mode 100644
index 0000000..2c977b0
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/842e61f9fd51b26cc9d07641ac6cbb8b46a2f107
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/847feee24df9110e6e905f5db08a33aff82348a2 b/fuzz/corpus/packet_recv_server/847feee24df9110e6e905f5db08a33aff82348a2
new file mode 100644
index 0000000..ab8f0b5
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/847feee24df9110e6e905f5db08a33aff82348a2
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/84ac6e6b0de5a33488348f767ac4f49530d243e1 b/fuzz/corpus/packet_recv_server/84ac6e6b0de5a33488348f767ac4f49530d243e1
new file mode 100644
index 0000000..dbeea17
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/84ac6e6b0de5a33488348f767ac4f49530d243e1
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/84ebb8d1710e0f0e23c6293b13a57eb128e042fe b/fuzz/corpus/packet_recv_server/84ebb8d1710e0f0e23c6293b13a57eb128e042fe
new file mode 100644
index 0000000..b0ef210
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/84ebb8d1710e0f0e23c6293b13a57eb128e042fe
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/85a7e15dae4eff017c5b99a82507ca34004872eb b/fuzz/corpus/packet_recv_server/85a7e15dae4eff017c5b99a82507ca34004872eb
new file mode 100644
index 0000000..7be9e6f
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/85a7e15dae4eff017c5b99a82507ca34004872eb
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/85f97f963c82e0dc608691f3ed23f034051d5e9d b/fuzz/corpus/packet_recv_server/85f97f963c82e0dc608691f3ed23f034051d5e9d
new file mode 100644
index 0000000..ae3ac1a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/85f97f963c82e0dc608691f3ed23f034051d5e9d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/86d21b07052b58f7884e454df4168dc097a3d0a7 b/fuzz/corpus/packet_recv_server/86d21b07052b58f7884e454df4168dc097a3d0a7
new file mode 100644
index 0000000..152956a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/86d21b07052b58f7884e454df4168dc097a3d0a7
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/872cfe4db521710b92cf3bbc86cc027e2c6d4435 b/fuzz/corpus/packet_recv_server/872cfe4db521710b92cf3bbc86cc027e2c6d4435
new file mode 100644
index 0000000..df14fb4
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/872cfe4db521710b92cf3bbc86cc027e2c6d4435
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/8731599c2aadee3654222bc86c728b353581d0d8 b/fuzz/corpus/packet_recv_server/8731599c2aadee3654222bc86c728b353581d0d8
new file mode 100644
index 0000000..f8ba1ee
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/8731599c2aadee3654222bc86c728b353581d0d8
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/87cddc2776303e8cb7f5a13808dd896edaf11a8a b/fuzz/corpus/packet_recv_server/87cddc2776303e8cb7f5a13808dd896edaf11a8a
new file mode 100644
index 0000000..b007ce1
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/87cddc2776303e8cb7f5a13808dd896edaf11a8a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/8824fd6e41072bbc8167cfcb209d7e3a1141f25e b/fuzz/corpus/packet_recv_server/8824fd6e41072bbc8167cfcb209d7e3a1141f25e
new file mode 100644
index 0000000..38621d1
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/8824fd6e41072bbc8167cfcb209d7e3a1141f25e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/88933bbd52fb800b95188479ab3e6c6c82eac8b8 b/fuzz/corpus/packet_recv_server/88933bbd52fb800b95188479ab3e6c6c82eac8b8
new file mode 100644
index 0000000..7e7d161
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/88933bbd52fb800b95188479ab3e6c6c82eac8b8
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/8993ebf59a571d26e748bc5e12877a9401efbbeb b/fuzz/corpus/packet_recv_server/8993ebf59a571d26e748bc5e12877a9401efbbeb
new file mode 100644
index 0000000..898bf82
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/8993ebf59a571d26e748bc5e12877a9401efbbeb
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/89c540492e0f26ada3622775983fbeddcb9a772d b/fuzz/corpus/packet_recv_server/89c540492e0f26ada3622775983fbeddcb9a772d
deleted file mode 100644
index 54f5b99..0000000
--- a/fuzz/corpus/packet_recv_server/89c540492e0f26ada3622775983fbeddcb9a772d
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/8a1a6021e1b0c6001f42d49fc8cfbabc61692ddb b/fuzz/corpus/packet_recv_server/8a1a6021e1b0c6001f42d49fc8cfbabc61692ddb
deleted file mode 100644
index 3336ce1..0000000
--- a/fuzz/corpus/packet_recv_server/8a1a6021e1b0c6001f42d49fc8cfbabc61692ddb
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/8a30a316242c0537bf51177d22b3204894691ab2 b/fuzz/corpus/packet_recv_server/8a30a316242c0537bf51177d22b3204894691ab2
new file mode 100644
index 0000000..780269e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/8a30a316242c0537bf51177d22b3204894691ab2
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/8a51d90111c957a344d2f43a1e5ca6a5e583d688 b/fuzz/corpus/packet_recv_server/8a51d90111c957a344d2f43a1e5ca6a5e583d688
new file mode 100644
index 0000000..014f648
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/8a51d90111c957a344d2f43a1e5ca6a5e583d688
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/8ad75b3bec57ca312f31292b6c0713930d90fc77 b/fuzz/corpus/packet_recv_server/8ad75b3bec57ca312f31292b6c0713930d90fc77
new file mode 100644
index 0000000..4270a0d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/8ad75b3bec57ca312f31292b6c0713930d90fc77
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/8d1013cef3f7770f04bc2d44c73540a977508ad6 b/fuzz/corpus/packet_recv_server/8d1013cef3f7770f04bc2d44c73540a977508ad6
deleted file mode 100644
index 0a4d900..0000000
--- a/fuzz/corpus/packet_recv_server/8d1013cef3f7770f04bc2d44c73540a977508ad6
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/8d1082663df7dbc92ec7579a3dc0930c7ae03681 b/fuzz/corpus/packet_recv_server/8d1082663df7dbc92ec7579a3dc0930c7ae03681
deleted file mode 100644
index d3c3d0e..0000000
--- a/fuzz/corpus/packet_recv_server/8d1082663df7dbc92ec7579a3dc0930c7ae03681
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/8daacc5c126564a944b9f1ae374aac09f48e1b3f b/fuzz/corpus/packet_recv_server/8daacc5c126564a944b9f1ae374aac09f48e1b3f
new file mode 100644
index 0000000..3619ea9
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/8daacc5c126564a944b9f1ae374aac09f48e1b3f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/8e5bf11f2ba99fcc8d1d2b3e8ebcb38c4aa7858a b/fuzz/corpus/packet_recv_server/8e5bf11f2ba99fcc8d1d2b3e8ebcb38c4aa7858a
new file mode 100644
index 0000000..866c2a6
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/8e5bf11f2ba99fcc8d1d2b3e8ebcb38c4aa7858a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/8e9185fba1787a185ccb27979356442e3440f0fe b/fuzz/corpus/packet_recv_server/8e9185fba1787a185ccb27979356442e3440f0fe
new file mode 100644
index 0000000..cec577d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/8e9185fba1787a185ccb27979356442e3440f0fe
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/90c0a9acd581fa8b7ced19449c30b16617fc4a9e b/fuzz/corpus/packet_recv_server/90c0a9acd581fa8b7ced19449c30b16617fc4a9e
new file mode 100644
index 0000000..e61df46
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/90c0a9acd581fa8b7ced19449c30b16617fc4a9e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/90d02d23a3411df31c91b5ead2644c7c6332b6b3 b/fuzz/corpus/packet_recv_server/90d02d23a3411df31c91b5ead2644c7c6332b6b3
new file mode 100644
index 0000000..c4978a7
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/90d02d23a3411df31c91b5ead2644c7c6332b6b3
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/91641f164de7764068ee217df98acefa4db4332a b/fuzz/corpus/packet_recv_server/91641f164de7764068ee217df98acefa4db4332a
new file mode 100644
index 0000000..eb7b525
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/91641f164de7764068ee217df98acefa4db4332a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/91d56c07a9e9fb906f92843a947fc2c0cc647ede b/fuzz/corpus/packet_recv_server/91d56c07a9e9fb906f92843a947fc2c0cc647ede
new file mode 100644
index 0000000..ac30653
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/91d56c07a9e9fb906f92843a947fc2c0cc647ede
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/92e0eae4e426950143459f659c02148d0b914cd9 b/fuzz/corpus/packet_recv_server/92e0eae4e426950143459f659c02148d0b914cd9
deleted file mode 100644
index 4a1ae77..0000000
--- a/fuzz/corpus/packet_recv_server/92e0eae4e426950143459f659c02148d0b914cd9
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/9345f8c4ec1bc61c0dee5a3bd6c96e670e60f103 b/fuzz/corpus/packet_recv_server/9345f8c4ec1bc61c0dee5a3bd6c96e670e60f103
new file mode 100644
index 0000000..a5a19cc
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/9345f8c4ec1bc61c0dee5a3bd6c96e670e60f103
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/93b3c89ac5563e506d174ccdd2885954b8c01d80 b/fuzz/corpus/packet_recv_server/93b3c89ac5563e506d174ccdd2885954b8c01d80
new file mode 100644
index 0000000..ee5ea34
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/93b3c89ac5563e506d174ccdd2885954b8c01d80
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/9426ab68f1eeb1daaeac39c7243d4f7e6aaaa6fe b/fuzz/corpus/packet_recv_server/9426ab68f1eeb1daaeac39c7243d4f7e6aaaa6fe
new file mode 100644
index 0000000..43a536f
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/9426ab68f1eeb1daaeac39c7243d4f7e6aaaa6fe
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/9568c5e3001d844205fd53fb20feee93ac7e5847 b/fuzz/corpus/packet_recv_server/9568c5e3001d844205fd53fb20feee93ac7e5847
new file mode 100644
index 0000000..f858de4
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/9568c5e3001d844205fd53fb20feee93ac7e5847
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/976b50b074c6d0625ca054d6ce9a7402df7aea82 b/fuzz/corpus/packet_recv_server/976b50b074c6d0625ca054d6ce9a7402df7aea82
new file mode 100644
index 0000000..4f52ce8
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/976b50b074c6d0625ca054d6ce9a7402df7aea82
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/98b53eec278d61db69a6938618e12636cac6df19 b/fuzz/corpus/packet_recv_server/98b53eec278d61db69a6938618e12636cac6df19
deleted file mode 100644
index e63c5a6..0000000
--- a/fuzz/corpus/packet_recv_server/98b53eec278d61db69a6938618e12636cac6df19
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/98cb01e5cc4a788f13c588c8c831056133dbc9cd b/fuzz/corpus/packet_recv_server/98cb01e5cc4a788f13c588c8c831056133dbc9cd
deleted file mode 100644
index 80ecae0..0000000
--- a/fuzz/corpus/packet_recv_server/98cb01e5cc4a788f13c588c8c831056133dbc9cd
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/98eb6ad8ea4961d03ab834d4e1bf2a82714cb82e b/fuzz/corpus/packet_recv_server/98eb6ad8ea4961d03ab834d4e1bf2a82714cb82e
new file mode 100644
index 0000000..d2b02c9
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/98eb6ad8ea4961d03ab834d4e1bf2a82714cb82e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/9a6fed57291617283dc985bacc3ba3e1f953b8a3 b/fuzz/corpus/packet_recv_server/9a6fed57291617283dc985bacc3ba3e1f953b8a3
new file mode 100644
index 0000000..12a2519
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/9a6fed57291617283dc985bacc3ba3e1f953b8a3
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/9b22f52f388b7f1277891ed225f8344c75d266db b/fuzz/corpus/packet_recv_server/9b22f52f388b7f1277891ed225f8344c75d266db
deleted file mode 100644
index b19983a..0000000
--- a/fuzz/corpus/packet_recv_server/9b22f52f388b7f1277891ed225f8344c75d266db
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/9b4434ad2c96a6d7570576b12008753639490350 b/fuzz/corpus/packet_recv_server/9b4434ad2c96a6d7570576b12008753639490350
new file mode 100644
index 0000000..71dade3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/9b4434ad2c96a6d7570576b12008753639490350
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/9b4848d7bddfc00094403779f37afbaea71edd25 b/fuzz/corpus/packet_recv_server/9b4848d7bddfc00094403779f37afbaea71edd25
new file mode 100644
index 0000000..1c7e933
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/9b4848d7bddfc00094403779f37afbaea71edd25
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/9b486106f7799c571d638eac8b81116302aefe93 b/fuzz/corpus/packet_recv_server/9b486106f7799c571d638eac8b81116302aefe93
deleted file mode 100644
index ad85e48..0000000
--- a/fuzz/corpus/packet_recv_server/9b486106f7799c571d638eac8b81116302aefe93
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/9c14063d58a38e55710d48c4808b044ade43121d b/fuzz/corpus/packet_recv_server/9c14063d58a38e55710d48c4808b044ade43121d
new file mode 100644
index 0000000..b99d4fb
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/9c14063d58a38e55710d48c4808b044ade43121d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/9c68d68453cdbd3f094410f2124319009ee7fa8b b/fuzz/corpus/packet_recv_server/9c68d68453cdbd3f094410f2124319009ee7fa8b
new file mode 100644
index 0000000..a124e94
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/9c68d68453cdbd3f094410f2124319009ee7fa8b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/9e7b19765f46daba0338d4fed6430ceefa2020f2 b/fuzz/corpus/packet_recv_server/9e7b19765f46daba0338d4fed6430ceefa2020f2
deleted file mode 100644
index 8d060e6..0000000
--- a/fuzz/corpus/packet_recv_server/9e7b19765f46daba0338d4fed6430ceefa2020f2
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/9e90ca8d3474cfe50fa35ce3a3a2b955e6a31a6c b/fuzz/corpus/packet_recv_server/9e90ca8d3474cfe50fa35ce3a3a2b955e6a31a6c
new file mode 100644
index 0000000..3991ef5
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/9e90ca8d3474cfe50fa35ce3a3a2b955e6a31a6c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/9ea28cd6bcbec38cf4b319dce3a958a8b8b34cde b/fuzz/corpus/packet_recv_server/9ea28cd6bcbec38cf4b319dce3a958a8b8b34cde
deleted file mode 100644
index 221418a..0000000
--- a/fuzz/corpus/packet_recv_server/9ea28cd6bcbec38cf4b319dce3a958a8b8b34cde
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a08e4b77c2863a50cbb386c24a389cc68d398ac0 b/fuzz/corpus/packet_recv_server/a08e4b77c2863a50cbb386c24a389cc68d398ac0
new file mode 100644
index 0000000..8754e23
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/a08e4b77c2863a50cbb386c24a389cc68d398ac0
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a23d05d3549859266f175664b158c718be9784c3 b/fuzz/corpus/packet_recv_server/a23d05d3549859266f175664b158c718be9784c3
new file mode 100644
index 0000000..a484068
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/a23d05d3549859266f175664b158c718be9784c3
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a278ef7b32b6b622361343e9147adf849638ee5b b/fuzz/corpus/packet_recv_server/a278ef7b32b6b622361343e9147adf849638ee5b
deleted file mode 100644
index 1fb196e..0000000
--- a/fuzz/corpus/packet_recv_server/a278ef7b32b6b622361343e9147adf849638ee5b
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a28fbf7656f2b702484581c0e4c3fe2f41897c13 b/fuzz/corpus/packet_recv_server/a28fbf7656f2b702484581c0e4c3fe2f41897c13
deleted file mode 100644
index b4a31cc..0000000
--- a/fuzz/corpus/packet_recv_server/a28fbf7656f2b702484581c0e4c3fe2f41897c13
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a465f9efeebf0f746160d98feb286144f8382083 b/fuzz/corpus/packet_recv_server/a465f9efeebf0f746160d98feb286144f8382083
new file mode 100644
index 0000000..2e94565
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/a465f9efeebf0f746160d98feb286144f8382083
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a4868afee262e154f628672b6db091a14744d9a0 b/fuzz/corpus/packet_recv_server/a4868afee262e154f628672b6db091a14744d9a0
deleted file mode 100644
index 3764da6..0000000
--- a/fuzz/corpus/packet_recv_server/a4868afee262e154f628672b6db091a14744d9a0
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a4c0a81358e111f37df7eba581f104cae57cd23d b/fuzz/corpus/packet_recv_server/a4c0a81358e111f37df7eba581f104cae57cd23d
new file mode 100644
index 0000000..0e99e4c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/a4c0a81358e111f37df7eba581f104cae57cd23d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a4f5a5019dd6aa6bfe610f63cae953295d403c9a b/fuzz/corpus/packet_recv_server/a4f5a5019dd6aa6bfe610f63cae953295d403c9a
new file mode 100644
index 0000000..b7342ac
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/a4f5a5019dd6aa6bfe610f63cae953295d403c9a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a52eb5bc9e9ef3fc46a78a1a61282b68d5ce5817 b/fuzz/corpus/packet_recv_server/a52eb5bc9e9ef3fc46a78a1a61282b68d5ce5817
new file mode 100644
index 0000000..e3ec2ab
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/a52eb5bc9e9ef3fc46a78a1a61282b68d5ce5817
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a5451e90d97616dec59aa6b0bfb5c1cf83ccdd88 b/fuzz/corpus/packet_recv_server/a5451e90d97616dec59aa6b0bfb5c1cf83ccdd88
deleted file mode 100644
index 850daee..0000000
--- a/fuzz/corpus/packet_recv_server/a5451e90d97616dec59aa6b0bfb5c1cf83ccdd88
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a5859c226b64f69641487336c5bcb1e2f3bc1c60 b/fuzz/corpus/packet_recv_server/a5859c226b64f69641487336c5bcb1e2f3bc1c60
new file mode 100644
index 0000000..9509e43
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/a5859c226b64f69641487336c5bcb1e2f3bc1c60
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a5f697968b734c4cd2c898d049a681960b6f7ce5 b/fuzz/corpus/packet_recv_server/a5f697968b734c4cd2c898d049a681960b6f7ce5
new file mode 100644
index 0000000..8f56653
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/a5f697968b734c4cd2c898d049a681960b6f7ce5
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a5f6eeddb397bce2d4256705728ab12d9a8b005d b/fuzz/corpus/packet_recv_server/a5f6eeddb397bce2d4256705728ab12d9a8b005d
deleted file mode 100644
index 96e266d..0000000
--- a/fuzz/corpus/packet_recv_server/a5f6eeddb397bce2d4256705728ab12d9a8b005d
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a66a37938fcd970d608c69e8043f47e1f6afdd9c b/fuzz/corpus/packet_recv_server/a66a37938fcd970d608c69e8043f47e1f6afdd9c
new file mode 100644
index 0000000..a22056b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/a66a37938fcd970d608c69e8043f47e1f6afdd9c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a7aaf54b817486bbaa32a867c10444ebab02f5c3 b/fuzz/corpus/packet_recv_server/a7aaf54b817486bbaa32a867c10444ebab02f5c3
new file mode 100644
index 0000000..303c4a3
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/a7aaf54b817486bbaa32a867c10444ebab02f5c3
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a7b70f70016cace56571c4d39e8f8da14eb49274 b/fuzz/corpus/packet_recv_server/a7b70f70016cace56571c4d39e8f8da14eb49274
deleted file mode 100644
index 47bb36f..0000000
--- a/fuzz/corpus/packet_recv_server/a7b70f70016cace56571c4d39e8f8da14eb49274
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a8551286ff0bc00c2f08546fded74c60c7341faf b/fuzz/corpus/packet_recv_server/a8551286ff0bc00c2f08546fded74c60c7341faf
new file mode 100644
index 0000000..87def94
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/a8551286ff0bc00c2f08546fded74c60c7341faf
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a856a64fe07ddd0026650a2db449d8a8979a8c4a b/fuzz/corpus/packet_recv_server/a856a64fe07ddd0026650a2db449d8a8979a8c4a
deleted file mode 100644
index 2bc963c..0000000
--- a/fuzz/corpus/packet_recv_server/a856a64fe07ddd0026650a2db449d8a8979a8c4a
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a879fe77c01768ff156cf7e185df2a25ee8ef909 b/fuzz/corpus/packet_recv_server/a879fe77c01768ff156cf7e185df2a25ee8ef909
new file mode 100644
index 0000000..5e33857
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/a879fe77c01768ff156cf7e185df2a25ee8ef909
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a8c48cda4b2ea0ecc178a8a0aebea8c8daf6de0f b/fuzz/corpus/packet_recv_server/a8c48cda4b2ea0ecc178a8a0aebea8c8daf6de0f
new file mode 100644
index 0000000..dd475cc
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/a8c48cda4b2ea0ecc178a8a0aebea8c8daf6de0f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a8d721c824fac38e3217ad052e2095e46ff60d8c b/fuzz/corpus/packet_recv_server/a8d721c824fac38e3217ad052e2095e46ff60d8c
new file mode 100644
index 0000000..77e3ec9
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/a8d721c824fac38e3217ad052e2095e46ff60d8c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a92e6b8ca1351583445cc0d925ef586770602c4f b/fuzz/corpus/packet_recv_server/a92e6b8ca1351583445cc0d925ef586770602c4f
new file mode 100644
index 0000000..0a31338
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/a92e6b8ca1351583445cc0d925ef586770602c4f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a9545f76382f741a70b0d459e0e84d2598dbd915 b/fuzz/corpus/packet_recv_server/a9545f76382f741a70b0d459e0e84d2598dbd915
new file mode 100644
index 0000000..43a57d9
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/a9545f76382f741a70b0d459e0e84d2598dbd915
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a96546286ac443c5aa705f8184e55c2119cbb627 b/fuzz/corpus/packet_recv_server/a96546286ac443c5aa705f8184e55c2119cbb627
new file mode 100644
index 0000000..3c75c60
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/a96546286ac443c5aa705f8184e55c2119cbb627
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/a9aae88bad1633e6f8af54948e4e967143d2fe1f b/fuzz/corpus/packet_recv_server/a9aae88bad1633e6f8af54948e4e967143d2fe1f
deleted file mode 100644
index e123fa4..0000000
--- a/fuzz/corpus/packet_recv_server/a9aae88bad1633e6f8af54948e4e967143d2fe1f
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/aa262476e258f2b720382618bd9e0acf10dffdda b/fuzz/corpus/packet_recv_server/aa262476e258f2b720382618bd9e0acf10dffdda
new file mode 100644
index 0000000..6edff68
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/aa262476e258f2b720382618bd9e0acf10dffdda
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/aafc59e95bd4163fb70277abf006fb0614387f6d b/fuzz/corpus/packet_recv_server/aafc59e95bd4163fb70277abf006fb0614387f6d
new file mode 100644
index 0000000..9681346
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/aafc59e95bd4163fb70277abf006fb0614387f6d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/ac44a1bb48e037c04cb7c1af24891cfbecceeb8e b/fuzz/corpus/packet_recv_server/ac44a1bb48e037c04cb7c1af24891cfbecceeb8e
deleted file mode 100644
index 4d8a215..0000000
--- a/fuzz/corpus/packet_recv_server/ac44a1bb48e037c04cb7c1af24891cfbecceeb8e
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/ac8cc434d23274c9ed92c5b415118d8143fc893a b/fuzz/corpus/packet_recv_server/ac8cc434d23274c9ed92c5b415118d8143fc893a
new file mode 100644
index 0000000..d33c868
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/ac8cc434d23274c9ed92c5b415118d8143fc893a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/ad267e2336d62bb768b141b4b890efa77b30259c b/fuzz/corpus/packet_recv_server/ad267e2336d62bb768b141b4b890efa77b30259c
new file mode 100644
index 0000000..8a5cb04
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/ad267e2336d62bb768b141b4b890efa77b30259c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/adaa6c05bd040fab096b8bd28fc68d2387929593 b/fuzz/corpus/packet_recv_server/adaa6c05bd040fab096b8bd28fc68d2387929593
new file mode 100644
index 0000000..0fd0216
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/adaa6c05bd040fab096b8bd28fc68d2387929593
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/ae2f567f4ce5a6a19c454c16e7e5cb4053b55877 b/fuzz/corpus/packet_recv_server/ae2f567f4ce5a6a19c454c16e7e5cb4053b55877
new file mode 100644
index 0000000..0b497de
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/ae2f567f4ce5a6a19c454c16e7e5cb4053b55877
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/ae9c504d8106e7c91d146f690da4fca276d87620 b/fuzz/corpus/packet_recv_server/ae9c504d8106e7c91d146f690da4fca276d87620
new file mode 100644
index 0000000..47b8f91
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/ae9c504d8106e7c91d146f690da4fca276d87620
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/aed580bcaf4ab50a756f4098cf004600d5305728 b/fuzz/corpus/packet_recv_server/aed580bcaf4ab50a756f4098cf004600d5305728
new file mode 100644
index 0000000..151762a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/aed580bcaf4ab50a756f4098cf004600d5305728
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/b006198bf087b8f9b3c050cb35c7c0a12bfd3f69 b/fuzz/corpus/packet_recv_server/b006198bf087b8f9b3c050cb35c7c0a12bfd3f69
new file mode 100644
index 0000000..477fb49
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/b006198bf087b8f9b3c050cb35c7c0a12bfd3f69
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/b0568f04a5dd0b00659a24813a14cb0a4254f8bc b/fuzz/corpus/packet_recv_server/b0568f04a5dd0b00659a24813a14cb0a4254f8bc
deleted file mode 100644
index 0f68b30..0000000
--- a/fuzz/corpus/packet_recv_server/b0568f04a5dd0b00659a24813a14cb0a4254f8bc
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/b0e40b990814fc0b07f34e17689e527357cd547a b/fuzz/corpus/packet_recv_server/b0e40b990814fc0b07f34e17689e527357cd547a
new file mode 100644
index 0000000..3e31f13
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/b0e40b990814fc0b07f34e17689e527357cd547a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/b197c7206e3b5171163a059fe666bfe59ba278c9 b/fuzz/corpus/packet_recv_server/b197c7206e3b5171163a059fe666bfe59ba278c9
deleted file mode 100644
index 727e3bc..0000000
--- a/fuzz/corpus/packet_recv_server/b197c7206e3b5171163a059fe666bfe59ba278c9
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/b21206ef8692c9d940ec2ff7a25a86bf8364d736 b/fuzz/corpus/packet_recv_server/b21206ef8692c9d940ec2ff7a25a86bf8364d736
new file mode 100644
index 0000000..6d29769
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/b21206ef8692c9d940ec2ff7a25a86bf8364d736
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/b259d2099877d7ba0c7eb8ce2e58afb2ccd06bbf b/fuzz/corpus/packet_recv_server/b259d2099877d7ba0c7eb8ce2e58afb2ccd06bbf
new file mode 100644
index 0000000..c7e398b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/b259d2099877d7ba0c7eb8ce2e58afb2ccd06bbf
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/b264dc0645dcc4851e58b552c64aae2e9362e507 b/fuzz/corpus/packet_recv_server/b264dc0645dcc4851e58b552c64aae2e9362e507
new file mode 100644
index 0000000..ae26c38
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/b264dc0645dcc4851e58b552c64aae2e9362e507
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/b294ddf477d81c3f5c15b1fc0a086eb0532f533e b/fuzz/corpus/packet_recv_server/b294ddf477d81c3f5c15b1fc0a086eb0532f533e
deleted file mode 100644
index 1567081..0000000
--- a/fuzz/corpus/packet_recv_server/b294ddf477d81c3f5c15b1fc0a086eb0532f533e
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/b2981a11d7d92ce4c18c9fe6706665c5f6b858a4 b/fuzz/corpus/packet_recv_server/b2981a11d7d92ce4c18c9fe6706665c5f6b858a4
new file mode 100644
index 0000000..6d24f79
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/b2981a11d7d92ce4c18c9fe6706665c5f6b858a4
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/b3af4ba5b4ac5dd6f2a0fe059c286f7359ec2602 b/fuzz/corpus/packet_recv_server/b3af4ba5b4ac5dd6f2a0fe059c286f7359ec2602
deleted file mode 100644
index 9d6dff1..0000000
--- a/fuzz/corpus/packet_recv_server/b3af4ba5b4ac5dd6f2a0fe059c286f7359ec2602
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/b520f4d6e46da75c51f6d8df2c8f039a55c1d837 b/fuzz/corpus/packet_recv_server/b520f4d6e46da75c51f6d8df2c8f039a55c1d837
deleted file mode 100644
index 7701869..0000000
--- a/fuzz/corpus/packet_recv_server/b520f4d6e46da75c51f6d8df2c8f039a55c1d837
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/b5c1b0eef8061beee43f958e66373020b113992c b/fuzz/corpus/packet_recv_server/b5c1b0eef8061beee43f958e66373020b113992c
new file mode 100644
index 0000000..e9169f2
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/b5c1b0eef8061beee43f958e66373020b113992c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/b64d27576eb1cac1a21bb79aca14c6e4c56de61c b/fuzz/corpus/packet_recv_server/b64d27576eb1cac1a21bb79aca14c6e4c56de61c
new file mode 100644
index 0000000..d70b16a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/b64d27576eb1cac1a21bb79aca14c6e4c56de61c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/b6d51129f8a75df309562d7fba6611db6892ae0e b/fuzz/corpus/packet_recv_server/b6d51129f8a75df309562d7fba6611db6892ae0e
new file mode 100644
index 0000000..4e71009
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/b6d51129f8a75df309562d7fba6611db6892ae0e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/b6d7cf907246f505ec858063b97fa37f3247ece7 b/fuzz/corpus/packet_recv_server/b6d7cf907246f505ec858063b97fa37f3247ece7
new file mode 100644
index 0000000..b90b75c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/b6d7cf907246f505ec858063b97fa37f3247ece7
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/b7396ef1c792567ead599fddd52f1402f6ad84a6 b/fuzz/corpus/packet_recv_server/b7396ef1c792567ead599fddd52f1402f6ad84a6
new file mode 100644
index 0000000..b60e8ed
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/b7396ef1c792567ead599fddd52f1402f6ad84a6
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/b8a0015ef976e3e2b12412c9d3f62af979cc462b b/fuzz/corpus/packet_recv_server/b8a0015ef976e3e2b12412c9d3f62af979cc462b
new file mode 100644
index 0000000..b3ac58b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/b8a0015ef976e3e2b12412c9d3f62af979cc462b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/b97ba31166c47f7b260ac2f1ea850a3af80c9170 b/fuzz/corpus/packet_recv_server/b97ba31166c47f7b260ac2f1ea850a3af80c9170
new file mode 100644
index 0000000..f8642b1
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/b97ba31166c47f7b260ac2f1ea850a3af80c9170
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/b9bfd6c40058578baf6b7f7697b85dbf83cf8d49 b/fuzz/corpus/packet_recv_server/b9bfd6c40058578baf6b7f7697b85dbf83cf8d49
new file mode 100644
index 0000000..94751b2
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/b9bfd6c40058578baf6b7f7697b85dbf83cf8d49
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/b9f8023f7b8e83433df0c357eeb8e4ddda79851f b/fuzz/corpus/packet_recv_server/b9f8023f7b8e83433df0c357eeb8e4ddda79851f
new file mode 100644
index 0000000..b468976
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/b9f8023f7b8e83433df0c357eeb8e4ddda79851f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/b9fb265b958eced2627da3ae8e5c43ab6741e99c b/fuzz/corpus/packet_recv_server/b9fb265b958eced2627da3ae8e5c43ab6741e99c
new file mode 100644
index 0000000..c91d38c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/b9fb265b958eced2627da3ae8e5c43ab6741e99c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/ba077a84c02eed891e4430d7c118bf2596368d6c b/fuzz/corpus/packet_recv_server/ba077a84c02eed891e4430d7c118bf2596368d6c
new file mode 100644
index 0000000..89271d7
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/ba077a84c02eed891e4430d7c118bf2596368d6c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/ba70b97920cafb63d094f87ae211ecc7610faa0c b/fuzz/corpus/packet_recv_server/ba70b97920cafb63d094f87ae211ecc7610faa0c
new file mode 100644
index 0000000..57dd7bd
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/ba70b97920cafb63d094f87ae211ecc7610faa0c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/bc31ed728ee5f134004f08f0379422ba3f7c69ed b/fuzz/corpus/packet_recv_server/bc31ed728ee5f134004f08f0379422ba3f7c69ed
deleted file mode 100644
index ddddb39..0000000
--- a/fuzz/corpus/packet_recv_server/bc31ed728ee5f134004f08f0379422ba3f7c69ed
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/bd1e7aeac8f23d89804f6998037c26ecabe1f349 b/fuzz/corpus/packet_recv_server/bd1e7aeac8f23d89804f6998037c26ecabe1f349
new file mode 100644
index 0000000..558385b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/bd1e7aeac8f23d89804f6998037c26ecabe1f349
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/bdc9a24a3b99716b855574b2ba29afa15c2ce698 b/fuzz/corpus/packet_recv_server/bdc9a24a3b99716b855574b2ba29afa15c2ce698
new file mode 100644
index 0000000..01c3f92
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/bdc9a24a3b99716b855574b2ba29afa15c2ce698
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/be497a50b799e777de79eebef469343e1e7e52d6 b/fuzz/corpus/packet_recv_server/be497a50b799e777de79eebef469343e1e7e52d6
new file mode 100644
index 0000000..87811ef
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/be497a50b799e777de79eebef469343e1e7e52d6
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/bed1957369630cdccc779206184f509a222213ab b/fuzz/corpus/packet_recv_server/bed1957369630cdccc779206184f509a222213ab
new file mode 100644
index 0000000..f70bcf0
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/bed1957369630cdccc779206184f509a222213ab
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/bedb441f831fbddd728acdb2d0b6ab7c25680d41 b/fuzz/corpus/packet_recv_server/bedb441f831fbddd728acdb2d0b6ab7c25680d41
new file mode 100644
index 0000000..83a7868
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/bedb441f831fbddd728acdb2d0b6ab7c25680d41
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/befd2fdb9505d2c2a3515a33341197ccb0e35839 b/fuzz/corpus/packet_recv_server/befd2fdb9505d2c2a3515a33341197ccb0e35839
new file mode 100644
index 0000000..4d6c727
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/befd2fdb9505d2c2a3515a33341197ccb0e35839
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/bf9be9f1fceddca1ca31ad71c82fca4f14342822 b/fuzz/corpus/packet_recv_server/bf9be9f1fceddca1ca31ad71c82fca4f14342822
deleted file mode 100644
index 1253268..0000000
--- a/fuzz/corpus/packet_recv_server/bf9be9f1fceddca1ca31ad71c82fca4f14342822
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/bfa5115ae3439bb056cc5d1bcb569b5bd080aed3 b/fuzz/corpus/packet_recv_server/bfa5115ae3439bb056cc5d1bcb569b5bd080aed3
new file mode 100644
index 0000000..7c511d1
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/bfa5115ae3439bb056cc5d1bcb569b5bd080aed3
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/c07b4b13a9d94dc079733f779197c936534afd13 b/fuzz/corpus/packet_recv_server/c07b4b13a9d94dc079733f779197c936534afd13
new file mode 100644
index 0000000..e772949
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/c07b4b13a9d94dc079733f779197c936534afd13
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/c0d8c850707d268c7e09ce2a9bd2908620befe0b b/fuzz/corpus/packet_recv_server/c0d8c850707d268c7e09ce2a9bd2908620befe0b
new file mode 100644
index 0000000..268b83f
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/c0d8c850707d268c7e09ce2a9bd2908620befe0b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/c287488097bbf9d00c0ff144078344b8d0b2ca34 b/fuzz/corpus/packet_recv_server/c287488097bbf9d00c0ff144078344b8d0b2ca34
deleted file mode 100644
index ddad2f9..0000000
--- a/fuzz/corpus/packet_recv_server/c287488097bbf9d00c0ff144078344b8d0b2ca34
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/c294a6480437b105c2d20f289169ce533eac50aa b/fuzz/corpus/packet_recv_server/c294a6480437b105c2d20f289169ce533eac50aa
deleted file mode 100644
index af50240..0000000
--- a/fuzz/corpus/packet_recv_server/c294a6480437b105c2d20f289169ce533eac50aa
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/c304241607ab8d3dd7c6d0511da7e01add18fff4 b/fuzz/corpus/packet_recv_server/c304241607ab8d3dd7c6d0511da7e01add18fff4
new file mode 100644
index 0000000..4c1faea
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/c304241607ab8d3dd7c6d0511da7e01add18fff4
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/c3a528a3faaa58ce92bb855c17b891d2cd93da2c b/fuzz/corpus/packet_recv_server/c3a528a3faaa58ce92bb855c17b891d2cd93da2c
deleted file mode 100644
index 3141d63..0000000
--- a/fuzz/corpus/packet_recv_server/c3a528a3faaa58ce92bb855c17b891d2cd93da2c
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/c3ebc1eb7f80cf7f51053b42e7957e72cf4c2897 b/fuzz/corpus/packet_recv_server/c3ebc1eb7f80cf7f51053b42e7957e72cf4c2897
new file mode 100644
index 0000000..73bb0a2
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/c3ebc1eb7f80cf7f51053b42e7957e72cf4c2897
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/c41a3e7b3c900c7c3a5929a33868b40c647eb4c5 b/fuzz/corpus/packet_recv_server/c41a3e7b3c900c7c3a5929a33868b40c647eb4c5
deleted file mode 100644
index 72d7b28..0000000
--- a/fuzz/corpus/packet_recv_server/c41a3e7b3c900c7c3a5929a33868b40c647eb4c5
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/c5aa9226f2d2075d5432d8e5884b403a60081b59 b/fuzz/corpus/packet_recv_server/c5aa9226f2d2075d5432d8e5884b403a60081b59
deleted file mode 100644
index b066d4f..0000000
--- a/fuzz/corpus/packet_recv_server/c5aa9226f2d2075d5432d8e5884b403a60081b59
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/c5cdc6cf337c1d52171e592335f017b1c51cfe81 b/fuzz/corpus/packet_recv_server/c5cdc6cf337c1d52171e592335f017b1c51cfe81
deleted file mode 100644
index 8211218..0000000
--- a/fuzz/corpus/packet_recv_server/c5cdc6cf337c1d52171e592335f017b1c51cfe81
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/c62bbb5323647ea8b74c3f9a00af12caf7468def b/fuzz/corpus/packet_recv_server/c62bbb5323647ea8b74c3f9a00af12caf7468def
new file mode 100644
index 0000000..657cb34
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/c62bbb5323647ea8b74c3f9a00af12caf7468def
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/c6d2632afd20a12e03f084698ff82dd859bd7c80 b/fuzz/corpus/packet_recv_server/c6d2632afd20a12e03f084698ff82dd859bd7c80
deleted file mode 100644
index ab6fa9e..0000000
--- a/fuzz/corpus/packet_recv_server/c6d2632afd20a12e03f084698ff82dd859bd7c80
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/c8450c9b638f5907a42678687d1da640c5995f4e b/fuzz/corpus/packet_recv_server/c8450c9b638f5907a42678687d1da640c5995f4e
deleted file mode 100644
index a66f46a..0000000
--- a/fuzz/corpus/packet_recv_server/c8450c9b638f5907a42678687d1da640c5995f4e
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/c9ca445850432762b4fec6202ff549b79bde364f b/fuzz/corpus/packet_recv_server/c9ca445850432762b4fec6202ff549b79bde364f
new file mode 100644
index 0000000..c2b9f36
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/c9ca445850432762b4fec6202ff549b79bde364f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/ca22c8b8af2a76bb1f364eff9445247ce9522a20 b/fuzz/corpus/packet_recv_server/ca22c8b8af2a76bb1f364eff9445247ce9522a20
new file mode 100644
index 0000000..dd5939f
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/ca22c8b8af2a76bb1f364eff9445247ce9522a20
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/ca96864e8a123d927857d797655fa005ecb52fa4 b/fuzz/corpus/packet_recv_server/ca96864e8a123d927857d797655fa005ecb52fa4
deleted file mode 100644
index 79dd93a..0000000
--- a/fuzz/corpus/packet_recv_server/ca96864e8a123d927857d797655fa005ecb52fa4
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/cb4de0ee26b38c02db52713a737ef92f384d3b1e b/fuzz/corpus/packet_recv_server/cb4de0ee26b38c02db52713a737ef92f384d3b1e
new file mode 100644
index 0000000..d14fc12
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/cb4de0ee26b38c02db52713a737ef92f384d3b1e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/cbee5f4fe445cb86795ad00615986f3fdf5e403f b/fuzz/corpus/packet_recv_server/cbee5f4fe445cb86795ad00615986f3fdf5e403f
new file mode 100644
index 0000000..cd1105e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/cbee5f4fe445cb86795ad00615986f3fdf5e403f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/cc71a1c2453ab9dcfd8f5b2f8177dbaacf50ba49 b/fuzz/corpus/packet_recv_server/cc71a1c2453ab9dcfd8f5b2f8177dbaacf50ba49
new file mode 100644
index 0000000..9252d5c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/cc71a1c2453ab9dcfd8f5b2f8177dbaacf50ba49
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/ccc12e45965372efea833187ddb1bd1ec0fe8f44 b/fuzz/corpus/packet_recv_server/ccc12e45965372efea833187ddb1bd1ec0fe8f44
new file mode 100644
index 0000000..e3bcc9c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/ccc12e45965372efea833187ddb1bd1ec0fe8f44
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/cd2d3a551b86826768af1c2fe8b9de2925c8591e b/fuzz/corpus/packet_recv_server/cd2d3a551b86826768af1c2fe8b9de2925c8591e
new file mode 100644
index 0000000..94e4f5a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/cd2d3a551b86826768af1c2fe8b9de2925c8591e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/cd9c7553939c60ef34e2efa83ae584c26f0cfb15 b/fuzz/corpus/packet_recv_server/cd9c7553939c60ef34e2efa83ae584c26f0cfb15
new file mode 100644
index 0000000..318a89d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/cd9c7553939c60ef34e2efa83ae584c26f0cfb15
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/ce04f8d935b42ceed2e62a494f114e07c94dc8f2 b/fuzz/corpus/packet_recv_server/ce04f8d935b42ceed2e62a494f114e07c94dc8f2
deleted file mode 100644
index c293d43..0000000
--- a/fuzz/corpus/packet_recv_server/ce04f8d935b42ceed2e62a494f114e07c94dc8f2
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/cf61f2c3d45c25bf354788ed4cd229c339625546 b/fuzz/corpus/packet_recv_server/cf61f2c3d45c25bf354788ed4cd229c339625546
new file mode 100644
index 0000000..67463ae
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/cf61f2c3d45c25bf354788ed4cd229c339625546
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/cf641a0532c2af2670baf32e253db36b52def6e5 b/fuzz/corpus/packet_recv_server/cf641a0532c2af2670baf32e253db36b52def6e5
new file mode 100644
index 0000000..ea0a667
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/cf641a0532c2af2670baf32e253db36b52def6e5
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/d088db21bfdbf67652297b7b9e217c825a2669b0 b/fuzz/corpus/packet_recv_server/d088db21bfdbf67652297b7b9e217c825a2669b0
new file mode 100644
index 0000000..87ee4f2
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/d088db21bfdbf67652297b7b9e217c825a2669b0
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/d1484a57b29ff92a5a6f8db46a1fd94105a9a702 b/fuzz/corpus/packet_recv_server/d1484a57b29ff92a5a6f8db46a1fd94105a9a702
new file mode 100644
index 0000000..e7240e5
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/d1484a57b29ff92a5a6f8db46a1fd94105a9a702
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/d19c95d427add30a2b385b9223fbc457e3e28f4c b/fuzz/corpus/packet_recv_server/d19c95d427add30a2b385b9223fbc457e3e28f4c
new file mode 100644
index 0000000..d7faa50
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/d19c95d427add30a2b385b9223fbc457e3e28f4c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/d1a75322cae494d27d2039ed8ad036db97e643a0 b/fuzz/corpus/packet_recv_server/d1a75322cae494d27d2039ed8ad036db97e643a0
new file mode 100644
index 0000000..e97b8e0
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/d1a75322cae494d27d2039ed8ad036db97e643a0
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/d1f52fa64078bb865f4c12569841d62d1857673a b/fuzz/corpus/packet_recv_server/d1f52fa64078bb865f4c12569841d62d1857673a
new file mode 100644
index 0000000..77f5892
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/d1f52fa64078bb865f4c12569841d62d1857673a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/d219ded0b0051cf8cafc4ef4c3b5b3c0f34a4113 b/fuzz/corpus/packet_recv_server/d219ded0b0051cf8cafc4ef4c3b5b3c0f34a4113
new file mode 100644
index 0000000..e32cd3e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/d219ded0b0051cf8cafc4ef4c3b5b3c0f34a4113
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/d38424049f508b76e919d2bf5f37f406e2edb137 b/fuzz/corpus/packet_recv_server/d38424049f508b76e919d2bf5f37f406e2edb137
deleted file mode 100644
index f8d10bf..0000000
--- a/fuzz/corpus/packet_recv_server/d38424049f508b76e919d2bf5f37f406e2edb137
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/d3d06481dfe30994aef144847ee7079d9dee7cde b/fuzz/corpus/packet_recv_server/d3d06481dfe30994aef144847ee7079d9dee7cde
deleted file mode 100644
index 8230e22..0000000
--- a/fuzz/corpus/packet_recv_server/d3d06481dfe30994aef144847ee7079d9dee7cde
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/d3d786c6329ed30c525445821f5b7fda74598ec1 b/fuzz/corpus/packet_recv_server/d3d786c6329ed30c525445821f5b7fda74598ec1
new file mode 100644
index 0000000..170616b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/d3d786c6329ed30c525445821f5b7fda74598ec1
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/d422f45d669233336bb3e5c7b318fa334245203d b/fuzz/corpus/packet_recv_server/d422f45d669233336bb3e5c7b318fa334245203d
deleted file mode 100644
index c590ebb..0000000
--- a/fuzz/corpus/packet_recv_server/d422f45d669233336bb3e5c7b318fa334245203d
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/d4e4422dd8b858d2b78163e59115fe7ce21706be b/fuzz/corpus/packet_recv_server/d4e4422dd8b858d2b78163e59115fe7ce21706be
deleted file mode 100644
index 5f358a1..0000000
--- a/fuzz/corpus/packet_recv_server/d4e4422dd8b858d2b78163e59115fe7ce21706be
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/d5cdde85d8141482229774f0644bc365ee73d345 b/fuzz/corpus/packet_recv_server/d5cdde85d8141482229774f0644bc365ee73d345
deleted file mode 100644
index a96746c..0000000
--- a/fuzz/corpus/packet_recv_server/d5cdde85d8141482229774f0644bc365ee73d345
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/d798c77c76dbbc7cd40c5e852f86afd18ee5b355 b/fuzz/corpus/packet_recv_server/d798c77c76dbbc7cd40c5e852f86afd18ee5b355
deleted file mode 100644
index 05d25bb..0000000
--- a/fuzz/corpus/packet_recv_server/d798c77c76dbbc7cd40c5e852f86afd18ee5b355
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/d7fc3683706467674ff4ede48f114b051d68c485 b/fuzz/corpus/packet_recv_server/d7fc3683706467674ff4ede48f114b051d68c485
new file mode 100644
index 0000000..f2ac76b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/d7fc3683706467674ff4ede48f114b051d68c485
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/d90c83a018125891d25493a140d40d2f88a04f2d b/fuzz/corpus/packet_recv_server/d90c83a018125891d25493a140d40d2f88a04f2d
new file mode 100644
index 0000000..beebb2d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/d90c83a018125891d25493a140d40d2f88a04f2d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/d9412e3408d5fc77ccaffbb5a0e313e67bc48ecd b/fuzz/corpus/packet_recv_server/d9412e3408d5fc77ccaffbb5a0e313e67bc48ecd
deleted file mode 100644
index 96d4326..0000000
--- a/fuzz/corpus/packet_recv_server/d9412e3408d5fc77ccaffbb5a0e313e67bc48ecd
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/d95f28b20fbcd90a4d9aa0defe9c9e4ea23f3c60 b/fuzz/corpus/packet_recv_server/d95f28b20fbcd90a4d9aa0defe9c9e4ea23f3c60
new file mode 100644
index 0000000..319c4dc
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/d95f28b20fbcd90a4d9aa0defe9c9e4ea23f3c60
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/d9f2d84a18d52d215fce1292dd5f53bc4cd6b17f b/fuzz/corpus/packet_recv_server/d9f2d84a18d52d215fce1292dd5f53bc4cd6b17f
deleted file mode 100644
index b3a85a0..0000000
--- a/fuzz/corpus/packet_recv_server/d9f2d84a18d52d215fce1292dd5f53bc4cd6b17f
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/da26c7d0f894510063ede0a03b6b4d3002784497 b/fuzz/corpus/packet_recv_server/da26c7d0f894510063ede0a03b6b4d3002784497
new file mode 100644
index 0000000..f19fc4c
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/da26c7d0f894510063ede0a03b6b4d3002784497
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/dbad43f1d28913e9679f0eb3d0e5d036254f1d4c b/fuzz/corpus/packet_recv_server/dbad43f1d28913e9679f0eb3d0e5d036254f1d4c
new file mode 100644
index 0000000..369e450
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/dbad43f1d28913e9679f0eb3d0e5d036254f1d4c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/dd53682e63b9269c26268e6239dba6fe47c41f6f b/fuzz/corpus/packet_recv_server/dd53682e63b9269c26268e6239dba6fe47c41f6f
deleted file mode 100644
index dace137..0000000
--- a/fuzz/corpus/packet_recv_server/dd53682e63b9269c26268e6239dba6fe47c41f6f
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/de861986294e0eed9cebba28da18a7e9994808ec b/fuzz/corpus/packet_recv_server/de861986294e0eed9cebba28da18a7e9994808ec
deleted file mode 100644
index 1fe1378..0000000
--- a/fuzz/corpus/packet_recv_server/de861986294e0eed9cebba28da18a7e9994808ec
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/df00e2cac5133c5bdeea3facc71ba8fe4dd3d2f3 b/fuzz/corpus/packet_recv_server/df00e2cac5133c5bdeea3facc71ba8fe4dd3d2f3
new file mode 100644
index 0000000..9365ce6
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/df00e2cac5133c5bdeea3facc71ba8fe4dd3d2f3
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/e029df7ffd9761cd258f891e1fc19b0841953cb4 b/fuzz/corpus/packet_recv_server/e029df7ffd9761cd258f891e1fc19b0841953cb4
deleted file mode 100644
index 1aa32f7..0000000
--- a/fuzz/corpus/packet_recv_server/e029df7ffd9761cd258f891e1fc19b0841953cb4
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/e0c46405569bf24aabda8f2a4253aec1f63a5e7d b/fuzz/corpus/packet_recv_server/e0c46405569bf24aabda8f2a4253aec1f63a5e7d
new file mode 100644
index 0000000..1aad594
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/e0c46405569bf24aabda8f2a4253aec1f63a5e7d
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/e29b825721218eec71e3ee33a71a25fb9993deb6 b/fuzz/corpus/packet_recv_server/e29b825721218eec71e3ee33a71a25fb9993deb6
new file mode 100644
index 0000000..40f4f84
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/e29b825721218eec71e3ee33a71a25fb9993deb6
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/e31c3c16e3fe75175f58c8da5cf88f796cf59e9a b/fuzz/corpus/packet_recv_server/e31c3c16e3fe75175f58c8da5cf88f796cf59e9a
deleted file mode 100644
index c6c45d1..0000000
--- a/fuzz/corpus/packet_recv_server/e31c3c16e3fe75175f58c8da5cf88f796cf59e9a
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/e3f1344a559c489a45e2a9be028d703640652b95 b/fuzz/corpus/packet_recv_server/e3f1344a559c489a45e2a9be028d703640652b95
deleted file mode 100644
index afcb08b..0000000
--- a/fuzz/corpus/packet_recv_server/e3f1344a559c489a45e2a9be028d703640652b95
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/e468308fb27463211e96b889438636e52c6d5a75 b/fuzz/corpus/packet_recv_server/e468308fb27463211e96b889438636e52c6d5a75
new file mode 100644
index 0000000..5e117f5
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/e468308fb27463211e96b889438636e52c6d5a75
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/e4ef59e9ab00a53c25a805cd85f5063ba8213d2c b/fuzz/corpus/packet_recv_server/e4ef59e9ab00a53c25a805cd85f5063ba8213d2c
new file mode 100644
index 0000000..676088a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/e4ef59e9ab00a53c25a805cd85f5063ba8213d2c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/e5c847522c2b2eababce8f4d81a87cfd8a8da81e b/fuzz/corpus/packet_recv_server/e5c847522c2b2eababce8f4d81a87cfd8a8da81e
deleted file mode 100644
index a207425..0000000
--- a/fuzz/corpus/packet_recv_server/e5c847522c2b2eababce8f4d81a87cfd8a8da81e
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/e5f2af5e9d67409ee011620f528514237ab62a59 b/fuzz/corpus/packet_recv_server/e5f2af5e9d67409ee011620f528514237ab62a59
new file mode 100644
index 0000000..8ec1bd0
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/e5f2af5e9d67409ee011620f528514237ab62a59
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/e7196d8bfbd208a23e835699b6f4209840c4a683 b/fuzz/corpus/packet_recv_server/e7196d8bfbd208a23e835699b6f4209840c4a683
deleted file mode 100644
index d5d57fc..0000000
--- a/fuzz/corpus/packet_recv_server/e7196d8bfbd208a23e835699b6f4209840c4a683
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/e7894bd2bcd3a032e0de4dc7426ad0f01bf4762c b/fuzz/corpus/packet_recv_server/e7894bd2bcd3a032e0de4dc7426ad0f01bf4762c
deleted file mode 100644
index b581d7d..0000000
--- a/fuzz/corpus/packet_recv_server/e7894bd2bcd3a032e0de4dc7426ad0f01bf4762c
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/e9baadb434dd25b96c926d56c3fcba1795ca2657 b/fuzz/corpus/packet_recv_server/e9baadb434dd25b96c926d56c3fcba1795ca2657
deleted file mode 100644
index bd98b59..0000000
--- a/fuzz/corpus/packet_recv_server/e9baadb434dd25b96c926d56c3fcba1795ca2657
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/ea6a3b9f019e5274b31a64d227f44f0b0db38e15 b/fuzz/corpus/packet_recv_server/ea6a3b9f019e5274b31a64d227f44f0b0db38e15
new file mode 100644
index 0000000..2f575e9
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/ea6a3b9f019e5274b31a64d227f44f0b0db38e15
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/ea9e259b1ceab79438784c373b3768d90332e008 b/fuzz/corpus/packet_recv_server/ea9e259b1ceab79438784c373b3768d90332e008
new file mode 100644
index 0000000..d70c312
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/ea9e259b1ceab79438784c373b3768d90332e008
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/eaf96e33d7d72f56648bb585535bfc5e37a04402 b/fuzz/corpus/packet_recv_server/eaf96e33d7d72f56648bb585535bfc5e37a04402
new file mode 100644
index 0000000..476b860
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/eaf96e33d7d72f56648bb585535bfc5e37a04402
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/ec6560aaec7da1079240091bbd9f607551d5c800 b/fuzz/corpus/packet_recv_server/ec6560aaec7da1079240091bbd9f607551d5c800
new file mode 100644
index 0000000..73f354b
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/ec6560aaec7da1079240091bbd9f607551d5c800
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/ecc9928ced516610601d3474c4191fdf74d0df13 b/fuzz/corpus/packet_recv_server/ecc9928ced516610601d3474c4191fdf74d0df13
new file mode 100644
index 0000000..7d1c3a8
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/ecc9928ced516610601d3474c4191fdf74d0df13
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/ee30909e647f524e3521d488537d0a1ae9db7750 b/fuzz/corpus/packet_recv_server/ee30909e647f524e3521d488537d0a1ae9db7750
new file mode 100644
index 0000000..f058743
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/ee30909e647f524e3521d488537d0a1ae9db7750
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/ef01d68875b8a956c01b2d3aa45d7eb2c45e252b b/fuzz/corpus/packet_recv_server/ef01d68875b8a956c01b2d3aa45d7eb2c45e252b
new file mode 100644
index 0000000..b99713a
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/ef01d68875b8a956c01b2d3aa45d7eb2c45e252b
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/effc0e6a61de731b57eb080d48c6c79a4731226f b/fuzz/corpus/packet_recv_server/effc0e6a61de731b57eb080d48c6c79a4731226f
new file mode 100644
index 0000000..4fbe27d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/effc0e6a61de731b57eb080d48c6c79a4731226f
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/f071bdf64a6869b89c0465c1b4bb2553ebc291f8 b/fuzz/corpus/packet_recv_server/f071bdf64a6869b89c0465c1b4bb2553ebc291f8
new file mode 100644
index 0000000..8ddaca1
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/f071bdf64a6869b89c0465c1b4bb2553ebc291f8
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/f0ecb2aad9ffea41388397e70fc6077f5b6abb1e b/fuzz/corpus/packet_recv_server/f0ecb2aad9ffea41388397e70fc6077f5b6abb1e
new file mode 100644
index 0000000..128f067
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/f0ecb2aad9ffea41388397e70fc6077f5b6abb1e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/f10d0d0932528d9e5e04f4839073a1da2097a2a6 b/fuzz/corpus/packet_recv_server/f10d0d0932528d9e5e04f4839073a1da2097a2a6
new file mode 100644
index 0000000..2530b5f
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/f10d0d0932528d9e5e04f4839073a1da2097a2a6
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/f1aa0cc7029a930bdb70a9d8977db850fa4d1cb5 b/fuzz/corpus/packet_recv_server/f1aa0cc7029a930bdb70a9d8977db850fa4d1cb5
deleted file mode 100644
index eeccc12..0000000
--- a/fuzz/corpus/packet_recv_server/f1aa0cc7029a930bdb70a9d8977db850fa4d1cb5
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/f1fae1acc7d8ddadcc8f1720cc2d142cadbc967a b/fuzz/corpus/packet_recv_server/f1fae1acc7d8ddadcc8f1720cc2d142cadbc967a
new file mode 100644
index 0000000..cc4eace
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/f1fae1acc7d8ddadcc8f1720cc2d142cadbc967a
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/f297dd9e329757a53385826df7e6813ab380e4c3 b/fuzz/corpus/packet_recv_server/f297dd9e329757a53385826df7e6813ab380e4c3
new file mode 100644
index 0000000..35943cd
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/f297dd9e329757a53385826df7e6813ab380e4c3
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/f38e3f7d2fd2d59cb1ec775e1f8e23747042d05c b/fuzz/corpus/packet_recv_server/f38e3f7d2fd2d59cb1ec775e1f8e23747042d05c
new file mode 100644
index 0000000..70e3d1e
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/f38e3f7d2fd2d59cb1ec775e1f8e23747042d05c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/f40b5b9dbc811cf1fcd7763b7e6a1503308b7e40 b/fuzz/corpus/packet_recv_server/f40b5b9dbc811cf1fcd7763b7e6a1503308b7e40
new file mode 100644
index 0000000..49080e8
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/f40b5b9dbc811cf1fcd7763b7e6a1503308b7e40
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/f4aceaf8a274680ce4d995a1db7824a6d1995c4d b/fuzz/corpus/packet_recv_server/f4aceaf8a274680ce4d995a1db7824a6d1995c4d
deleted file mode 100644
index 8f8b8a5..0000000
--- a/fuzz/corpus/packet_recv_server/f4aceaf8a274680ce4d995a1db7824a6d1995c4d
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/f5e41f37a7b537fb9f95b655935650f8be92d4e7 b/fuzz/corpus/packet_recv_server/f5e41f37a7b537fb9f95b655935650f8be92d4e7
deleted file mode 100644
index 2091d7e..0000000
--- a/fuzz/corpus/packet_recv_server/f5e41f37a7b537fb9f95b655935650f8be92d4e7
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/f7667bb58832ed95dd1dda71236031ed239c8985 b/fuzz/corpus/packet_recv_server/f7667bb58832ed95dd1dda71236031ed239c8985
new file mode 100644
index 0000000..0d24828
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/f7667bb58832ed95dd1dda71236031ed239c8985
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/f7998b29455e5a39cf4f6adf808ae70ee03a3c8a b/fuzz/corpus/packet_recv_server/f7998b29455e5a39cf4f6adf808ae70ee03a3c8a
deleted file mode 100644
index 7e72bfc..0000000
--- a/fuzz/corpus/packet_recv_server/f7998b29455e5a39cf4f6adf808ae70ee03a3c8a
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/f7dea16b66f836a47990364e83b0b7df1f5078af b/fuzz/corpus/packet_recv_server/f7dea16b66f836a47990364e83b0b7df1f5078af
deleted file mode 100644
index 3615697..0000000
--- a/fuzz/corpus/packet_recv_server/f7dea16b66f836a47990364e83b0b7df1f5078af
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/f7e4547400b6a45463bc8d44e02e18c290ae310f b/fuzz/corpus/packet_recv_server/f7e4547400b6a45463bc8d44e02e18c290ae310f
deleted file mode 100644
index 1729c82..0000000
--- a/fuzz/corpus/packet_recv_server/f7e4547400b6a45463bc8d44e02e18c290ae310f
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/f8d06a8648fa30cb76adc13fcbf3cd14fc0e17aa b/fuzz/corpus/packet_recv_server/f8d06a8648fa30cb76adc13fcbf3cd14fc0e17aa
new file mode 100644
index 0000000..60501a2
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/f8d06a8648fa30cb76adc13fcbf3cd14fc0e17aa
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/f8ef20fdac285e55549670532b5b3a7a2b9313a6 b/fuzz/corpus/packet_recv_server/f8ef20fdac285e55549670532b5b3a7a2b9313a6
deleted file mode 100644
index d6b7f3e..0000000
--- a/fuzz/corpus/packet_recv_server/f8ef20fdac285e55549670532b5b3a7a2b9313a6
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/f978cc5000cfb41c6572f3a839b0121149e879b7 b/fuzz/corpus/packet_recv_server/f978cc5000cfb41c6572f3a839b0121149e879b7
deleted file mode 100644
index 1fc6dec..0000000
--- a/fuzz/corpus/packet_recv_server/f978cc5000cfb41c6572f3a839b0121149e879b7
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/fabb111286dc9e48f9ac460b0f3db9a3e17b191e b/fuzz/corpus/packet_recv_server/fabb111286dc9e48f9ac460b0f3db9a3e17b191e
new file mode 100644
index 0000000..6cf6fa0
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/fabb111286dc9e48f9ac460b0f3db9a3e17b191e
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/fb279bd1c5bf79882fdf4a8c5629d2ccffe19348 b/fuzz/corpus/packet_recv_server/fb279bd1c5bf79882fdf4a8c5629d2ccffe19348
new file mode 100644
index 0000000..ebbb8ae
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/fb279bd1c5bf79882fdf4a8c5629d2ccffe19348
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/fbe424a7957cee26b237258e43145c89c5ff8cbb b/fuzz/corpus/packet_recv_server/fbe424a7957cee26b237258e43145c89c5ff8cbb
new file mode 100644
index 0000000..c478415
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/fbe424a7957cee26b237258e43145c89c5ff8cbb
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/fbe6b699b869203039e2ffce37b2d9a81b08a343 b/fuzz/corpus/packet_recv_server/fbe6b699b869203039e2ffce37b2d9a81b08a343
new file mode 100644
index 0000000..8c08205
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/fbe6b699b869203039e2ffce37b2d9a81b08a343
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/fc0cad96551998b6e34d6ff6e2b1ee838e22407c b/fuzz/corpus/packet_recv_server/fc0cad96551998b6e34d6ff6e2b1ee838e22407c
deleted file mode 100644
index b9613d5..0000000
--- a/fuzz/corpus/packet_recv_server/fc0cad96551998b6e34d6ff6e2b1ee838e22407c
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/fc711e084ccdac3df90c205e23ef4e33fb5929d4 b/fuzz/corpus/packet_recv_server/fc711e084ccdac3df90c205e23ef4e33fb5929d4
new file mode 100644
index 0000000..eecea36
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/fc711e084ccdac3df90c205e23ef4e33fb5929d4
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/fd3d344d2990ed5e6cf8fb0b86c68c0cb4fa3c18 b/fuzz/corpus/packet_recv_server/fd3d344d2990ed5e6cf8fb0b86c68c0cb4fa3c18
deleted file mode 100644
index c23857d..0000000
--- a/fuzz/corpus/packet_recv_server/fd3d344d2990ed5e6cf8fb0b86c68c0cb4fa3c18
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/fd54567dce80d6a21fc95ff0e3602fad8267b73c b/fuzz/corpus/packet_recv_server/fd54567dce80d6a21fc95ff0e3602fad8267b73c
new file mode 100644
index 0000000..e31ee8d
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/fd54567dce80d6a21fc95ff0e3602fad8267b73c
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/fddda779ad24fc620b58692abd59f6531d0632ca b/fuzz/corpus/packet_recv_server/fddda779ad24fc620b58692abd59f6531d0632ca
deleted file mode 100644
index aeefb5e..0000000
--- a/fuzz/corpus/packet_recv_server/fddda779ad24fc620b58692abd59f6531d0632ca
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/fe2dca1c603261c9e7dff536f7c2554921cbe9b0 b/fuzz/corpus/packet_recv_server/fe2dca1c603261c9e7dff536f7c2554921cbe9b0
new file mode 100644
index 0000000..46cecce
--- /dev/null
+++ b/fuzz/corpus/packet_recv_server/fe2dca1c603261c9e7dff536f7c2554921cbe9b0
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/feb8798e69ffa69ef46b3a0781b3c6beac89a231 b/fuzz/corpus/packet_recv_server/feb8798e69ffa69ef46b3a0781b3c6beac89a231
deleted file mode 100644
index 5e17738..0000000
--- a/fuzz/corpus/packet_recv_server/feb8798e69ffa69ef46b3a0781b3c6beac89a231
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/ff38d421772c88784a9ca113e446bfc69287b6a1 b/fuzz/corpus/packet_recv_server/ff38d421772c88784a9ca113e446bfc69287b6a1
deleted file mode 100644
index 48b83c8..0000000
--- a/fuzz/corpus/packet_recv_server/ff38d421772c88784a9ca113e446bfc69287b6a1
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/packet_recv_server/ffa4be62bfb54c33846d2d9f64bd4945844acdde b/fuzz/corpus/packet_recv_server/ffa4be62bfb54c33846d2d9f64bd4945844acdde
deleted file mode 100644
index db6d969..0000000
--- a/fuzz/corpus/packet_recv_server/ffa4be62bfb54c33846d2d9f64bd4945844acdde
+++ /dev/null
Binary files differ
diff --git a/fuzz/corpus/qpack_decode/aaf76b4fd678d56c3b6c4d95a0635ff84d33f7c1 b/fuzz/corpus/qpack_decode/aaf76b4fd678d56c3b6c4d95a0635ff84d33f7c1
deleted file mode 100644
index e90aa84..0000000
--- a/fuzz/corpus/qpack_decode/aaf76b4fd678d56c3b6c4d95a0635ff84d33f7c1
+++ /dev/null
@@ -1 +0,0 @@
-óÏÁÏØ0
\ No newline at end of file
diff --git a/fuzz/src/qpack_decode.rs b/fuzz/src/qpack_decode.rs
index 50d650c..28266c3 100644
--- a/fuzz/src/qpack_decode.rs
+++ b/fuzz/src/qpack_decode.rs
@@ -3,9 +3,26 @@
#[macro_use]
extern crate libfuzzer_sys;
+// Fuzzer for qpack codec. Checks that decode(encode(hdrs)) == hdrs. To get the
+// initial hdrs, the fuzzer deserializes the input, and skips inputs where
+// deserialization fails.
+//
+// The fuzzer could have been written to instead check encode(decode(input)) ==
+// input. However, that transformation is not guaranteed to be the identify
+// function, as there are multiple ways the same hdr list could be encoded.
fuzz_target!(|data: &[u8]| {
- let mut buf = data.to_vec();
let mut decoder = quiche::h3::qpack::Decoder::new();
+ let mut encoder = quiche::h3::qpack::Encoder::new();
+ let hdrs = match decoder.decode(&mut data.to_vec(), std::u64::MAX) {
+ Err(_) => return,
+ Ok(hdrs) => hdrs,
+ };
+ let mut encoded_hdrs = vec![0; data.len() * 10 + 1000];
+ let encoded_size = encoder.encode(&hdrs, &mut encoded_hdrs).unwrap();
- decoder.decode(&mut buf, std::u64::MAX).ok();
+ let decoded_hdrs = decoder
+ .decode(&mut encoded_hdrs[..encoded_size], std::u64::MAX)
+ .unwrap();
+
+ assert_eq!(hdrs, decoded_hdrs)
});
diff --git a/include/quiche.h b/include/quiche.h
index f346c7b..b366ce4 100644
--- a/include/quiche.h
+++ b/include/quiche.h
@@ -1,5 +1,4 @@
-// Copyright (C) 2018, Cloudflare, Inc.
-// Copyright (C) 2018, Alessandro Ghedini
+// Copyright (C) 2018-2019, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -39,7 +38,7 @@
//
// The current QUIC wire version.
-#define QUICHE_PROTOCOL_VERSION 0xff000017
+#define QUICHE_PROTOCOL_VERSION 0xff000019
// The maximum length of a connection ID.
#define QUICHE_MAX_CONN_ID_LEN 20
@@ -89,6 +88,9 @@
// The received data exceeds the stream's final size.
QUICHE_ERR_FINAL_SIZE = -13,
+
+ // Error in congestion control.
+ QUICHE_ERR_CONGESTION_CONTROL = -14,
};
// Returns a human readable string with the quiche version number.
@@ -96,7 +98,7 @@
// Enables logging. |cb| will be called with log messages
int quiche_enable_debug_logging(void (*cb)(const char *line, void *argp),
- void *argp);
+ void *argp);
// Stores configuration shared between multiple connections.
typedef struct Config quiche_config;
@@ -121,13 +123,16 @@
// Enables logging of secrets.
void quiche_config_log_keys(quiche_config *config);
+// Enables sending or receiving early data.
+void quiche_config_enable_early_data(quiche_config *config);
+
// Configures the list of supported application protocols.
int quiche_config_set_application_protos(quiche_config *config,
const uint8_t *protos,
size_t protos_len);
-// Sets the `idle_timeout` transport parameter.
-void quiche_config_set_idle_timeout(quiche_config *config, uint64_t v);
+// Sets the `max_idle_timeout` transport parameter.
+void quiche_config_set_max_idle_timeout(quiche_config *config, uint64_t v);
// Sets the `max_packet_size` transport parameter.
void quiche_config_set_max_packet_size(quiche_config *config, uint64_t v);
@@ -159,6 +164,13 @@
// Sets the `disable_active_migration` transport parameter.
void quiche_config_set_disable_active_migration(quiche_config *config, bool v);
+enum quiche_cc_algorithm {
+ QUICHE_CC_RENO = 0,
+};
+
+// Sets the congestion control algorithm used.
+void quiche_config_set_cc_algorithm(quiche_config *config, enum quiche_cc_algorithm algo);
+
// Frees the config object.
void quiche_config_free(quiche_config *config);
@@ -194,6 +206,9 @@
const uint8_t *token, size_t token_len,
uint8_t *out, size_t out_len);
+// Returns true if the given protocol version is supported.
+bool quiche_version_is_supported(uint32_t version);
+
quiche_conn *quiche_conn_new_with_tls(const uint8_t *scid, size_t scid_len,
const uint8_t *odcid, size_t odcid_len,
quiche_config *config, void *ssl,
@@ -258,9 +273,26 @@
// Returns true if the connection handshake is complete.
bool quiche_conn_is_established(quiche_conn *conn);
+// Returns true if the connection has a pending handshake that has progressed
+// enough to send or receive early data.
+bool quiche_conn_is_in_early_data(quiche_conn *conn);
+
// Returns true if the connection is closed.
bool quiche_conn_is_closed(quiche_conn *conn);
+// Initializes the stream's application data.
+//
+// Stream data can only be initialized once. Additional calls to this method
+// will fail.
+//
+// Note that the application is responsible for freeing the data.
+int quiche_conn_stream_init_application_data(quiche_conn *conn,
+ uint64_t stream_id,
+ void *data);
+
+// Returns the stream's application data, if any was initialized.
+void *quiche_conn_stream_application_data(quiche_conn *conn, uint64_t stream_id);
+
// Fetches the next stream from the given iterator. Returns false if there are
// no more elements in the iterator.
bool quiche_stream_iter_next(quiche_stream_iter *iter, uint64_t *stream_id);
@@ -295,8 +327,8 @@
// HTTP/3 API
//
-/// The current HTTP/3 ALPN token.
-#define QUICHE_H3_APPLICATION_PROTOCOL "\x05h3-23"
+// List of ALPN tokens of supported HTTP/3 versions.
+#define QUICHE_H3_APPLICATION_PROTOCOL "\x05h3-25\x05h3-24\x05h3-23"
// Stores configuration shared between multiple connections.
typedef struct Http3Config quiche_h3_config;
@@ -321,7 +353,7 @@
// Creates a new server-side connection.
quiche_h3_conn *quiche_h3_accept(quiche_conn *quiche_conn,
- quiche_h3_config *config);
+ quiche_h3_config *config);
// Creates a new HTTP/3 connection using the provided QUIC connection.
quiche_h3_conn *quiche_h3_conn_new_with_transport(quiche_conn *quiche_conn,
@@ -344,15 +376,19 @@
// Iterates over the headers in the event.
//
-// The `cb` callback will be called for each header in `ev`. If `cb` returns
-// any value other than `0`, processing will be interrupted and the value is
-// returned to the caller.
+// The `cb` callback will be called for each header in `ev`. `cb` should check
+// the validity of pseudo-headers and headers. If `cb` returns any value other
+// than `0`, processing will be interrupted and the value is returned to the
+// caller.
int quiche_h3_event_for_each_header(quiche_h3_event *ev,
int (*cb)(uint8_t *name, size_t name_len,
uint8_t *value, size_t value_len,
void *argp),
void *argp);
+// Check whether data will follow the headers on the stream.
+bool quiche_h3_event_headers_has_body(quiche_h3_event *ev);
+
// Frees the HTTP/3 event object.
void quiche_h3_event_free(quiche_h3_event *ev);
diff --git a/src/build.rs b/src/build.rs
index a0e2ee5..1a0080f 100644
--- a/src/build.rs
+++ b/src/build.rs
@@ -1,37 +1,31 @@
// Additional parameters for Android build of BoringSSL.
-const CMAKE_PARAMS_ANDROID: &[(&str, &[(&str, &str)])] = &[
- ("aarch64", &[
- ("ANDROID_TOOLCHAIN_NAME", "aarch64-linux-android-4.9"),
- ("ANDROID_NATIVE_API_LEVEL", "21"),
- (
- "CMAKE_TOOLCHAIN_FILE",
- "${ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake",
- ),
- ]),
- ("arm", &[
- ("ANDROID_TOOLCHAIN_NAME", "arm-linux-androideabi-4.9"),
- ("ANDROID_NATIVE_API_LEVEL", "21"),
- (
- "CMAKE_TOOLCHAIN_FILE",
- "${ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake",
- ),
- ]),
- ("x86", &[
- ("ANDROID_TOOLCHAIN_NAME", "x86-linux-android-4.9"),
- ("ANDROID_NATIVE_API_LEVEL", "21"),
- (
- "CMAKE_TOOLCHAIN_FILE",
- "${ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake",
- ),
- ]),
- ("x86_64", &[
- ("ANDROID_TOOLCHAIN_NAME", "x86_64-linux-android-4.9"),
- ("ANDROID_NATIVE_API_LEVEL", "21"),
- (
- "CMAKE_TOOLCHAIN_FILE",
- "${ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake",
- ),
- ]),
+//
+// Android NDK < 18 with GCC.
+const CMAKE_PARAMS_ANDROID_NDK_OLD_GCC: &[(&str, &[(&str, &str)])] = &[
+ ("aarch64", &[(
+ "ANDROID_TOOLCHAIN_NAME",
+ "aarch64-linux-android-4.9",
+ )]),
+ ("arm", &[(
+ "ANDROID_TOOLCHAIN_NAME",
+ "arm-linux-androideabi-4.9",
+ )]),
+ ("x86", &[(
+ "ANDROID_TOOLCHAIN_NAME",
+ "x86-linux-android-4.9",
+ )]),
+ ("x86_64", &[(
+ "ANDROID_TOOLCHAIN_NAME",
+ "x86_64-linux-android-4.9",
+ )]),
+];
+
+// Android NDK >= 19.
+const CMAKE_PARAMS_ANDROID_NDK: &[(&str, &[(&str, &str)])] = &[
+ ("aarch64", &[("ANDROID_ABI", "arm64-v8a")]),
+ ("arm", &[("ANDROID_ABI", "armeabi-v7a")]),
+ ("x86", &[("ANDROID_ABI", "x86")]),
+ ("x86_64", &[("ANDROID_ABI", "x86_64")]),
];
const CMAKE_PARAMS_IOS: &[(&str, &[(&str, &str)])] = &[
@@ -61,13 +55,13 @@
fn get_boringssl_platform_output_path(lib: &str) -> String {
if cfg!(windows) {
if cfg!(debug_assertions) {
- return format!("{}/Debug", lib);
+ format!("{}/Debug", lib)
} else {
- return format!("{}/RelWithDebInfo", lib);
+ format!("{}/RelWithDebInfo", lib)
}
} else {
- return format!("{}", lib);
- };
+ lib.to_string()
+ }
}
/// Returns a new cmake::Config for building BoringSSL.
@@ -76,25 +70,40 @@
fn get_boringssl_cmake_config() -> cmake::Config {
let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
let os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
+ let pwd = std::env::current_dir().unwrap();
let mut boringssl_cmake = cmake::Config::new("deps/boringssl");
// Add platform-specific parameters.
- return match os.as_ref() {
+ match os.as_ref() {
"android" => {
+ let cmake_params_android = if cfg!(feature = "ndk-old-gcc") {
+ CMAKE_PARAMS_ANDROID_NDK_OLD_GCC
+ } else {
+ CMAKE_PARAMS_ANDROID_NDK
+ };
+
// We need ANDROID_NDK_HOME to be set properly.
let android_ndk_home = std::env::var("ANDROID_NDK_HOME")
.expect("Please set ANDROID_NDK_HOME for Android build");
- for (android_arch, params) in CMAKE_PARAMS_ANDROID {
+ let android_ndk_home = std::path::Path::new(&android_ndk_home);
+ for (android_arch, params) in cmake_params_android {
if *android_arch == arch {
for (name, value) in *params {
- let value = value
- .replace("${ANDROID_NDK_HOME}", &android_ndk_home);
eprintln!("android arch={} add {}={}", arch, name, value);
boringssl_cmake.define(name, value);
}
}
}
+ let toolchain_file =
+ android_ndk_home.join("build/cmake/android.toolchain.cmake");
+ let toolchain_file = toolchain_file.to_str().unwrap();
+ eprintln!("android toolchain={}", toolchain_file);
+ boringssl_cmake.define("CMAKE_TOOLCHAIN_FILE", toolchain_file);
+
+ // 21 is the minimum level tested. You can give higher value.
+ boringssl_cmake.define("ANDROID_NATIVE_API_LEVEL", "21");
+ boringssl_cmake.define("ANDROID_STL", "c++_shared");
boringssl_cmake
},
@@ -116,8 +125,19 @@
boringssl_cmake
},
- _ => boringssl_cmake,
- };
+ _ => {
+ // Configure BoringSSL for building on 32-bit non-windows platforms.
+ if arch == "x86" && os != "windows" {
+ boringssl_cmake.define(
+ "CMAKE_TOOLCHAIN_FILE",
+ pwd.join("deps/boringssl/util/32-bit-toolchain.cmake")
+ .as_os_str(),
+ );
+ }
+
+ boringssl_cmake
+ },
+ }
}
fn write_pkg_config() {
@@ -176,6 +196,11 @@
println!("cargo:rustc-link-lib=static=ssl");
}
+ // MacOS: Allow cdylib to link with undefined symbols
+ if cfg!(target_os = "macos") {
+ println!("cargo:rustc-cdylib-link-arg=-Wl,-undefined,dynamic_lookup");
+ }
+
if cfg!(feature = "pkg-config-meta") {
write_pkg_config();
}
diff --git a/src/cc/mod.rs b/src/cc/mod.rs
new file mode 100644
index 0000000..3acbfb0
--- /dev/null
+++ b/src/cc/mod.rs
@@ -0,0 +1,150 @@
+// Copyright (C) 2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use std::str::FromStr;
+
+use std::time::Duration;
+use std::time::Instant;
+
+use crate::cc;
+use crate::recovery::Sent;
+
+pub const INITIAL_WINDOW_PACKETS: usize = 10;
+
+pub const INITIAL_WINDOW: usize = INITIAL_WINDOW_PACKETS * MAX_DATAGRAM_SIZE;
+
+pub const MINIMUM_WINDOW: usize = 2 * MAX_DATAGRAM_SIZE;
+
+pub const MAX_DATAGRAM_SIZE: usize = 1452;
+
+pub const LOSS_REDUCTION_FACTOR: f64 = 0.5;
+
+/// Available congestion control algorithms.
+///
+/// This enum provides currently available list of congestion control
+/// algorithms.
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub enum Algorithm {
+ /// Reno congestion control algorithm (default). `reno` in a string form.
+ Reno = 0,
+}
+
+impl FromStr for Algorithm {
+ type Err = crate::Error;
+
+ /// Converts a string to `CongestionControlAlgorithm`.
+ ///
+ /// If `name` is not valid, `Error::CongestionControl` is returned.
+ fn from_str(name: &str) -> Result<Self, Self::Err> {
+ match name {
+ "reno" => Ok(Algorithm::Reno),
+ _ => Err(crate::Error::CongestionControl),
+ }
+ }
+}
+
+/// Congestion control algorithm.
+pub trait CongestionControl
+where
+ Self: std::fmt::Debug,
+{
+ fn new() -> Self
+ where
+ Self: Sized;
+
+ fn cwnd(&self) -> usize;
+
+ fn bytes_in_flight(&self) -> usize;
+
+ fn decrease_bytes_in_flight(&mut self, bytes_in_flight: usize);
+
+ fn congestion_recovery_start_time(&self) -> Option<Instant>;
+
+ /// Resets the congestion window to the minimum size.
+ fn collapse_cwnd(&mut self);
+
+ /// OnPacketSentCC(bytes_sent)
+ fn on_packet_sent_cc(&mut self, bytes_sent: usize, trace_id: &str);
+
+ /// InCongestionRecovery(sent_time)
+ fn in_congestion_recovery(&self, sent_time: Instant) -> bool {
+ match self.congestion_recovery_start_time() {
+ Some(congestion_recovery_start_time) =>
+ sent_time <= congestion_recovery_start_time,
+
+ None => false,
+ }
+ }
+
+ /// OnPacketAckedCC(packet)
+ fn on_packet_acked_cc(
+ &mut self, packet: &Sent, srtt: Duration, min_rtt: Duration,
+ app_limited: bool, trace_id: &str,
+ );
+
+ /// CongestionEvent(time_sent)
+ fn congestion_event(
+ &mut self, time_sent: Instant, now: Instant, trace_id: &str,
+ );
+}
+
+/// Instances a congestion control implementation based on the CC algorithm ID.
+pub fn new_congestion_control(algo: Algorithm) -> Box<dyn CongestionControl> {
+ trace!("Initializing congestion control: {:?}", algo);
+ match algo {
+ Algorithm::Reno => Box::new(cc::reno::Reno::new()),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn new_cc() {
+ let cc = new_congestion_control(Algorithm::Reno);
+
+ assert!(cc.cwnd() > 0);
+ assert_eq!(cc.bytes_in_flight(), 0);
+ }
+
+ #[test]
+ fn lookup_cc_algo_ok() {
+ let algo = Algorithm::from_str("reno").unwrap();
+
+ assert_eq!(algo, Algorithm::Reno);
+ }
+
+ #[test]
+ fn lookup_cc_algo_bad() {
+ assert_eq!(
+ Algorithm::from_str("???"),
+ Err(crate::Error::CongestionControl)
+ );
+ }
+}
+
+mod reno;
diff --git a/src/cc/reno.rs b/src/cc/reno.rs
new file mode 100644
index 0000000..e9694bb
--- /dev/null
+++ b/src/cc/reno.rs
@@ -0,0 +1,263 @@
+// Copyright (C) 2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use std::time::Duration;
+use std::time::Instant;
+
+use crate::cc;
+use crate::recovery::Sent;
+
+/// Reno congestion control implementation.
+pub struct Reno {
+ congestion_window: usize,
+
+ bytes_in_flight: usize,
+
+ congestion_recovery_start_time: Option<Instant>,
+
+ ssthresh: usize,
+ /* TODO: ECN is not implemented.
+ * ecn_ce_counters: [usize; packet::EPOCH_COUNT] */
+}
+
+impl cc::CongestionControl for Reno {
+ fn new() -> Self
+ where
+ Self: Sized,
+ {
+ Reno {
+ congestion_window: cc::INITIAL_WINDOW,
+
+ bytes_in_flight: 0,
+
+ congestion_recovery_start_time: None,
+
+ ssthresh: std::usize::MAX,
+ /* TODO: ECN is not implemented.
+ * ecn_ce_counters: [0; packet::EPOCH_COUNT], */
+ }
+ }
+
+ fn cwnd(&self) -> usize {
+ self.congestion_window
+ }
+
+ fn collapse_cwnd(&mut self) {
+ self.congestion_window = cc::MINIMUM_WINDOW;
+ }
+
+ fn bytes_in_flight(&self) -> usize {
+ self.bytes_in_flight
+ }
+
+ fn decrease_bytes_in_flight(&mut self, bytes_in_flight: usize) {
+ self.bytes_in_flight =
+ self.bytes_in_flight.saturating_sub(bytes_in_flight);
+ }
+
+ fn congestion_recovery_start_time(&self) -> Option<Instant> {
+ self.congestion_recovery_start_time
+ }
+
+ fn on_packet_sent_cc(&mut self, bytes_sent: usize, _trace_id: &str) {
+ self.bytes_in_flight += bytes_sent;
+ }
+
+ fn on_packet_acked_cc(
+ &mut self, packet: &Sent, _srtt: Duration, _min_rtt: Duration,
+ app_limited: bool, _trace_id: &str,
+ ) {
+ self.bytes_in_flight -= packet.size;
+
+ if self.in_congestion_recovery(packet.time) {
+ return;
+ }
+
+ if app_limited {
+ return;
+ }
+
+ if self.congestion_window < self.ssthresh {
+ // Slow start.
+ self.congestion_window += packet.size;
+ } else {
+ // Congestion avoidance.
+ self.congestion_window +=
+ (cc::MAX_DATAGRAM_SIZE * packet.size) / self.congestion_window;
+ }
+ }
+
+ fn congestion_event(
+ &mut self, time_sent: Instant, now: Instant, _trace_id: &str,
+ ) {
+ // Start a new congestion event if packet was sent after the
+ // start of the previous congestion recovery period.
+ if !self.in_congestion_recovery(time_sent) {
+ self.congestion_recovery_start_time = Some(now);
+
+ self.congestion_window = (self.congestion_window as f64 *
+ cc::LOSS_REDUCTION_FACTOR)
+ as usize;
+ self.congestion_window =
+ std::cmp::max(self.congestion_window, cc::MINIMUM_WINDOW);
+ self.ssthresh = self.congestion_window;
+ }
+ }
+}
+
+impl std::fmt::Debug for Reno {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(
+ f,
+ "cwnd={} ssthresh={} bytes_in_flight={}",
+ self.congestion_window, self.ssthresh, self.bytes_in_flight,
+ )
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ const TRACE_ID: &str = "test_id";
+
+ #[test]
+ fn reno_init() {
+ let cc = cc::new_congestion_control(cc::Algorithm::Reno);
+
+ assert!(cc.cwnd() > 0);
+ assert_eq!(cc.bytes_in_flight(), 0);
+ }
+
+ #[test]
+ fn reno_send() {
+ let mut cc = cc::new_congestion_control(cc::Algorithm::Reno);
+
+ cc.on_packet_sent_cc(1000, TRACE_ID);
+
+ assert_eq!(cc.bytes_in_flight(), 1000);
+ }
+
+ #[test]
+ fn reno_slow_start() {
+ let mut cc = cc::new_congestion_control(cc::Algorithm::Reno);
+
+ let p = Sent {
+ pkt_num: 0,
+ frames: vec![],
+ time: std::time::Instant::now(),
+ size: 5000,
+ ack_eliciting: true,
+ in_flight: true,
+ };
+
+ // Send 5k x 4 = 20k, higher than default cwnd(~15k)
+ // to become no longer app limited.
+ cc.on_packet_sent_cc(p.size, TRACE_ID);
+ cc.on_packet_sent_cc(p.size, TRACE_ID);
+ cc.on_packet_sent_cc(p.size, TRACE_ID);
+ cc.on_packet_sent_cc(p.size, TRACE_ID);
+
+ let cwnd_prev = cc.cwnd();
+
+ cc.on_packet_acked_cc(
+ &p,
+ Duration::new(0, 1),
+ Duration::new(0, 1),
+ false,
+ TRACE_ID,
+ );
+
+ // Check if cwnd increased by packet size (slow start).
+ assert_eq!(cc.cwnd(), cwnd_prev + p.size);
+ }
+
+ #[test]
+ fn reno_congestion_event() {
+ let mut cc = cc::new_congestion_control(cc::Algorithm::Reno);
+ let prev_cwnd = cc.cwnd();
+
+ cc.congestion_event(
+ std::time::Instant::now(),
+ std::time::Instant::now(),
+ TRACE_ID,
+ );
+
+ // In Reno, after congestion event, cwnd will be cut in half.
+ assert_eq!(prev_cwnd / 2, cc.cwnd());
+ }
+
+ #[test]
+ fn reno_congestion_avoidance() {
+ let mut cc = cc::new_congestion_control(cc::Algorithm::Reno);
+ let prev_cwnd = cc.cwnd();
+
+ // Send 20K bytes.
+ cc.on_packet_sent_cc(20000, TRACE_ID);
+
+ cc.congestion_event(
+ std::time::Instant::now(),
+ std::time::Instant::now(),
+ TRACE_ID,
+ );
+
+ // In Reno, after congestion event, cwnd will be cut in half.
+ assert_eq!(prev_cwnd / 2, cc.cwnd());
+
+ let p = Sent {
+ pkt_num: 0,
+ frames: vec![],
+ time: std::time::Instant::now(),
+ size: 5000,
+ ack_eliciting: true,
+ in_flight: true,
+ };
+
+ let prev_cwnd = cc.cwnd();
+
+ // Ack 5000 bytes.
+ cc.on_packet_acked_cc(
+ &p,
+ Duration::new(0, 1),
+ Duration::new(0, 1),
+ false,
+ TRACE_ID,
+ );
+
+ // Check if cwnd increase is smaller than a packet size (congestion
+ // avoidance).
+ assert!(cc.cwnd() < prev_cwnd + 1111);
+ }
+
+ #[test]
+ fn reno_collapse_cwnd() {
+ let mut cc = cc::new_congestion_control(cc::Algorithm::Reno);
+
+ // cwnd will be reset
+ cc.collapse_cwnd();
+ assert_eq!(cc.cwnd(), cc::MINIMUM_WINDOW);
+ }
+}
diff --git a/src/crypto.rs b/src/crypto.rs
index 61c53b1..21873b8 100644
--- a/src/crypto.rs
+++ b/src/crypto.rs
@@ -1,5 +1,4 @@
-// Copyright (C) 2018, Cloudflare, Inc.
-// Copyright (C) 2018, Alessandro Ghedini
+// Copyright (C) 2018-2019, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -37,9 +36,6 @@
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Level {
Initial = 0,
- // Silence "variant is never constructed" warning because the value can
- // be received from BoringSSL as part of the FFI callbacks.
- #[allow(dead_code)]
ZeroRTT = 1,
Handshake = 2,
OneRTT = 3,
@@ -392,7 +388,7 @@
}
fn make_nonce(iv: &[u8], counter: u64) -> aead::Nonce {
- let mut nonce = [0; 12];
+ let mut nonce = [0; aead::NONCE_LEN];
nonce.copy_from_slice(&iv);
// XOR the last bytes of the IV with the counter. This is equivalent to
diff --git a/src/ffi.rs b/src/ffi.rs
index f75f45d..831faee 100644
--- a/src/ffi.rs
+++ b/src/ffi.rs
@@ -1,5 +1,4 @@
-// Copyright (C) 2018, Cloudflare, Inc.
-// Copyright (C) 2018, Alessandro Ghedini
+// Copyright (C) 2018-2019, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -129,6 +128,11 @@
}
#[no_mangle]
+pub extern fn quiche_config_enable_early_data(config: &mut Config) {
+ config.enable_early_data();
+}
+
+#[no_mangle]
pub extern fn quiche_config_set_application_protos(
config: &mut Config, protos: *const u8, protos_len: size_t,
) -> c_int {
@@ -142,8 +146,8 @@
}
#[no_mangle]
-pub extern fn quiche_config_set_idle_timeout(config: &mut Config, v: u64) {
- config.set_idle_timeout(v);
+pub extern fn quiche_config_set_max_idle_timeout(config: &mut Config, v: u64) {
+ config.set_max_idle_timeout(v);
}
#[no_mangle]
@@ -209,6 +213,25 @@
}
#[no_mangle]
+pub extern fn quiche_config_set_cc_algorithm_name(
+ config: &mut Config, name: *const c_char,
+) -> c_int {
+ let name = unsafe { ffi::CStr::from_ptr(name).to_str().unwrap() };
+ match config.set_cc_algorithm_name(name) {
+ Ok(_) => 0,
+
+ Err(e) => e.to_c() as c_int,
+ }
+}
+
+#[no_mangle]
+pub extern fn quiche_config_set_cc_algorithm(
+ config: &mut Config, algo: cc::Algorithm,
+) {
+ config.set_cc_algorithm(algo);
+}
+
+#[no_mangle]
pub extern fn quiche_config_free(config: *mut Config) {
unsafe { Box::from_raw(config) };
}
@@ -292,7 +315,7 @@
};
match accept(scid, odcid, config) {
- Ok(c) => Box::into_raw(c),
+ Ok(c) => Box::into_raw(Pin::into_inner(c)),
Err(_) => ptr::null_mut(),
}
@@ -312,7 +335,7 @@
let scid = unsafe { slice::from_raw_parts(scid, scid_len) };
match connect(server_name, scid, config) {
- Ok(c) => Box::into_raw(c),
+ Ok(c) => Box::into_raw(Pin::into_inner(c)),
Err(_) => ptr::null_mut(),
}
@@ -335,6 +358,11 @@
}
#[no_mangle]
+pub extern fn quiche_version_is_supported(version: u32) -> bool {
+ version_is_supported(version)
+}
+
+#[no_mangle]
pub extern fn quiche_retry(
scid: *const u8, scid_len: size_t, dcid: *const u8, dcid_len: size_t,
new_scid: *const u8, new_scid_len: size_t, token: *const u8,
@@ -369,7 +397,7 @@
let tls = unsafe { tls::Handshake::from_ptr(ssl) };
match Connection::with_tls(scid, odcid, config, tls, is_server) {
- Ok(c) => Box::into_raw(c),
+ Ok(c) => Box::into_raw(Pin::into_inner(c)),
Err(_) => ptr::null_mut(),
}
@@ -489,6 +517,28 @@
}
#[no_mangle]
+pub extern fn quiche_conn_stream_init_application_data(
+ conn: &mut Connection, stream_id: u64, data: *mut c_void,
+) -> c_int {
+ match conn.stream_init_application_data(stream_id, data) {
+ Ok(_) => 0,
+
+ Err(e) => e.to_c() as c_int,
+ }
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_stream_application_data(
+ conn: &mut Connection, stream_id: u64,
+) -> *mut c_void {
+ match conn.stream_application_data(stream_id) {
+ Some(v) => *v.downcast_mut::<*mut c_void>().unwrap(),
+
+ None => ptr::null_mut(),
+ }
+}
+
+#[no_mangle]
pub extern fn quiche_conn_close(
conn: &mut Connection, app: bool, err: u64, reason: *const u8,
reason_len: size_t,
@@ -541,6 +591,11 @@
}
#[no_mangle]
+pub extern fn quiche_conn_is_in_early_data(conn: &mut Connection) -> bool {
+ conn.is_in_early_data()
+}
+
+#[no_mangle]
pub extern fn quiche_conn_is_closed(conn: &mut Connection) -> bool {
conn.is_closed()
}
diff --git a/src/frame.rs b/src/frame.rs
index 75fa56e..9a461ed 100644
--- a/src/frame.rs
+++ b/src/frame.rs
@@ -1,5 +1,4 @@
-// Copyright (C) 2018, Cloudflare, Inc.
-// Copyright (C) 2018, Alessandro Ghedini
+// Copyright (C) 2018-2019, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -137,6 +136,8 @@
error_code: u64,
reason: Vec<u8>,
},
+
+ HandshakeDone,
}
impl Frame {
@@ -253,25 +254,34 @@
reason: b.get_bytes_with_varint_length()?.to_vec(),
},
+ 0x1e => Frame::HandshakeDone,
+
_ => return Err(Error::InvalidFrame),
};
let allowed = match (pkt, &frame) {
- // PADDING is allowed on all packet types.
- (_, Frame::Padding { .. }) => true,
+ // PADDING and PING are allowed on all packet types.
+ (_, Frame::Padding { .. }) | (_, Frame::Ping { .. }) => true,
- // ACK, CRYPTO and CONNECTION_CLOSE frames are allowed on all
- // packet types except 0-RTT.
+ // ACK, CRYPTO, HANDSHAKE_DONE, NEW_TOKEN, PATH_RESPONSE, and
+ // RETIRE_CONNECTION_ID can't be sent on 0-RTT packets.
(packet::Type::ZeroRTT, Frame::ACK { .. }) => false,
(packet::Type::ZeroRTT, Frame::Crypto { .. }) => false,
+ (packet::Type::ZeroRTT, Frame::HandshakeDone) => false,
+ (packet::Type::ZeroRTT, Frame::NewToken { .. }) => false,
+ (packet::Type::ZeroRTT, Frame::PathResponse { .. }) => false,
+ (packet::Type::ZeroRTT, Frame::RetireConnectionId { .. }) => false,
(packet::Type::ZeroRTT, Frame::ConnectionClose { .. }) => false,
+ // ACK, CRYPTO and CONNECTION_CLOSE can be sent on all other packet
+ // types.
(_, Frame::ACK { .. }) => true,
(_, Frame::Crypto { .. }) => true,
(_, Frame::ConnectionClose { .. }) => true,
- // All frames are allowed on Application packets.
+ // All frames are allowed on 0-RTT and 1-RTT packets.
(packet::Type::Short, _) => true,
+ (packet::Type::ZeroRTT, _) => true,
// All other cases are forbidden.
(..) => false,
@@ -489,6 +499,10 @@
b.put_varint(reason.len() as u64)?;
b.put_bytes(reason.as_ref())?;
},
+
+ Frame::HandshakeDone => {
+ b.put_varint(0x1e)?;
+ },
}
Ok(before - b.cap())
@@ -658,12 +672,19 @@
octets::varint_len(reason.len() as u64) + // reason_len
reason.len() // reason
},
+
+ Frame::HandshakeDone => {
+ 1 // frame type
+ },
}
}
pub fn ack_eliciting(&self) -> bool {
match self {
- Frame::Padding { .. } | Frame::ACK { .. } => false,
+ Frame::Padding { .. } |
+ Frame::ACK { .. } |
+ Frame::ApplicationClose { .. } |
+ Frame::ConnectionClose { .. } => false,
_ => true,
}
@@ -798,6 +819,10 @@
error_code, reason
)?;
},
+
+ Frame::HandshakeDone => {
+ write!(f, "HANDSHAKE_DONE")?;
+ },
}
Ok(())
@@ -921,13 +946,13 @@
assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_ok());
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_ok());
}
#[test]
@@ -986,10 +1011,10 @@
assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
let mut b = octets::Octets::with_slice(&mut d);
assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
@@ -1015,10 +1040,10 @@
assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
let mut b = octets::Octets::with_slice(&mut d);
assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
@@ -1104,10 +1129,10 @@
assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
let mut b = octets::Octets::with_slice(&mut d);
assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
@@ -1155,10 +1180,10 @@
assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
let mut b = octets::Octets::with_slice(&mut d);
assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
@@ -1184,10 +1209,10 @@
assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
let mut b = octets::Octets::with_slice(&mut d);
assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
@@ -1210,10 +1235,10 @@
assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
let mut b = octets::Octets::with_slice(&mut d);
assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
@@ -1236,10 +1261,10 @@
assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
let mut b = octets::Octets::with_slice(&mut d);
assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
@@ -1262,10 +1287,10 @@
assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
let mut b = octets::Octets::with_slice(&mut d);
assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
@@ -1291,10 +1316,10 @@
assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
let mut b = octets::Octets::with_slice(&mut d);
assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
@@ -1317,10 +1342,10 @@
assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
let mut b = octets::Octets::with_slice(&mut d);
assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
@@ -1343,10 +1368,10 @@
assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
let mut b = octets::Octets::with_slice(&mut d);
assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
@@ -1374,10 +1399,10 @@
assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
let mut b = octets::Octets::with_slice(&mut d);
assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
@@ -1428,10 +1453,10 @@
assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
let mut b = octets::Octets::with_slice(&mut d);
- assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err());
+ assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
let mut b = octets::Octets::with_slice(&mut d);
assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
@@ -1515,6 +1540,32 @@
assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
let mut b = octets::Octets::with_slice(&mut d);
+ assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok());
+
+ let mut b = octets::Octets::with_slice(&mut d);
+ assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
+
+ let mut b = octets::Octets::with_slice(&mut d);
+ assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err());
+ }
+
+ #[test]
+ fn handshake_done() {
+ let mut d = [42; 128];
+
+ let frame = Frame::HandshakeDone;
+
+ let wire_len = {
+ let mut b = octets::Octets::with_slice(&mut d);
+ frame.to_bytes(&mut b).unwrap()
+ };
+
+ assert_eq!(wire_len, 1);
+
+ let mut b = octets::Octets::with_slice(&mut d);
+ assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+ let mut b = octets::Octets::with_slice(&mut d);
assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err());
let mut b = octets::Octets::with_slice(&mut d);
diff --git a/src/h3/ffi.rs b/src/h3/ffi.rs
index 95e2968..50ffb1d 100644
--- a/src/h3/ffi.rs
+++ b/src/h3/ffi.rs
@@ -125,8 +125,8 @@
argp: *mut c_void,
) -> c_int {
match ev {
- h3::Event::Headers(headers) =>
- for h in headers {
+ h3::Event::Headers { list, .. } =>
+ for h in list {
let rc = cb(
h.name().as_ptr(),
h.name().len(),
@@ -147,6 +147,15 @@
}
#[no_mangle]
+pub extern fn quiche_h3_event_headers_has_body(ev: &h3::Event) -> bool {
+ match ev {
+ h3::Event::Headers { has_body, .. } => *has_body,
+
+ _ => unreachable!(),
+ }
+}
+
+#[no_mangle]
pub extern fn quiche_h3_event_free(ev: *mut h3::Event) {
unsafe { Box::from_raw(ev) };
}
diff --git a/src/h3/mod.rs b/src/h3/mod.rs
index b72724f..6e327b0 100644
--- a/src/h3/mod.rs
+++ b/src/h3/mod.rs
@@ -129,8 +129,8 @@
//! # let mut h3_conn = quiche::h3::Connection::with_transport(&mut conn, &h3_config)?;
//! loop {
//! match h3_conn.poll(&mut conn) {
-//! Ok((stream_id, quiche::h3::Event::Headers(headers))) => {
-//! let mut headers = headers.into_iter();
+//! Ok((stream_id, quiche::h3::Event::Headers{list, has_body})) => {
+//! let mut headers = list.into_iter();
//!
//! // Look for the request's method.
//! let method = headers.find(|h| h.name() == ":method").unwrap();
@@ -182,8 +182,8 @@
//! # let mut h3_conn = quiche::h3::Connection::with_transport(&mut conn, &h3_config)?;
//! loop {
//! match h3_conn.poll(&mut conn) {
-//! Ok((stream_id, quiche::h3::Event::Headers(headers))) => {
-//! let status = headers.iter().find(|h| h.name() == ":status").unwrap();
+//! Ok((stream_id, quiche::h3::Event::Headers{list, has_body})) => {
+//! let status = list.iter().find(|h| h.name() == ":status").unwrap();
//! println!("Received {} response on stream {}",
//! status.value(), stream_id);
//! },
@@ -251,8 +251,14 @@
use crate::octets;
-/// The current HTTP/3 ALPN token.
-pub const APPLICATION_PROTOCOL: &[u8] = b"\x05h3-23";
+/// List of ALPN tokens of supported HTTP/3 versions.
+///
+/// This can be passed directly to the [`Config::set_application_protos()`]
+/// method when implementing HTTP/3 applications.
+///
+/// [`Config::set_application_protos()`]:
+/// ../struct.Config.html#method.set_application_protos
+pub const APPLICATION_PROTOCOL: &[u8] = b"\x05h3-25\x05h3-24\x05h3-23";
/// A specialized [`Result`] type for quiche HTTP/3 operations.
///
@@ -494,7 +500,14 @@
#[derive(Clone, Debug, PartialEq)]
pub enum Event {
/// Request/response headers were received.
- Headers(Vec<Header>),
+ Headers {
+ /// The list of received header fields. The application should validate
+ /// pseudo-headers and headers.
+ list: Vec<Header>,
+
+ /// Whether data will follow the headers on the stream.
+ has_body: bool,
+ },
/// Data was received.
///
@@ -702,6 +715,10 @@
)?;
conn.stream_send(stream_id, &header_block, fin)?;
+ if fin && conn.stream_finished(stream_id) {
+ self.streams.remove(&stream_id);
+ }
+
Ok(())
}
@@ -758,6 +775,10 @@
// Return how many bytes were written, excluding the frame header.
let written = conn.stream_send(stream_id, &body[..body_len], fin)?;
+ if fin && written == body.len() && conn.stream_finished(stream_id) {
+ self.streams.remove(&stream_id);
+ }
+
Ok(written)
}
@@ -805,6 +826,13 @@
/// [`send_body()`]: struct.Connection.html#method.send_body
/// [`close()`]: ../struct.Connection.html#method.close
pub fn poll(&mut self, conn: &mut super::Connection) -> Result<(u64, Event)> {
+ // When connection close is initiated by the local application (e.g. due
+ // to a protocol error), the connection itself might be in a broken
+ // state, so return early.
+ if conn.error.is_some() || conn.app_error.is_some() {
+ return Err(Error::Done);
+ }
+
// Process control streams first.
if let Some(stream_id) = self.peer_control_stream_id {
self.process_control_stream(conn, stream_id)?;
@@ -839,6 +867,8 @@
self.finished_streams.push_back(s);
}
+ // TODO: check if stream is completed so it can be freed
+
if let Some(ev) = ev {
return Ok(ev);
}
@@ -984,6 +1014,16 @@
fn process_control_stream(
&mut self, conn: &mut super::Connection, stream_id: u64,
) -> Result<()> {
+ if conn.stream_finished(stream_id) {
+ conn.close(
+ true,
+ Error::ClosedCriticalStream.to_wire(),
+ b"Critical stream closed.",
+ )?;
+
+ return Err(Error::ClosedCriticalStream);
+ }
+
match self.process_readable_stream(conn, stream_id) {
Ok(_) => (),
@@ -1283,7 +1323,12 @@
_ => Error::QpackDecompressionFailed,
})?;
- return Ok((stream_id, Event::Headers(headers)));
+ let has_body = !conn.stream_finished(stream_id);
+
+ return Ok((stream_id, Event::Headers {
+ list: headers,
+ has_body,
+ }));
},
frame::Frame::Data { .. } => {
@@ -1441,7 +1486,7 @@
/// Generates an HTTP/3 GREASE variable length integer.
fn grease_value() -> u64 {
- let n = std::cmp::min(super::rand::rand_u64(), 148_764_065_110_560_899);
+ let n = super::rand::rand_u64_uniform(148_764_065_110_560_899);
31 * n + 33
}
@@ -1721,12 +1766,22 @@
assert_eq!(stream, 0);
- assert_eq!(s.poll_server(), Ok((stream, Event::Headers(req))));
+ let ev_headers = Event::Headers {
+ list: req,
+ has_body: false,
+ };
+
+ assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
assert_eq!(s.poll_server(), Ok((stream, Event::Finished)));
let resp = s.send_response(stream, true).unwrap();
- assert_eq!(s.poll_client(), Ok((stream, Event::Headers(resp))));
+ let ev_headers = Event::Headers {
+ list: resp,
+ has_body: false,
+ };
+
+ assert_eq!(s.poll_client(), Ok((stream, ev_headers)));
assert_eq!(s.poll_client(), Ok((stream, Event::Finished)));
assert_eq!(s.poll_client(), Err(Error::Done));
}
@@ -1740,7 +1795,13 @@
let (stream, req) = s.send_request(true).unwrap();
assert_eq!(stream, 0);
- assert_eq!(s.poll_server(), Ok((stream, Event::Headers(req))));
+ let ev_headers = Event::Headers {
+ list: req,
+ has_body: false,
+ };
+
+ assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
+
assert_eq!(s.poll_server(), Ok((stream, Event::Finished)));
let resp = s.send_response(stream, false).unwrap();
@@ -1749,7 +1810,12 @@
let mut recv_buf = vec![0; body.len()];
- assert_eq!(s.poll_client(), Ok((stream, Event::Headers(resp))));
+ let ev_headers = Event::Headers {
+ list: resp,
+ has_body: true,
+ };
+
+ assert_eq!(s.poll_client(), Ok((stream, ev_headers)));
assert_eq!(s.poll_client(), Ok((stream, Event::Data)));
assert_eq!(s.recv_body_client(stream, &mut recv_buf), Ok(body.len()));
@@ -1766,7 +1832,12 @@
let (stream, req) = s.send_request(true).unwrap();
- assert_eq!(s.poll_server(), Ok((stream, Event::Headers(req))));
+ let ev_headers = Event::Headers {
+ list: req,
+ has_body: false,
+ };
+
+ assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
assert_eq!(s.poll_server(), Ok((stream, Event::Finished)));
let total_data_frames = 4;
@@ -1781,7 +1852,12 @@
let mut recv_buf = vec![0; body.len()];
- assert_eq!(s.poll_client(), Ok((stream, Event::Headers(resp))));
+ let ev_headers = Event::Headers {
+ list: resp,
+ has_body: true,
+ };
+
+ assert_eq!(s.poll_client(), Ok((stream, ev_headers)));
for _ in 0..total_data_frames {
assert_eq!(s.poll_client(), Ok((stream, Event::Data)));
@@ -1804,7 +1880,12 @@
let mut recv_buf = vec![0; body.len()];
- assert_eq!(s.poll_server(), Ok((stream, Event::Headers(req))));
+ let ev_headers = Event::Headers {
+ list: req,
+ has_body: true,
+ };
+
+ assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
assert_eq!(s.poll_server(), Ok((stream, Event::Data)));
assert_eq!(s.recv_body_server(stream, &mut recv_buf), Ok(body.len()));
@@ -1813,7 +1894,12 @@
let resp = s.send_response(stream, true).unwrap();
- assert_eq!(s.poll_client(), Ok((stream, Event::Headers(resp))));
+ let ev_headers = Event::Headers {
+ list: resp,
+ has_body: false,
+ };
+
+ assert_eq!(s.poll_client(), Ok((stream, ev_headers)));
assert_eq!(s.poll_client(), Ok((stream, Event::Finished)));
}
@@ -1835,7 +1921,12 @@
let mut recv_buf = vec![0; body.len()];
- assert_eq!(s.poll_server(), Ok((stream, Event::Headers(req))));
+ let ev_headers = Event::Headers {
+ list: req,
+ has_body: true,
+ };
+
+ assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
for _ in 0..total_data_frames {
assert_eq!(s.poll_server(), Ok((stream, Event::Data)));
@@ -1846,7 +1937,12 @@
let resp = s.send_response(stream, true).unwrap();
- assert_eq!(s.poll_client(), Ok((stream, Event::Headers(resp))));
+ let ev_headers = Event::Headers {
+ list: resp,
+ has_body: false,
+ };
+
+ assert_eq!(s.poll_client(), Ok((stream, ev_headers)));
assert_eq!(s.poll_client(), Ok((stream, Event::Finished)));
}
@@ -1885,7 +1981,11 @@
for _ in 0..reqs.len() {
let (stream, ev) = s.poll_server().unwrap();
- assert_eq!(ev, Event::Headers(reqs[(stream / 4) as usize].clone()));
+ let ev_headers = Event::Headers {
+ list: reqs[(stream / 4) as usize].clone(),
+ has_body: true,
+ };
+ assert_eq!(ev, ev_headers);
assert_eq!(s.poll_server(), Ok((stream, Event::Data)));
assert_eq!(s.recv_body_server(stream, &mut recv_buf), Ok(body.len()));
assert_eq!(s.poll_server(), Ok((stream, Event::Data)));
@@ -1908,7 +2008,11 @@
for _ in 0..resps.len() {
let (stream, ev) = s.poll_client().unwrap();
- assert_eq!(ev, Event::Headers(resps[(stream / 4) as usize].clone()));
+ let ev_headers = Event::Headers {
+ list: resps[(stream / 4) as usize].clone(),
+ has_body: false,
+ };
+ assert_eq!(ev, ev_headers);
assert_eq!(s.poll_client(), Ok((stream, Event::Finished)));
}
@@ -1998,7 +2102,12 @@
)
.unwrap();
- assert_eq!(s.poll_server(), Ok((stream, Event::Headers(req))));
+ let ev_headers = Event::Headers {
+ list: req,
+ has_body: true,
+ };
+
+ assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
assert_eq!(s.poll_server(), Err(Error::FrameUnexpected));
}
@@ -2062,7 +2171,12 @@
)
.unwrap();
- assert_eq!(s.poll_server(), Ok((stream, Event::Headers(req))));
+ let ev_headers = Event::Headers {
+ list: req,
+ has_body: true,
+ };
+
+ assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
assert_eq!(s.poll_server(), Err(Error::FrameUnexpected));
}
@@ -2097,7 +2211,12 @@
)
.unwrap();
- assert_eq!(s.poll_server(), Ok((stream, Event::Headers(req))));
+ let ev_headers = Event::Headers {
+ list: req,
+ has_body: true,
+ };
+
+ assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
assert_eq!(s.poll_server(), Err(Error::FrameUnexpected));
}
@@ -2132,7 +2251,12 @@
)
.unwrap();
- assert_eq!(s.poll_server(), Ok((stream, Event::Headers(req))));
+ let ev_headers = Event::Headers {
+ list: req,
+ has_body: true,
+ };
+
+ assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
assert_eq!(s.poll_server(), Err(Error::FrameUnexpected));
}
@@ -2412,7 +2536,12 @@
let mut recv_buf = vec![0; bytes.len()];
- assert_eq!(s.poll_server(), Ok((stream, Event::Headers(req))));
+ let ev_headers = Event::Headers {
+ list: req,
+ has_body: true,
+ };
+
+ assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
for _ in 0..total_data_frames {
assert_eq!(s.poll_server(), Ok((stream, Event::Data)));
@@ -2514,6 +2643,51 @@
Err(Error::TransportError(crate::Error::StreamLimit))
);
}
+
+ #[test]
+ /// Tests that calling poll() after an error occured does nothing.
+ fn poll_after_error() {
+ // DATA frames don't consume the state buffer, so can be of any size.
+ let mut s = Session::default().unwrap();
+ s.handshake().unwrap();
+
+ let mut d = [42; 128];
+ let mut b = octets::Octets::with_slice(&mut d);
+
+ let frame_type = b.put_varint(frame::DATA_FRAME_TYPE_ID).unwrap();
+ s.pipe.client.stream_send(0, frame_type, false).unwrap();
+
+ let frame_len = b.put_varint(1 << 24).unwrap();
+ s.pipe.client.stream_send(0, frame_len, false).unwrap();
+
+ s.pipe.client.stream_send(0, &d, false).unwrap();
+
+ s.advance().ok();
+
+ assert_eq!(s.server.poll(&mut s.pipe.server), Ok((0, Event::Data)));
+
+ // GREASE frames consume the state buffer, so need to be limited.
+ let mut s = Session::default().unwrap();
+ s.handshake().unwrap();
+
+ let mut d = [42; 128];
+ let mut b = octets::Octets::with_slice(&mut d);
+
+ let frame_type = b.put_varint(148_764_065_110_560_899).unwrap();
+ s.pipe.client.stream_send(0, frame_type, false).unwrap();
+
+ let frame_len = b.put_varint(1 << 24).unwrap();
+ s.pipe.client.stream_send(0, frame_len, false).unwrap();
+
+ s.pipe.client.stream_send(0, &d, false).unwrap();
+
+ s.advance().ok();
+
+ assert_eq!(s.server.poll(&mut s.pipe.server), Err(Error::InternalError));
+
+ // Try to call poll() again after an error occurred.
+ assert_eq!(s.server.poll(&mut s.pipe.server), Err(Error::Done));
+ }
}
mod ffi;
diff --git a/src/lib.rs b/src/lib.rs
index 1bc17d3..5af573b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,4 @@
-// Copyright (C) 2018, Cloudflare, Inc.
-// Copyright (C) 2018, Alessandro Ghedini
+// Copyright (C) 2018-2019, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -230,7 +229,37 @@
//! [`readable()`]: struct.Connection.html#method.readable
//! [`stream_recv()`]: struct.Connection.html#method.stream_recv
//! [HTTP/3 module]: h3/index.html
+//!
+//! ## Congestion Control
+//!
+//! The quiche library provides a high-level API for configuring which
+//! congestion control algorithm to use throughout the QUIC connection.
+//!
+//! When a QUIC connection is created, the application can optionally choose
+//! which CC algorithm to use. See [`CongestionControlAlgorithm`] for currently
+//! available congestion control algorithms.
+//!
+//! For example:
+//!
+//! ```
+//! let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
+//! config.set_cc_algorithm(quiche::CongestionControlAlgorithm::Reno);
+//! ```
+//!
+//! Alternatively, you can configure the congestion control algorithm to use
+//! by its name.
+//!
+//! ```
+//! let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
+//! config.set_cc_algorithm_name("reno").unwrap();
+//! ```
+//!
+//! Note that the CC algorithm should be configured before calling [`connect()`]
+//! or [`accept()`]. Otherwise the connection will use a default CC algorithm.
+//!
+//! [`CongestionControlAlgorithm`]: cc/enum.Algorithm.html
+#![allow(improper_ctypes)]
#![warn(missing_docs)]
#[macro_use]
@@ -239,8 +268,20 @@
use std::cmp;
use std::time;
+use std::pin::Pin;
+use std::str::FromStr;
+
+pub use crate::cc::Algorithm as CongestionControlAlgorithm;
+
/// The current QUIC wire version.
-pub const PROTOCOL_VERSION: u32 = 0xff00_0017;
+pub const PROTOCOL_VERSION: u32 = PROTOCOL_VERSION_DRAFT25;
+
+/// Supported QUIC versions.
+///
+/// Note that the older ones might not be fully supported.
+const PROTOCOL_VERSION_DRAFT25: u32 = 0xff00_0019;
+const PROTOCOL_VERSION_DRAFT24: u32 = 0xff00_0018;
+const PROTOCOL_VERSION_DRAFT23: u32 = 0xff00_0017;
/// The maximum length of a connection ID.
pub const MAX_CONN_ID_LEN: usize = crate::packet::MAX_CID_LEN as usize;
@@ -312,6 +353,9 @@
/// The received data exceeds the stream's final size.
FinalSize = -13,
+
+ /// Error in congestion control.
+ CongestionControl = -14,
}
impl Error {
@@ -378,6 +422,8 @@
application_protos: Vec<Vec<u8>>,
grease: bool,
+
+ cc_algorithm: cc::Algorithm,
}
impl Config {
@@ -398,6 +444,7 @@
tls_ctx,
application_protos: Vec::new(),
grease: true,
+ cc_algorithm: cc::Algorithm::Reno, // default cc algorithm
})
}
@@ -458,6 +505,11 @@
self.tls_ctx.enable_keylog();
}
+ /// Enables sending or receiving early data.
+ pub fn enable_early_data(&mut self) {
+ self.tls_ctx.set_early_data_enabled(true);
+ }
+
/// Configures the list of supported application protocols.
///
/// The list of protocols `protos` must be in wire-format (i.e. a series
@@ -494,11 +546,11 @@
self.tls_ctx.set_alpn(&self.application_protos)
}
- /// Sets the `idle_timeout` transport parameter.
+ /// Sets the `max_idle_timeout` transport parameter.
///
/// The default value is infinite, that is, no timeout is used.
- pub fn set_idle_timeout(&mut self, v: u64) {
- self.local_transport_params.idle_timeout = v;
+ pub fn set_max_idle_timeout(&mut self, v: u64) {
+ self.local_transport_params.max_idle_timeout = v;
}
/// Sets the `max_packet_size transport` parameter.
@@ -570,7 +622,7 @@
/// A bidirectional stream is considered completed when all incoming data
/// has been read by the application (up to the `fin` offset) or the
/// stream's read direction has been shutdown, and all outgoing data has
- /// been ACKed by the peer (up to the `fin` offset) or the stream's write
+ /// been acked by the peer (up to the `fin` offset) or the stream's write
/// direction has been shutdown.
///
/// The default value is `0`.
@@ -614,6 +666,31 @@
pub fn set_disable_active_migration(&mut self, v: bool) {
self.local_transport_params.disable_active_migration = v;
}
+
+ /// Sets the congestion control algorithm used by string.
+ ///
+ /// The default value is `reno`. On error `Error::CongestionControl`
+ /// will be returned.
+ ///
+ /// ## Examples:
+ ///
+ /// ```
+ /// # let mut config = quiche::Config::new(0xbabababa)?;
+ /// config.set_cc_algorithm_name("reno");
+ /// # Ok::<(), quiche::Error>(())
+ /// ```
+ pub fn set_cc_algorithm_name(&mut self, name: &str) -> Result<()> {
+ self.cc_algorithm = CongestionControlAlgorithm::from_str(name)?;
+
+ Ok(())
+ }
+
+ /// Sets the congestion control algorithm used.
+ ///
+ /// The default value is `quiche::CongestionControlAlgorithm::Reno`.
+ pub fn set_cc_algorithm(&mut self, algo: CongestionControlAlgorithm) {
+ self.cc_algorithm = algo;
+ }
}
/// A QUIC connection.
@@ -684,14 +761,6 @@
/// Received address verification token.
token: Option<Vec<u8>>,
- /// List of frames from Application packets received before the handshake
- /// was completed.
- early_app_frames: Vec<frame::Frame>,
-
- /// Number of Application packets received before the handshake was
- /// completed.
- early_app_pkts: usize,
-
/// Error code to be sent to the peer in CONNECTION_CLOSE.
error: Option<u64>,
@@ -730,13 +799,16 @@
/// Whether the peer's address has been verified.
verified_peer_address: bool,
- /// Whether the connection handshake has completed.
- handshake_completed: bool,
+ /// Whether the peer's transport parameters were parsed.
+ parsed_peer_transport_params: bool,
+
+ /// Whether the HANDSHAKE_DONE has been sent.
+ handshake_done_sent: bool,
/// Whether the connection handshake has been confirmed.
handshake_confirmed: bool,
- /// Whether an ACK-eliciting packet has been sent since last receiving a
+ /// Whether an ack-eliciting packet has been sent since last receiving a
/// packet.
ack_eliciting_sent: bool,
@@ -766,7 +838,7 @@
/// ```
pub fn accept(
scid: &[u8], odcid: Option<&[u8]>, config: &mut Config,
-) -> Result<Box<Connection>> {
+) -> Result<Pin<Box<Connection>>> {
let conn = Connection::new(scid, odcid, config, true)?;
Ok(conn)
@@ -789,7 +861,7 @@
/// ```
pub fn connect(
server_name: Option<&str>, scid: &[u8], config: &mut Config,
-) -> Result<Box<Connection>> {
+) -> Result<Pin<Box<Connection>>> {
let conn = Connection::new(scid, None, config, false)?;
if let Some(server_name) = server_name {
@@ -892,10 +964,21 @@
packet::retry(scid, dcid, new_scid, token, out)
}
+/// Returns true if the given protocol version is supported.
+pub fn version_is_supported(version: u32) -> bool {
+ match version {
+ PROTOCOL_VERSION |
+ PROTOCOL_VERSION_DRAFT24 |
+ PROTOCOL_VERSION_DRAFT23 => true,
+
+ _ => false,
+ }
+}
+
impl Connection {
fn new(
scid: &[u8], odcid: Option<&[u8]>, config: &mut Config, is_server: bool,
- ) -> Result<Box<Connection>> {
+ ) -> Result<Pin<Box<Connection>>> {
let tls = config.tls_ctx.new_handshake()?;
Connection::with_tls(scid, odcid, config, tls, is_server)
}
@@ -903,13 +986,13 @@
fn with_tls(
scid: &[u8], odcid: Option<&[u8]>, config: &mut Config,
tls: tls::Handshake, is_server: bool,
- ) -> Result<Box<Connection>> {
+ ) -> Result<Pin<Box<Connection>>> {
let max_rx_data = config.local_transport_params.initial_max_data;
let scid_as_hex: Vec<String> =
scid.iter().map(|b| format!("{:02x}", b)).collect();
- let mut conn = Box::new(Connection {
+ let mut conn = Box::pin(Connection {
version: config.version,
dcid: Vec::new(),
@@ -929,7 +1012,7 @@
handshake: tls,
- recovery: recovery::Recovery::default(),
+ recovery: recovery::Recovery::new(&config),
application_protos: config.application_protos.clone(),
@@ -954,10 +1037,6 @@
token: None,
- early_app_frames: Vec::new(),
-
- early_app_pkts: 0,
-
error: None,
app_error: None,
@@ -982,7 +1061,9 @@
// If we did stateless retry assume the peer's address is verified.
verified_peer_address: odcid.is_some(),
- handshake_completed: false,
+ parsed_peer_transport_params: false,
+
+ handshake_done_sent: false,
handshake_confirmed: false,
@@ -1088,6 +1169,17 @@
left -= read;
}
+ // Keep track of how many bytes we received from the client, so we
+ // can limit bytes sent back before address validation, to a multiple
+ // of this. The limit needs to be increased early on, so that if there
+ // is an error there is enough credit to send a CONNECTION_CLOSE.
+ //
+ // It doesn't matter if the packets received were valid or not, we only
+ // need to track the total amount of bytes received.
+ if !self.verified_peer_address {
+ self.max_send_bytes += buf.len() * MAX_AMPLIFICATION_FACTOR;
+ }
+
Ok(done)
}
@@ -1139,21 +1231,16 @@
None => return Err(Error::InvalidPacket),
};
- let mut new_version = 0;
- for v in versions.iter() {
- if *v == PROTOCOL_VERSION {
- new_version = *v;
- }
- }
-
- // We don't support any of the versions offered.
- if new_version == 0 {
+ if let Some(version) =
+ versions.iter().filter(|v| version_is_supported(**v)).max()
+ {
+ self.version = *version;
+ self.did_version_negotiation = true;
+ } else {
+ // We don't support any of the versions offered.
return Err(Error::UnknownVersion);
}
- self.version = new_version;
- self.did_version_negotiation = true;
-
// Reset connection state to force sending another Initial packet.
self.got_peer_conn_id = false;
self.recovery.drop_unacked_data(packet::EPOCH_INITIAL);
@@ -1174,7 +1261,8 @@
return Err(Error::Done);
}
- if hdr.odcid.as_ref() != Some(&self.dcid) {
+ // Check if Retry packet is valid.
+ if packet::verify_retry_integrity(&b, &self.dcid).is_err() {
return Err(Error::Done);
}
@@ -1207,6 +1295,24 @@
return Err(Error::Done);
}
+ // Discard 1-RTT packets received before handshake is completed (even
+ // if the frames are not immediately processed) to avoid potential side
+ // effects.
+ //
+ // TODO: buffer packets instead of discarding as an optimization.
+ if hdr.ty == packet::Type::Short && !self.is_established() {
+ return Ok(b.len());
+ }
+
+ if self.is_server && !self.did_version_negotiation {
+ if !version_is_supported(hdr.version) {
+ return Err(Error::UnknownVersion);
+ }
+
+ self.version = hdr.version;
+ self.did_version_negotiation = true;
+ }
+
if hdr.ty != packet::Type::Short && hdr.version != self.version {
return Err(Error::UnknownVersion);
}
@@ -1253,26 +1359,36 @@
// Select packet number space epoch based on the received packet's type.
let epoch = hdr.ty.to_epoch()?;
- let aead = match self.pkt_num_spaces[epoch].crypto_open {
- Some(ref v) => v,
+ let aead = if hdr.ty == packet::Type::ZeroRTT &&
+ self.pkt_num_spaces[epoch].crypto_0rtt_open.is_some()
+ {
+ self.pkt_num_spaces[epoch]
+ .crypto_0rtt_open
+ .as_ref()
+ .unwrap()
+ } else {
+ match self.pkt_num_spaces[epoch].crypto_open {
+ Some(ref v) => v,
- // Ignore packets that can't be decrypted because we don't have the
- // necessary decryption key (either because we don't yet have it or
- // because we already dropped it).
- //
- // For example, this is necessary to prevent packet reordering (e.g.
- // between Initial and Handshake) from causing the connection to be
- // closed.
- None => {
- trace!(
- "{} dropped undecryptable packet type={:?} len={}",
- self.trace_id,
- hdr.ty,
- payload_len
- );
+ // Ignore packets that can't be decrypted because we don't have
+ // the necessary decryption key (either because we
+ // don't yet have it or because we already dropped
+ // it).
+ //
+ // For example, this is necessary to prevent packet reordering
+ // (e.g. between Initial and Handshake) from
+ // causing the connection to be closed.
+ None => {
+ trace!(
+ "{} dropped undecryptable packet type={:?} len={}",
+ self.trace_id,
+ hdr.ty,
+ payload_len
+ );
- return Ok(header_len + payload_len);
- },
+ return Ok(header_len + payload_len);
+ },
+ }
};
let aead_tag_len = aead.alg().tag_len();
@@ -1326,31 +1442,6 @@
return Err(Error::Done);
}
- // Keep track of the number of Application packets received before the
- // handshake is completed, and drop any that exceed the initial
- // congestion window packet count.
- if hdr.ty == packet::Type::Short && !self.is_established() {
- self.early_app_pkts += 1;
-
- if self.early_app_pkts > recovery::INITIAL_WINDOW_PACKETS {
- error!(
- "{} dropped early application packet len={} pn={}",
- self.trace_id, payload_len, pn,
- );
-
- return Ok(header_len + payload_len);
- }
- }
-
- // Keep track of how many bytes we received from the client, so we
- // can limit bytes sent back before address validation, to a multiple
- // of this. The limit needs to be increased early on, so that if there
- // is an error there is enough credit to send a CONNECTION_CLOSE.
- if !self.verified_peer_address {
- self.max_send_bytes +=
- (header_len + payload_len) * MAX_AMPLIFICATION_FACTOR;
- }
-
// To avoid sending an ACK in response to an ACK-only packet, we need
// to keep track of whether this packet contains any frame other than
// ACK and PADDING.
@@ -1364,23 +1455,16 @@
ack_elicited = true;
}
- // If the packet this frame belongs to is an early Application one,
- // buffer the frame for later processing.
- if hdr.ty == packet::Type::Short && !self.is_established() {
- self.early_app_frames.push(frame);
- continue;
- }
-
self.process_frame(frame, epoch, now)?;
}
- // Process ACK'd frames.
+ // Process acked frames.
for acked in self.recovery.acked[epoch].drain(..) {
match acked {
frame::Frame::ACK { ranges, .. } => {
// Stop acknowledging packets less than or equal to the
// largest acknowledged in the sent ACK frame that, in
- // turn, got ACK'd.
+ // turn, got acked.
if let Some(largest_acked) = ranges.largest() {
self.pkt_num_spaces[epoch]
.recv_pkt_need_ack
@@ -1415,7 +1499,7 @@
}
// We only record the time of arrival of the largest packet number
- // that still needs to be ACK'd, to be used for ACK delay calculation.
+ // that still needs to be acked, to be used for ACK delay calculation.
if self.pkt_num_spaces[epoch].recv_pkt_need_ack.largest() < Some(pn) {
self.pkt_num_spaces[epoch].largest_rx_pkt_time = now;
}
@@ -1429,8 +1513,8 @@
self.pkt_num_spaces[epoch].largest_rx_pkt_num =
cmp::max(self.pkt_num_spaces[epoch].largest_rx_pkt_num, pn);
- if self.local_transport_params.idle_timeout > 0 {
- self.idle_timer = Some(now + self.idle_timeout());
+ if let Some(idle_timeout) = self.idle_timeout() {
+ self.idle_timer = Some(now + idle_timeout);
}
self.recv_count += 1;
@@ -1513,16 +1597,22 @@
return Err(Error::Done);
}
+ // If the Initial secrets have not been derived yet, there's no point
+ // in trying to send a packet, so return early.
+ if !self.derived_initial_secrets {
+ return Err(Error::Done);
+ }
+
let is_closing = self.error.is_some() || self.app_error.is_some();
if !is_closing {
- self.do_handshake(now)?;
+ self.do_handshake()?;
}
// Use max_packet_size as sent by the peer, except during the handshake
// when we haven't parsed transport parameters yet, so use a default
// value then.
- let max_pkt_len = if self.handshake_completed {
+ let max_pkt_len = if self.is_established() {
// We cap the maximum packet size to 16KB or so, so that it can be
// always encoded with a 2-byte varint.
cmp::min(16383, self.peer_transport_params.max_packet_size) as usize
@@ -1562,11 +1652,16 @@
let was_flushable = stream.is_flushable();
+ let empty_fin = data.is_empty() && data.fin();
+
stream.send.push(data)?;
// If the stream is now flushable push it to the flushable
// queue, but only if it wasn't already queued.
- if stream.is_flushable() && !was_flushable {
+ //
+ // Consider the stream flushable also when we are sending a
+ // zero-length frame that has the fin flag set.
+ if (stream.is_flushable() || empty_fin) && !was_flushable {
self.streams.push_flushable(stream_id);
}
},
@@ -1575,12 +1670,16 @@
self.pkt_num_spaces[epoch].ack_elicited = true;
},
+ frame::Frame::HandshakeDone => {
+ self.handshake_done_sent = false;
+ },
+
_ => (),
}
}
// Calculate available space in the packet based on congestion window.
- let mut left = cmp::min(self.recovery.cwnd(), b.cap());
+ let mut left = cmp::min(self.recovery.cwnd_available(), b.cap());
// Limit data sent by the server based on the amount of data received
// from the client before its address is validated.
@@ -1592,7 +1691,8 @@
let pn_len = packet::pkt_num_len(pn)?;
// The AEAD overhead at the current encryption level.
- let overhead = self.pkt_num_spaces[epoch].overhead();
+ let overhead =
+ self.pkt_num_spaces[epoch].overhead().ok_or(Error::Done)?;
let hdr = Header {
ty: pkt_type,
@@ -1601,7 +1701,6 @@
scid: self.scid.clone(),
pkt_num: 0,
pkt_num_len: pn_len,
- odcid: None,
token: self.token.clone(),
versions: None,
key_phase: false,
@@ -1610,22 +1709,26 @@
hdr.to_bytes(&mut b)?;
// Make sure we have enough space left for the header, the payload
- // length, the packet number and the AEAD overhead. We assume that
- // the payload length can always be encoded with a 2-byte varint.
+ // length, the packet number and the AEAD overhead.
left = left
- .checked_sub(b.off() + 2 + pn_len + overhead)
+ .checked_sub(b.off() + pn_len + overhead)
.ok_or(Error::Done)?;
+ // We assume that the payload length, which is only present in long
+ // header packets, can always be encoded with a 2-byte varint.
+ if pkt_type != packet::Type::Short {
+ left = left.checked_sub(2).ok_or(Error::Done)?;
+ }
+
let mut frames: Vec<frame::Frame> = Vec::new();
let mut ack_eliciting = false;
let mut in_flight = false;
- let mut is_crypto = false;
let mut payload_len = 0;
// Create ACK frame.
- if self.pkt_num_spaces[epoch].ack_elicited {
+ if self.pkt_num_spaces[epoch].ack_elicited && !is_closing {
let ack_delay =
self.pkt_num_spaces[epoch].largest_rx_pkt_time.elapsed();
@@ -1649,6 +1752,22 @@
}
if pkt_type == packet::Type::Short && !is_closing {
+ // Create HANDSHAKE_DONE frame.
+ if self.is_established() &&
+ !self.handshake_done_sent &&
+ self.is_server &&
+ self.version >= PROTOCOL_VERSION_DRAFT25
+ {
+ let frame = frame::Frame::HandshakeDone;
+
+ payload_len += frame.wire_len();
+ left -= frame.wire_len();
+
+ frames.push(frame);
+
+ self.handshake_done_sent = true;
+ }
+
// Create MAX_STREAMS_BIDI frame.
if self.streams.should_update_max_streams_bidi() {
let max = self.streams.update_max_streams_bidi();
@@ -1730,21 +1849,6 @@
}
}
- // Create PING for PTO probe.
- if self.recovery.probes > 0 && left >= 1 {
- let frame = frame::Frame::Ping;
-
- payload_len += frame.wire_len();
- left -= frame.wire_len();
-
- frames.push(frame);
-
- self.recovery.probes -= 1;
-
- ack_eliciting = true;
- in_flight = true;
- }
-
// Create CONNECTION_CLOSE frame.
if let Some(err) = self.error {
let frame = frame::Frame::ConnectionClose {
@@ -1790,15 +1894,17 @@
data: challenge.clone(),
};
- payload_len += frame.wire_len();
- left -= frame.wire_len();
+ if left > frame.wire_len() {
+ payload_len += frame.wire_len();
+ left -= frame.wire_len();
- frames.push(frame);
+ frames.push(frame);
- self.challenge = None;
+ self.challenge = None;
- ack_eliciting = true;
- in_flight = true;
+ ack_eliciting = true;
+ in_flight = true;
+ }
}
// Create CRYPTO frame.
@@ -1821,7 +1927,6 @@
ack_eliciting = true;
in_flight = true;
- is_crypto = true;
}
// Create a single STREAM frame for the first stream that is flushable.
@@ -1838,14 +1943,28 @@
};
// Make sure we can fit the data in the packet.
- let stream_len = cmp::min(
- left - frame::MAX_STREAM_OVERHEAD,
- (self.max_tx_data - self.tx_data) as usize,
- );
+ let max_len =
+ cmp::min(left, (self.max_tx_data - self.tx_data) as usize);
- let stream_buf = stream.send.pop(stream_len)?;
+ let off = stream.send.off();
- if stream_buf.is_empty() {
+ // Try to accurately account for the STREAM frame's overhead,
+ // such that we can fill as much of the packet buffer as
+ // possible.
+ let overhead = 1 +
+ octets::varint_len(stream_id) +
+ octets::varint_len(off) +
+ octets::varint_len(max_len as u64);
+
+ let max_len = match max_len.checked_sub(overhead) {
+ Some(v) => v,
+
+ None => continue,
+ };
+
+ let stream_buf = stream.send.pop(max_len)?;
+
+ if stream_buf.is_empty() && !stream_buf.fin() {
continue;
}
@@ -1874,6 +1993,28 @@
}
}
+ // Create PING for PTO probe.
+ if self.recovery.loss_probes[epoch] > 0 &&
+ !ack_eliciting &&
+ left >= 1 &&
+ !is_closing
+ {
+ let frame = frame::Frame::Ping;
+
+ payload_len += frame.wire_len();
+ left -= frame.wire_len();
+
+ frames.push(frame);
+
+ ack_eliciting = true;
+ in_flight = true;
+ }
+
+ if ack_eliciting {
+ self.recovery.loss_probes[epoch] =
+ self.recovery.loss_probes[epoch].saturating_sub(1);
+ }
+
if frames.is_empty() {
return Err(Error::Done);
}
@@ -1954,11 +2095,15 @@
size: if ack_eliciting { written } else { 0 },
ack_eliciting,
in_flight,
- is_crypto,
};
- self.recovery
- .on_packet_sent(sent_pkt, epoch, now, &self.trace_id);
+ self.recovery.on_packet_sent(
+ sent_pkt,
+ epoch,
+ self.is_established(),
+ now,
+ &self.trace_id,
+ );
self.pkt_num_spaces[epoch].next_pkt_num += 1;
@@ -1971,13 +2116,12 @@
self.max_send_bytes = self.max_send_bytes.saturating_sub(written);
- // (Re)start the idle timer if we are sending the first ACK-eliciting
+ // (Re)start the idle timer if we are sending the first ack-eliciting
// packet since last receiving a packet.
- if ack_eliciting &&
- !self.ack_eliciting_sent &&
- self.local_transport_params.idle_timeout > 0
- {
- self.idle_timer = Some(now + self.idle_timeout());
+ if ack_eliciting && !self.ack_eliciting_sent {
+ if let Some(idle_timeout) = self.idle_timeout() {
+ self.idle_timer = Some(now + idle_timeout);
+ }
}
if ack_eliciting {
@@ -2096,11 +2240,18 @@
let sent = stream.send.push_slice(buf, fin)?;
+ let flushable = stream.is_flushable();
+
let writable = stream.is_writable();
+ let empty_fin = buf.is_empty() && fin;
+
// If the stream is now flushable push it to the flushable queue, but
// only if it wasn't already queued.
- if stream.is_flushable() && !was_flushable {
+ //
+ // Consider the stream flushable also when we are sending a zero-length
+ // frame that has the fin flag set.
+ if (flushable || empty_fin) && !was_flushable {
self.streams.push_flushable(stream_id);
}
@@ -2116,7 +2267,7 @@
/// When the `direction` argument is set to [`Shutdown::Read`], outstanding
/// data in the stream's receive buffer is dropped, and no additional data
/// is added to it. Data received after calling this method is still
- /// validated and ACKed but not stored, and [`stream_recv()`] will not
+ /// validated and acked but not stored, and [`stream_recv()`] will not
/// return it to the application.
///
/// When the `direction` argument is set to [`Shutdown::Write`], outstanding
@@ -2182,6 +2333,53 @@
stream.recv.is_fin()
}
+ /// Initializes the stream's application data.
+ ///
+ /// This can be used by applications to store per-stream information without
+ /// having to maintain their own stream map.
+ ///
+ /// Stream data can only be initialized once. Additional calls to this
+ /// method will return [`Done`].
+ ///
+ /// [`Done`]: enum.Error.html#variant.Done
+ pub fn stream_init_application_data<T>(
+ &mut self, stream_id: u64, data: T,
+ ) -> Result<()>
+ where
+ T: std::any::Any,
+ {
+ // Get existing stream.
+ let stream = self.streams.get_mut(stream_id).ok_or(Error::Done)?;
+
+ if stream.data.is_some() {
+ return Err(Error::Done);
+ }
+
+ stream.data = Some(Box::new(data));
+
+ Ok(())
+ }
+
+ /// Returns the stream's application data, if any was initialized.
+ ///
+ /// This returns a reference to the application data that was initialized
+ /// by calling [`stream_init_application_data()`].
+ ///
+ /// [`stream_init_application_data()`]:
+ /// struct.Connection.html#method.stream_init_application_data
+ pub fn stream_application_data(
+ &mut self, stream_id: u64,
+ ) -> Option<&mut dyn std::any::Any> {
+ // Get existing stream.
+ let stream = self.streams.get_mut(stream_id)?;
+
+ if let Some(ref mut stream_data) = stream.data {
+ return Some(stream_data.as_mut());
+ }
+
+ None
+ }
+
/// Returns an iterator over streams that have outstanding data to read.
///
/// Note that the iterator will only include streams that were readable at
@@ -2320,7 +2518,12 @@
if timer <= now {
trace!("{} loss detection timeout expired", self.trace_id);
- self.recovery.on_loss_detection_timeout(now, &self.trace_id);
+ self.recovery.on_loss_detection_timeout(
+ self.is_established(),
+ now,
+ &self.trace_id,
+ );
+
return;
}
}
@@ -2376,9 +2579,14 @@
self.handshake.alpn_protocol()
}
+ /// Returns the peer's leaf certificate (if any) as a DER-encoded buffer.
+ pub fn peer_cert(&self) -> Option<Vec<u8>> {
+ self.handshake.peer_cert()
+ }
+
/// Returns true if the connection handshake is complete.
pub fn is_established(&self) -> bool {
- self.handshake_completed
+ self.handshake.is_completed()
}
/// Returns true if the connection is resumed.
@@ -2386,6 +2594,12 @@
self.handshake.is_resumed()
}
+ /// Returns true if the connection has a pending handshake that has
+ /// progressed enough to send or receive early data.
+ pub fn is_in_early_data(&self) -> bool {
+ self.handshake.is_in_early_data()
+ }
+
/// Returns true if the connection is closed.
///
/// If this returns true, the connection object can be dropped.
@@ -2399,7 +2613,7 @@
recv: self.recv_count,
sent: self.sent_count,
lost: self.recovery.lost_count,
- cwnd: self.recovery.cwnd(),
+ cwnd: self.recovery.cc.cwnd(),
rtt: self.recovery.rtt(),
}
}
@@ -2407,67 +2621,60 @@
/// Continues the handshake.
///
/// If the connection is already established, it does nothing.
- fn do_handshake(&mut self, now: time::Instant) -> Result<()> {
- if !self.handshake_completed {
- match self.handshake.do_handshake() {
- Ok(_) => {
- if self.application_proto().is_empty() {
- // Send no_application_proto TLS alert when no protocol
- // can be negotiated.
- self.error = Some(0x178);
- return Err(Error::TlsFail);
- }
+ fn do_handshake(&mut self) -> Result<()> {
+ // Handshake is already complete, there's nothing to do.
+ if self.is_established() {
+ return Ok(());
+ }
- // Handshake is complete!
- self.handshake_completed = true;
+ match self.handshake.do_handshake() {
+ Ok(_) => (),
- let mut raw_params =
- self.handshake.quic_transport_params().to_vec();
+ Err(Error::Done) => return Ok(()),
- let peer_params =
- TransportParams::decode(&mut raw_params, self.is_server)?;
+ Err(e) => return Err(e),
+ };
- if peer_params.original_connection_id != self.odcid {
- return Err(Error::InvalidTransportParam);
- }
+ if self.application_proto().is_empty() {
+ // Send no_application_proto TLS alert when no protocol
+ // can be negotiated.
+ self.error = Some(0x178);
+ return Err(Error::TlsFail);
+ }
- self.max_tx_data = peer_params.initial_max_data;
+ if !self.parsed_peer_transport_params {
+ let mut raw_params = self.handshake.quic_transport_params().to_vec();
- self.streams.update_peer_max_streams_bidi(
- peer_params.initial_max_streams_bidi,
- );
- self.streams.update_peer_max_streams_uni(
- peer_params.initial_max_streams_uni,
- );
+ let peer_params =
+ TransportParams::decode(&mut raw_params, self.is_server)?;
- self.recovery.max_ack_delay =
- time::Duration::from_millis(peer_params.max_ack_delay);
-
- self.peer_transport_params = peer_params;
-
- trace!("{} connection established: proto={:?} cipher={:?} curve={:?} sigalg={:?} resumed={} {:?}",
- &self.trace_id,
- std::str::from_utf8(self.application_proto()),
- self.handshake.cipher(),
- self.handshake.curve(),
- self.handshake.sigalg(),
- self.is_resumed(),
- self.peer_transport_params);
-
- // Process outstanding frames from early Application packets.
- for f in self
- .early_app_frames
- .drain(..)
- .collect::<Vec<frame::Frame>>()
- {
- self.process_frame(f, packet::EPOCH_APPLICATION, now)?;
- }
- },
-
- Err(Error::Done) => (),
-
- Err(e) => return Err(e),
+ if peer_params.original_connection_id != self.odcid {
+ return Err(Error::InvalidTransportParam);
}
+
+ self.max_tx_data = peer_params.initial_max_data;
+
+ self.streams.update_peer_max_streams_bidi(
+ peer_params.initial_max_streams_bidi,
+ );
+ self.streams
+ .update_peer_max_streams_uni(peer_params.initial_max_streams_uni);
+
+ self.recovery.max_ack_delay =
+ time::Duration::from_millis(peer_params.max_ack_delay);
+
+ self.peer_transport_params = peer_params;
+
+ self.parsed_peer_transport_params = true;
+
+ trace!("{} connection established: proto={:?} cipher={:?} curve={:?} sigalg={:?} resumed={} {:?}",
+ &self.trace_id,
+ std::str::from_utf8(self.application_proto()),
+ self.handshake.cipher(),
+ self.handshake.curve(),
+ self.handshake.sigalg(),
+ self.is_resumed(),
+ self.peer_transport_params);
}
Ok(())
@@ -2475,8 +2682,9 @@
/// Selects the packet number space for outgoing packets.
fn write_epoch(&self) -> Result<packet::Epoch> {
- // On error or probe, send packet in the latest space available.
- if self.error.is_some() || self.recovery.probes > 0 {
+ // On error send packet in the latest epoch available, but only send
+ // 1-RTT ones when the handshake is completed.
+ if self.error.is_some() {
let epoch = match self.handshake.write_level() {
crypto::Level::Initial => packet::EPOCH_INITIAL,
crypto::Level::ZeroRTT => unreachable!(),
@@ -2484,13 +2692,18 @@
crypto::Level::OneRTT => packet::EPOCH_APPLICATION,
};
+ if epoch == packet::EPOCH_APPLICATION && !self.is_established() {
+ // Downgrade the epoch to handshake as the handshake is not
+ // completed yet.
+ return Ok(packet::EPOCH_HANDSHAKE);
+ }
+
return Ok(epoch);
}
for epoch in packet::EPOCH_INITIAL..packet::EPOCH_COUNT {
- // Only use application packet number space when handshake is
- // complete.
- if epoch == packet::EPOCH_APPLICATION && !self.handshake_completed {
+ // Only send 1-RTT packets when handshake is complete.
+ if epoch == packet::EPOCH_APPLICATION && !self.is_established() {
continue;
}
@@ -2503,10 +2716,15 @@
if !self.recovery.lost[epoch].is_empty() {
return Ok(epoch);
}
+
+ // We need to send PTO probe packets.
+ if self.recovery.loss_probes[epoch] > 0 {
+ return Ok(epoch);
+ }
}
// If there are flushable streams, use Application.
- if self.handshake_completed &&
+ if self.is_established() &&
(self.should_update_max_data() ||
self.streams.should_update_max_streams_bidi() ||
self.streams.should_update_max_streams_uni() ||
@@ -2555,14 +2773,14 @@
&ranges,
ack_delay,
epoch,
+ self.is_established(),
now,
&self.trace_id,
)?;
// When we receive an ACK for a 1-RTT packet after handshake
// completion, it means the handshake has been confirmed.
- if epoch == packet::EPOCH_APPLICATION && self.handshake_completed
- {
+ if epoch == packet::EPOCH_APPLICATION && self.is_established() {
self.handshake_confirmed = true;
// Once the handshake is confirmed, we can drop Handshake
@@ -2583,8 +2801,23 @@
return Err(Error::InvalidStreamState);
}
- // Get existing stream or create a new one.
- let stream = self.get_or_create_stream(stream_id, false)?;
+ // Get existing stream or create a new one, but if the stream
+ // has already been closed and collected, ignore the frame.
+ //
+ // This can happen if e.g. an ACK frame is lost, and the peer
+ // retransmits another frame before it realizes that the stream
+ // is gone.
+ //
+ // Note that it makes it impossible to check if the frame is
+ // illegal, since we have no state, but since we ignore the
+ // frame, it should be fine.
+ let stream = match self.get_or_create_stream(stream_id, false) {
+ Ok(v) => v,
+
+ Err(Error::Done) => return Ok(()),
+
+ Err(e) => return Err(e),
+ };
self.rx_data += stream.recv.reset(final_size)? as u64;
@@ -2619,7 +2852,7 @@
self.handshake.provide_data(level, &recv_buf)?;
}
- self.do_handshake(now)?;
+ self.do_handshake()?;
},
// TODO: implement stateless retry
@@ -2640,8 +2873,23 @@
return Err(Error::FlowControl);
}
- // Get existing stream or create a new one.
- let stream = self.get_or_create_stream(stream_id, false)?;
+ // Get existing stream or create a new one, but if the stream
+ // has already been closed and collected, ignore the frame.
+ //
+ // This can happen if e.g. an ACK frame is lost, and the peer
+ // retransmits another frame before it realizes that the stream
+ // is gone.
+ //
+ // Note that it makes it impossible to check if the frame is
+ // illegal, since we have no state, but since we ignore the
+ // frame, it should be fine.
+ let stream = match self.get_or_create_stream(stream_id, false) {
+ Ok(v) => v,
+
+ Err(Error::Done) => return Ok(()),
+
+ Err(e) => return Err(e),
+ };
stream.recv.push(data)?;
@@ -2657,8 +2905,23 @@
},
frame::Frame::MaxStreamData { stream_id, max } => {
- // Get existing stream or create a new one.
- let stream = self.get_or_create_stream(stream_id, false)?;
+ // Get existing stream or create a new one, but if the stream
+ // has already been closed and collected, ignore the frame.
+ //
+ // This can happen if e.g. an ACK frame is lost, and the peer
+ // retransmits another frame before it realizes that the stream
+ // is gone.
+ //
+ // Note that it makes it impossible to check if the frame is
+ // illegal, since we have no state, but since we ignore the
+ // frame, it should be fine.
+ let stream = match self.get_or_create_stream(stream_id, false) {
+ Ok(v) => v,
+
+ Err(Error::Done) => return Ok(()),
+
+ Err(e) => return Err(e),
+ };
let was_flushable = stream.is_flushable();
@@ -2720,6 +2983,21 @@
frame::Frame::ApplicationClose { .. } => {
self.draining_timer = Some(now + (self.recovery.pto() * 3));
},
+
+ frame::Frame::HandshakeDone => {
+ if self.version < PROTOCOL_VERSION_DRAFT25 {
+ return Err(Error::InvalidFrame);
+ }
+
+ if self.is_server {
+ return Err(Error::InvalidPacket);
+ }
+
+ self.handshake_confirmed = true;
+
+ // Once the handshake is confirmed, we can drop Handshake keys.
+ self.drop_epoch_state(packet::EPOCH_HANDSHAKE);
+ },
}
Ok(())
@@ -2749,11 +3027,35 @@
}
/// Returns the idle timeout value.
- fn idle_timeout(&mut self) -> time::Duration {
- cmp::max(
- time::Duration::from_millis(self.local_transport_params.idle_timeout),
- 3 * self.recovery.pto(),
- )
+ ///
+ /// `None` is returned if both end-points disabled the idle timeout.
+ fn idle_timeout(&mut self) -> Option<time::Duration> {
+ // If the transport parameter is set to 0, then the respective endpoint
+ // decided to disable the idle timeout. If both are disabled we should
+ // not set any timeout.
+ if self.local_transport_params.max_idle_timeout == 0 &&
+ self.peer_transport_params.max_idle_timeout == 0
+ {
+ return None;
+ }
+
+ // If the local endpoint or the peer disabled the idle timeout, use the
+ // other peer's value, otherwise use the minimum of the two values.
+ let idle_timeout = if self.local_transport_params.max_idle_timeout == 0 {
+ self.peer_transport_params.max_idle_timeout
+ } else if self.peer_transport_params.max_idle_timeout == 0 {
+ self.local_transport_params.max_idle_timeout
+ } else {
+ cmp::min(
+ self.local_transport_params.max_idle_timeout,
+ self.peer_transport_params.max_idle_timeout,
+ )
+ };
+
+ let idle_timeout = time::Duration::from_millis(idle_timeout);
+ let idle_timeout = cmp::max(idle_timeout, 3 * self.recovery.pto());
+
+ Some(idle_timeout)
}
}
@@ -2784,8 +3086,8 @@
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
- "recv={} sent={} lost={} rtt={:?}",
- self.recv, self.sent, self.lost, self.rtt
+ "recv={} sent={} lost={} rtt={:?} cwnd={}",
+ self.recv, self.sent, self.lost, self.rtt, self.cwnd
)
}
}
@@ -2793,7 +3095,7 @@
#[derive(Clone, PartialEq)]
struct TransportParams {
pub original_connection_id: Option<Vec<u8>>,
- pub idle_timeout: u64,
+ pub max_idle_timeout: u64,
pub stateless_reset_token: Option<Vec<u8>>,
pub max_packet_size: u64,
pub initial_max_data: u64,
@@ -2813,7 +3115,7 @@
fn default() -> TransportParams {
TransportParams {
original_connection_id: None,
- idle_timeout: 0,
+ max_idle_timeout: 0,
stateless_reset_token: None,
max_packet_size: 65527,
initial_max_data: 0,
@@ -2855,7 +3157,7 @@
},
0x0001 => {
- tp.idle_timeout = val.get_varint()?;
+ tp.max_idle_timeout = val.get_varint()?;
},
0x0002 => {
@@ -2868,6 +3170,10 @@
0x0003 => {
tp.max_packet_size = val.get_varint()?;
+
+ if tp.max_packet_size < 1200 {
+ return Err(Error::InvalidTransportParam);
+ }
},
0x0004 => {
@@ -2966,10 +3272,10 @@
}
};
- if tp.idle_timeout != 0 {
+ if tp.max_idle_timeout != 0 {
b.put_u16(0x0001)?;
- b.put_u16(octets::varint_len(tp.idle_timeout) as u16)?;
- b.put_varint(tp.idle_timeout)?;
+ b.put_u16(octets::varint_len(tp.max_idle_timeout) as u16)?;
+ b.put_varint(tp.max_idle_timeout)?;
}
if let Some(ref token) = tp.stateless_reset_token {
@@ -3071,7 +3377,7 @@
impl std::fmt::Debug for TransportParams {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
- write!(f, "idle_timeout={} ", self.idle_timeout)?;
+ write!(f, "max_idle_timeout={} ", self.max_idle_timeout)?;
write!(f, "max_packet_size={} ", self.max_packet_size)?;
write!(f, "initial_max_data={} ", self.initial_max_data)?;
write!(
@@ -3116,8 +3422,8 @@
use super::*;
pub struct Pipe {
- pub client: Box<Connection>,
- pub server: Box<Connection>,
+ pub client: Pin<Box<Connection>>,
+ pub server: Pin<Box<Connection>>,
}
impl Pipe {
@@ -3329,7 +3635,6 @@
scid: conn.scid.clone(),
pkt_num: 0,
pkt_num_len: pn_len,
- odcid: None,
token: conn.token.clone(),
versions: None,
key_phase: false,
@@ -3337,8 +3642,8 @@
hdr.to_bytes(&mut b)?;
- let payload_len =
- frames.iter().fold(0, |acc, x| acc + x.wire_len()) + space.overhead();
+ let payload_len = frames.iter().fold(0, |acc, x| acc + x.wire_len()) +
+ space.overhead().unwrap();
if pkt_type != packet::Type::Short {
let len = pn_len + payload_len;
@@ -3416,7 +3721,7 @@
fn transport_params() {
let tp = TransportParams {
original_connection_id: None,
- idle_timeout: 30,
+ max_idle_timeout: 30,
stateless_reset_token: Some(vec![0xba; 16]),
max_packet_size: 23_421,
initial_max_data: 424_645_563,
@@ -3501,69 +3806,46 @@
// Server sends initial flight.
len = testing::recv_send(&mut pipe.server, &mut buf, len).unwrap();
- assert!(!pipe.client.handshake_completed);
+ assert!(!pipe.client.is_established());
assert!(!pipe.client.handshake_confirmed);
- assert!(!pipe.server.handshake_completed);
+ assert!(!pipe.server.is_established());
assert!(!pipe.server.handshake_confirmed);
// Client sends Handshake packet and completes handshake.
len = testing::recv_send(&mut pipe.client, &mut buf, len).unwrap();
- assert!(pipe.client.handshake_completed);
+ assert!(pipe.client.is_established());
assert!(!pipe.client.handshake_confirmed);
- assert!(!pipe.server.handshake_completed);
+ assert!(!pipe.server.is_established());
assert!(!pipe.server.handshake_confirmed);
- // Server completes handshake.
+ // Server completes handshake and sends HANDSHAKE_DONE.
len = testing::recv_send(&mut pipe.server, &mut buf, len).unwrap();
- assert!(pipe.client.handshake_completed);
+ assert!(pipe.client.is_established());
assert!(!pipe.client.handshake_confirmed);
- assert!(pipe.server.handshake_completed);
+ assert!(pipe.server.is_established());
assert!(!pipe.server.handshake_confirmed);
- // Client ACKs 1-RTT packet.
+ // Client acks 1-RTT packet, and confirms handshake.
len = testing::recv_send(&mut pipe.client, &mut buf, len).unwrap();
- assert!(pipe.client.handshake_completed);
- assert!(!pipe.client.handshake_confirmed);
+ assert!(pipe.client.is_established());
+ assert!(pipe.client.handshake_confirmed);
- assert!(pipe.server.handshake_completed);
+ assert!(pipe.server.is_established());
assert!(!pipe.server.handshake_confirmed);
// Server handshake is confirmed.
- len = testing::recv_send(&mut pipe.server, &mut buf, len).unwrap();
+ testing::recv_send(&mut pipe.server, &mut buf, len).unwrap();
- assert!(pipe.client.handshake_completed);
- assert!(!pipe.client.handshake_confirmed);
-
- assert!(pipe.server.handshake_completed);
- assert!(pipe.server.handshake_confirmed);
-
- // Client sends 1-RTT ACK-eliciting packet.
- assert_eq!(pipe.client.stream_send(0, b"a", false), Ok(1));
-
- len = testing::recv_send(&mut pipe.client, &mut buf, len).unwrap();
-
- // Server ACKs 1-RTT packet.
- len = testing::recv_send(&mut pipe.server, &mut buf, len).unwrap();
-
- assert!(pipe.client.handshake_completed);
- assert!(!pipe.client.handshake_confirmed);
-
- assert!(pipe.server.handshake_completed);
- assert!(pipe.server.handshake_confirmed);
-
- // Client handshake is confirmed.
- testing::recv_send(&mut pipe.client, &mut buf, len).unwrap();
-
- assert!(pipe.client.handshake_completed);
+ assert!(pipe.client.is_established());
assert!(pipe.client.handshake_confirmed);
- assert!(pipe.server.handshake_completed);
+ assert!(pipe.server.is_established());
assert!(pipe.server.handshake_confirmed);
}
@@ -4116,9 +4398,9 @@
}
#[test]
- /// Simulates reception of an early Application packet on the server, by
+ /// Simulates reception of an early 1-RTT packet on the server, by
/// delaying the client's Handshake packet that completes the handshake.
- fn buffer_early_app_frames() {
+ fn early_1rtt_packet() {
let mut buf = [0; 65535];
let mut pipe = testing::Pipe::default().unwrap();
@@ -4138,90 +4420,39 @@
testing::recv_send(&mut pipe.server, &mut buf, 0).unwrap();
assert!(pipe.client.is_established());
- assert_eq!(pipe.client.streams.iter_mut().len(), 0);
+ // Send 1-RTT packet #0.
+ assert_eq!(pipe.client.stream_send(0, b"hello, world", true), Ok(12));
+ assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+ // Send 1-RTT packet #1.
assert_eq!(pipe.client.stream_send(4, b"hello, world", true), Ok(12));
assert_eq!(pipe.advance(&mut buf), Ok(()));
- assert_eq!(pipe.client.streams.iter_mut().len(), 1);
-
assert!(!pipe.server.is_established());
- assert_eq!(pipe.server.streams.iter_mut().len(), 0);
+
+ // Client sent 1-RTT packets 0 and 1, but server hasn't received them.
+ //
+ // Note that `largest_rx_pkt_num` is initialized to 0, so we need to
+ // send another 1-RTT packet to make this check meaningful.
+ assert_eq!(
+ pipe.server.pkt_num_spaces[packet::EPOCH_APPLICATION]
+ .largest_rx_pkt_num,
+ 0
+ );
// Process delayed packet.
pipe.server.recv(&mut delayed).unwrap();
assert!(pipe.server.is_established());
- assert_eq!(pipe.server.streams.iter_mut().len(), 1);
- assert_eq!(pipe.client.stats().sent, pipe.server.stats().recv);
- }
+ assert_eq!(
+ pipe.server.pkt_num_spaces[packet::EPOCH_APPLICATION]
+ .largest_rx_pkt_num,
+ 0
+ );
- #[test]
- /// Simulates reception of multiple early Application packets on the server
- /// exceeding the limit imposed for buffering of their frames.
- fn buffer_early_app_frames_limit() {
- let mut buf = [0; 65535];
-
- let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
- config
- .load_cert_chain_from_pem_file("examples/cert.crt")
- .unwrap();
- config
- .load_priv_key_from_pem_file("examples/cert.key")
- .unwrap();
- config
- .set_application_protos(b"\x06proto1\x06proto2")
- .unwrap();
- config.set_initial_max_data(256);
- config.set_initial_max_stream_data_bidi_local(256);
- config.set_initial_max_stream_data_bidi_remote(256);
- config.set_initial_max_streams_bidi(13);
- config.set_initial_max_streams_uni(13);
- config.verify_peer(false);
-
- let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
-
- // Client sends initial flight
- let mut len = pipe.client.send(&mut buf).unwrap();
-
- // Server sends initial flight..
- len = testing::recv_send(&mut pipe.server, &mut buf, len).unwrap();
-
- // Client sends Handshake packet.
- len = testing::recv_send(&mut pipe.client, &mut buf, len).unwrap();
-
- // Emulate handshake packet delay by not making server process client
- // packet.
- let mut delayed = (&buf[..len]).to_vec();
- testing::recv_send(&mut pipe.server, &mut buf, 0).unwrap();
-
- assert!(pipe.client.is_established());
- assert_eq!(pipe.client.streams.iter_mut().len(), 0);
-
- // Client sends `INITIAL_WINDOW_PACKETS` + 1 Application packets to
- // trigger the server's limit.
- for i in 1..=recovery::INITIAL_WINDOW_PACKETS + 1 {
- pipe.client
- .stream_send(i as u64 * 4, b"hello, world", true)
- .unwrap();
- pipe.advance(&mut buf).unwrap();
- }
-
- assert_eq!(pipe.client.streams.iter_mut().len(), 11);
-
- assert!(!pipe.server.is_established());
- assert_eq!(pipe.server.streams.iter_mut().len(), 0);
-
- // Process delayed packet.
- pipe.server.recv(&mut delayed).unwrap();
-
- // Server received `INITIAL_WINDOW_PACKETS` Application packets and
- // dropped the 11th.
- assert!(pipe.server.is_established());
- assert_eq!(pipe.server.streams.iter_mut().len(), 10);
-
- assert_eq!(pipe.client.stats().sent, pipe.server.stats().recv + 1);
+ assert_eq!(pipe.client.stats().sent, pipe.server.stats().recv + 2);
}
#[test]
@@ -4696,12 +4927,173 @@
assert_eq!(pipe.server.readable().len(), 3);
}
+
+ #[test]
+ /// Tests that the stream's fin flag is properly flushed even if there's no
+ /// data in the buffer, and that the buffer becomes readable on the other
+ /// side.
+ fn stream_zero_length_fin() {
+ let mut buf = [0; 65535];
+
+ let mut pipe = testing::Pipe::default().unwrap();
+
+ assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+ assert_eq!(
+ pipe.client.stream_send(0, b"aaaaaaaaaaaaaaa", false),
+ Ok(15)
+ );
+ assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+ let mut r = pipe.server.readable();
+ assert_eq!(r.next(), Some(0));
+ assert!(r.next().is_none());
+
+ let mut b = [0; 15];
+ pipe.server.stream_recv(0, &mut b).unwrap();
+ assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+ // Client sends zero-length frame.
+ assert_eq!(pipe.client.stream_send(0, b"", true), Ok(0));
+ assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+ // Stream should be readable on the server after receiving empty fin.
+ let mut r = pipe.server.readable();
+ assert_eq!(r.next(), Some(0));
+ assert!(r.next().is_none());
+
+ let mut b = [0; 15];
+ pipe.server.stream_recv(0, &mut b).unwrap();
+ assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+ // Client sends zero-length frame (again).
+ assert_eq!(pipe.client.stream_send(0, b"", true), Ok(0));
+ assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+ // Stream should _not_ be readable on the server after receiving empty
+ // fin, because it was already finished.
+ let mut r = pipe.server.readable();
+ assert_eq!(r.next(), None);
+ }
+
+ #[test]
+ /// Tests that completed streams are garbage collected.
+ fn collect_streams() {
+ let mut buf = [0; 65535];
+
+ let mut pipe = testing::Pipe::default().unwrap();
+
+ assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+ assert_eq!(pipe.client.streams.len(), 0);
+ assert_eq!(pipe.server.streams.len(), 0);
+
+ assert_eq!(pipe.client.stream_send(0, b"aaaaa", true), Ok(5));
+ assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+ assert!(!pipe.client.stream_finished(0));
+ assert!(!pipe.server.stream_finished(0));
+
+ assert_eq!(pipe.client.streams.len(), 1);
+ assert_eq!(pipe.server.streams.len(), 1);
+
+ let mut b = [0; 5];
+ pipe.server.stream_recv(0, &mut b).unwrap();
+ assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+ assert_eq!(pipe.server.stream_send(0, b"aaaaa", true), Ok(5));
+ assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+ assert!(!pipe.client.stream_finished(0));
+ assert!(pipe.server.stream_finished(0));
+
+ assert_eq!(pipe.client.streams.len(), 1);
+ assert_eq!(pipe.server.streams.len(), 0);
+
+ let mut b = [0; 5];
+ pipe.client.stream_recv(0, &mut b).unwrap();
+ assert_eq!(pipe.advance(&mut buf), Ok(()));
+
+ assert_eq!(pipe.client.streams.len(), 0);
+ assert_eq!(pipe.server.streams.len(), 0);
+
+ assert!(pipe.client.stream_finished(0));
+ assert!(pipe.server.stream_finished(0));
+
+ assert_eq!(pipe.client.stream_send(0, b"", true), Err(Error::Done));
+
+ let frames = [frame::Frame::Stream {
+ stream_id: 0,
+ data: stream::RangeBuf::from(b"aa", 0, false),
+ }];
+
+ let pkt_type = packet::Type::Short;
+ assert_eq!(pipe.send_pkt_to_server(pkt_type, &frames, &mut buf), Ok(39));
+ }
+
+ #[test]
+ fn config_set_cc_algorithm_name() {
+ let mut config = Config::new(PROTOCOL_VERSION).unwrap();
+
+ assert_eq!(config.set_cc_algorithm_name("reno"), Ok(()));
+
+ // Unknown name.
+ assert_eq!(
+ config.set_cc_algorithm_name("???"),
+ Err(Error::CongestionControl)
+ );
+ }
+
+ #[test]
+ fn peer_cert() {
+ let mut buf = [0; 65535];
+
+ let mut pipe = testing::Pipe::default().unwrap();
+
+ assert_eq!(pipe.handshake(&mut buf), Ok(()));
+
+ match pipe.client.peer_cert() {
+ Some(c) => assert_eq!(c.len(), 919),
+
+ None => panic!("missing server certificate"),
+ }
+ }
+
+ #[test]
+ fn retry() {
+ let mut buf = [0; 65535];
+
+ let mut pipe = testing::Pipe::default().unwrap();
+
+ // Client sends initial flight.
+ let mut len = pipe.client.send(&mut buf).unwrap();
+
+ // Server sends Retry packet.
+ let hdr = Header::from_slice(&mut buf[..len], MAX_CONN_ID_LEN).unwrap();
+
+ let mut scid = [0; MAX_CONN_ID_LEN];
+ rand::rand_bytes(&mut scid[..]);
+
+ let token = b"quiche test retry token";
+
+ len =
+ packet::retry(&hdr.scid, &hdr.dcid, &scid, token, &mut buf).unwrap();
+
+ // Client receives Retry and sends new Initial.
+ assert_eq!(pipe.client.recv(&mut buf[..len]), Err(Error::Done));
+
+ len = pipe.client.send(&mut buf).unwrap();
+
+ let hdr = Header::from_slice(&mut buf[..len], MAX_CONN_ID_LEN).unwrap();
+ assert_eq!(&hdr.token.unwrap(), token);
+ }
}
pub use crate::packet::Header;
pub use crate::packet::Type;
pub use crate::stream::StreamIter;
+mod cc;
mod crypto;
mod ffi;
mod frame;
diff --git a/src/octets.rs b/src/octets.rs
index 98b3c4b..ed1e1e1 100644
--- a/src/octets.rs
+++ b/src/octets.rs
@@ -1,5 +1,4 @@
-// Copyright (C) 2018, Cloudflare, Inc.
-// Copyright (C) 2018, Alessandro Ghedini
+// Copyright (C) 2018-2019, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -396,6 +395,11 @@
self.off
}
+ /// Returns a reference to the internal buffer.
+ pub fn buf(&self) -> &[u8] {
+ self.buf
+ }
+
/// Copies the buffer from the current offset into a new `Vec<u8>`.
pub fn to_vec(&self) -> Vec<u8> {
self.as_ref().to_vec()
diff --git a/src/packet.rs b/src/packet.rs
index 45bb836..c9a78ac 100644
--- a/src/packet.rs
+++ b/src/packet.rs
@@ -1,5 +1,4 @@
-// Copyright (C) 2018, Cloudflare, Inc.
-// Copyright (C) 2018, Alessandro Ghedini
+// Copyright (C) 2018-2019, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -27,6 +26,8 @@
use std::time;
+use ring::aead;
+
use crate::Error;
use crate::Result;
@@ -125,10 +126,6 @@
/// The source connection ID of the packet.
pub scid: Vec<u8>,
- /// The original destination connection ID. Only present in `Retry`
- /// packets.
- pub odcid: Option<Vec<u8>>,
-
/// The packet number. It's only meaningful after the header protection is
/// removed.
pub(crate) pkt_num: u64,
@@ -187,7 +184,6 @@
version: 0,
dcid: dcid.to_vec(),
scid: Vec::new(),
- odcid: None,
pkt_num: 0,
pkt_num_len: 0,
token: None,
@@ -212,20 +208,19 @@
};
let dcid_len = b.get_u8()?;
- if version == crate::PROTOCOL_VERSION && dcid_len > MAX_CID_LEN {
+ if crate::version_is_supported(version) && dcid_len > MAX_CID_LEN {
return Err(Error::InvalidPacket);
}
let dcid = b.get_bytes(dcid_len as usize)?.to_vec();
let scid_len = b.get_u8()?;
- if version == crate::PROTOCOL_VERSION && scid_len > MAX_CID_LEN {
+ if crate::version_is_supported(version) && scid_len > MAX_CID_LEN {
return Err(Error::InvalidPacket);
}
let scid = b.get_bytes(scid_len as usize)?.to_vec();
// End of invariants.
- let mut odcid: Option<Vec<u8>> = None;
let mut token: Option<Vec<u8>> = None;
let mut versions: Option<Vec<u32>> = None;
@@ -235,14 +230,13 @@
},
Type::Retry => {
- let odcid_len = b.get_u8()?;
-
- if odcid_len > MAX_CID_LEN {
+ // Exclude the integrity tag from the token.
+ if b.cap() < aead::AES_128_GCM.tag_len() {
return Err(Error::InvalidPacket);
}
- odcid = Some(b.get_bytes(odcid_len as usize)?.to_vec());
- token = Some(b.to_vec());
+ let token_len = b.cap() - aead::AES_128_GCM.tag_len();
+ token = Some(b.get_bytes(token_len)?.to_vec());
},
Type::VersionNegotiation => {
@@ -264,7 +258,6 @@
version,
dcid,
scid,
- odcid,
pkt_num: 0,
pkt_num_len: 0,
token,
@@ -321,30 +314,28 @@
out.put_u8(self.scid.len() as u8)?;
out.put_bytes(&self.scid)?;
- if self.ty == Type::Retry {
- let odcid = self.odcid.as_ref().unwrap();
- out.put_u8(odcid.len() as u8)?;
- out.put_bytes(odcid)?;
- }
-
// Only Initial and Retry packets have a token.
- if self.ty == Type::Initial {
- match self.token {
- Some(ref v) => {
- out.put_varint(v.len() as u64)?;
- out.put_bytes(v)?;
- },
+ match self.ty {
+ Type::Initial => {
+ match self.token {
+ Some(ref v) => {
+ out.put_varint(v.len() as u64)?;
+ out.put_bytes(v)?;
+ },
- // No token, so length = 0.
- None => {
- out.put_varint(0)?;
- },
- }
- }
+ // No token, so length = 0.
+ None => {
+ out.put_varint(0)?;
+ },
+ }
+ },
- // Retry packets don't have a token length.
- if self.ty == Type::Retry {
- out.put_bytes(self.token.as_ref().unwrap())?;
+ Type::Retry => {
+ // Retry packets don't have a token length.
+ out.put_bytes(self.token.as_ref().unwrap())?;
+ },
+
+ _ => (),
}
Ok(())
@@ -378,13 +369,6 @@
}
}
- if let Some(ref odcid) = self.odcid {
- write!(f, " odcid=")?;
- for b in odcid {
- write!(f, "{:02x}", b)?;
- }
- }
-
if let Some(ref token) = self.token {
write!(f, " token=")?;
for b in token {
@@ -484,11 +468,12 @@
let pn_mask = pn_win - 1;
let candidate_pn = (expected_pn & !pn_mask) | truncated_pn;
- if candidate_pn + pn_hwin <= expected_pn {
+ if candidate_pn + pn_hwin <= expected_pn && candidate_pn < (1 << 62) - pn_win
+ {
return candidate_pn + pn_win;
}
- if candidate_pn > expected_pn + pn_hwin && candidate_pn > pn_win {
+ if candidate_pn > expected_pn + pn_hwin && candidate_pn >= pn_win {
return candidate_pn - pn_win;
}
@@ -588,6 +573,8 @@
b.put_u8(dcid.len() as u8)?;
b.put_bytes(&dcid)?;
b.put_u32(crate::PROTOCOL_VERSION)?;
+ b.put_u32(crate::PROTOCOL_VERSION_DRAFT24)?;
+ b.put_u32(crate::PROTOCOL_VERSION_DRAFT23)?;
Ok(b.off())
}
@@ -604,7 +591,6 @@
scid: new_scid.to_vec(),
pkt_num: 0,
pkt_num_len: 0,
- odcid: Some(dcid.to_vec()),
token: Some(token.to_vec()),
versions: None,
key_phase: false,
@@ -612,9 +598,60 @@
hdr.to_bytes(&mut b)?;
+ let tag = compute_retry_integrity_tag(&b, dcid)?;
+
+ b.put_bytes(tag.as_ref())?;
+
Ok(b.off())
}
+pub fn verify_retry_integrity(b: &octets::Octets, odcid: &[u8]) -> Result<()> {
+ let tag = compute_retry_integrity_tag(b, odcid)?;
+
+ ring::constant_time::verify_slices_are_equal(
+ &b.as_ref()[..aead::AES_128_GCM.tag_len()],
+ tag.as_ref(),
+ )
+ .map_err(|_| Error::CryptoFail)?;
+
+ Ok(())
+}
+
+fn compute_retry_integrity_tag(
+ b: &octets::Octets, odcid: &[u8],
+) -> Result<aead::Tag> {
+ const RETRY_INTEGRITY_KEY: [u8; 16] = [
+ 0x4d, 0x32, 0xec, 0xdb, 0x2a, 0x21, 0x33, 0xc8, 0x41, 0xe4, 0x04, 0x3d,
+ 0xf2, 0x7d, 0x44, 0x30,
+ ];
+
+ const RETRY_INTEGRITY_NONCE: [u8; aead::NONCE_LEN] = [
+ 0x4d, 0x16, 0x11, 0xd0, 0x55, 0x13, 0xa5, 0x52, 0xc5, 0x87, 0xd5, 0x75,
+ ];
+
+ let hdr_len = b.off();
+
+ let mut pseudo = vec![0; 1 + odcid.len() + hdr_len];
+
+ let mut pb = octets::Octets::with_slice(&mut pseudo);
+
+ pb.put_u8(odcid.len() as u8)?;
+ pb.put_bytes(odcid)?;
+ pb.put_bytes(&b.buf()[..hdr_len])?;
+
+ let key = aead::LessSafeKey::new(
+ aead::UnboundKey::new(&aead::AES_128_GCM, &RETRY_INTEGRITY_KEY)
+ .map_err(|_| Error::CryptoFail)?,
+ );
+
+ let nonce = aead::Nonce::assume_unique_for_key(RETRY_INTEGRITY_NONCE);
+
+ let aad = aead::Aad::from(&pseudo);
+
+ key.seal_in_place_separate_tag(nonce, aad, &mut [])
+ .map_err(|_| Error::CryptoFail)
+}
+
pub struct PktNumSpace {
pub largest_rx_pkt_num: u64,
@@ -631,6 +668,9 @@
pub crypto_open: Option<crypto::Open>,
pub crypto_seal: Option<crypto::Seal>,
+ pub crypto_0rtt_open: Option<crypto::Open>,
+ pub crypto_0rtt_seal: Option<crypto::Seal>,
+
pub crypto_stream: stream::Stream,
}
@@ -652,6 +692,9 @@
crypto_open: None,
crypto_seal: None,
+ crypto_0rtt_open: None,
+ crypto_0rtt_seal: None,
+
crypto_stream: stream::Stream::new(
std::u64::MAX,
std::u64::MAX,
@@ -668,8 +711,8 @@
self.ack_elicited = false;
}
- pub fn overhead(&self) -> usize {
- self.crypto_seal.as_ref().unwrap().alg().tag_len()
+ pub fn overhead(&self) -> Option<usize> {
+ Some(self.crypto_seal.as_ref()?.alg().tag_len())
}
pub fn ready(&self) -> bool {
@@ -740,17 +783,19 @@
scid: vec![0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb],
pkt_num: 0,
pkt_num_len: 0,
- odcid: Some(vec![0x01, 0x02, 0x03, 0x04]),
token: Some(vec![0xba; 24]),
versions: None,
key_phase: false,
};
- let mut d = [0; 52];
+ let mut d = [0; 63];
let mut b = octets::Octets::with_slice(&mut d);
assert!(hdr.to_bytes(&mut b).is_ok());
+ // Add fake retry integrity token.
+ b.put_bytes(&vec![0xba; 16]).unwrap();
+
let mut b = octets::Octets::with_slice(&mut d);
assert_eq!(Header::from_bytes(&mut b, 9).unwrap(), hdr);
}
@@ -764,7 +809,6 @@
scid: vec![0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb],
pkt_num: 0,
pkt_num_len: 0,
- odcid: None,
token: Some(vec![0x05, 0x06, 0x07, 0x08]),
versions: None,
key_phase: false,
@@ -791,7 +835,6 @@
scid: vec![0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb],
pkt_num: 0,
pkt_num_len: 0,
- odcid: None,
token: Some(vec![0x05, 0x06, 0x07, 0x08]),
versions: None,
key_phase: false,
@@ -818,7 +861,6 @@
],
pkt_num: 0,
pkt_num_len: 0,
- odcid: None,
token: Some(vec![0x05, 0x06, 0x07, 0x08]),
versions: None,
key_phase: false,
@@ -845,7 +887,6 @@
],
pkt_num: 0,
pkt_num_len: 0,
- odcid: None,
token: Some(vec![0x05, 0x06, 0x07, 0x08]),
versions: None,
key_phase: false,
@@ -869,7 +910,6 @@
scid: vec![0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb],
pkt_num: 0,
pkt_num_len: 0,
- odcid: None,
token: None,
versions: None,
key_phase: false,
@@ -893,7 +933,6 @@
scid: vec![],
pkt_num: 0,
pkt_num_len: 0,
- odcid: None,
token: None,
versions: None,
key_phase: false,
@@ -1541,7 +1580,6 @@
version: crate::PROTOCOL_VERSION,
dcid: Vec::new(),
scid: Vec::new(),
- odcid: None,
pkt_num: 0,
pkt_num_len: 0,
token: None,
diff --git a/src/rand.rs b/src/rand.rs
index 5290939..dbd0a7f 100644
--- a/src/rand.rs
+++ b/src/rand.rs
@@ -1,5 +1,4 @@
-// Copyright (C) 2018, Cloudflare, Inc.
-// Copyright (C) 2018, Alessandro Ghedini
+// Copyright (C) 2018-2019, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -47,6 +46,19 @@
u64::from_ne_bytes(buf)
}
+pub fn rand_u64_uniform(max: u64) -> u64 {
+ let chunk_size = u64::max_value() / max;
+ let end_of_last_chunk = chunk_size * max;
+
+ let mut r = rand_u64();
+
+ while r >= end_of_last_chunk {
+ r = rand_u64();
+ }
+
+ r / chunk_size
+}
+
extern {
fn RAND_bytes(buf: *mut u8, len: libc::size_t) -> libc::c_int;
}
diff --git a/src/ranges.rs b/src/ranges.rs
index 622aa60..3d1f718 100644
--- a/src/ranges.rs
+++ b/src/ranges.rs
@@ -1,5 +1,4 @@
-// Copyright (C) 2018, Cloudflare, Inc.
-// Copyright (C) 2018, Alessandro Ghedini
+// Copyright (C) 2018-2019, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
diff --git a/src/recovery.rs b/src/recovery.rs
index 760daeb..a0c22d4 100644
--- a/src/recovery.rs
+++ b/src/recovery.rs
@@ -1,5 +1,4 @@
-// Copyright (C) 2018, Cloudflare, Inc.
-// Copyright (C) 2018, Alessandro Ghedini
+// Copyright (C) 2018-2019, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -32,9 +31,11 @@
use std::collections::BTreeMap;
+use crate::Config;
use crate::Error;
use crate::Result;
+use crate::cc;
use crate::frame;
use crate::packet;
use crate::ranges;
@@ -48,14 +49,6 @@
const INITIAL_RTT: Duration = Duration::from_millis(500);
-// Congestion Control
-pub const INITIAL_WINDOW_PACKETS: usize = 10;
-
-const MAX_DATAGRAM_SIZE: usize = 1452;
-
-const INITIAL_WINDOW: usize = INITIAL_WINDOW_PACKETS * MAX_DATAGRAM_SIZE;
-const MINIMUM_WINDOW: usize = 2 * MAX_DATAGRAM_SIZE;
-
const PERSISTENT_CONGESTION_THRESHOLD: u32 = 3;
#[derive(Debug)]
@@ -71,20 +64,14 @@
pub ack_eliciting: bool,
pub in_flight: bool,
-
- pub is_crypto: bool,
}
pub struct Recovery {
loss_detection_timer: Option<Instant>,
- crypto_count: u32,
-
pto_count: u32,
- time_of_last_sent_ack_eliciting_pkt: Instant,
-
- time_of_last_sent_crypto_pkt: Instant,
+ time_of_last_sent_ack_eliciting_pkt: [Option<Instant>; packet::EPOCH_COUNT],
largest_acked_pkt: [u64; packet::EPOCH_COUNT],
@@ -110,33 +97,21 @@
pub lost_count: usize,
- bytes_in_flight: usize,
+ pub loss_probes: [usize; packet::EPOCH_COUNT],
- crypto_bytes_in_flight: usize,
+ pub cc: Box<dyn cc::CongestionControl>,
- cwnd: usize,
-
- recovery_start_time: Option<Instant>,
-
- ssthresh: usize,
-
- pub probes: usize,
+ app_limited: bool,
}
-impl Default for Recovery {
- fn default() -> Recovery {
- let now = Instant::now();
-
+impl Recovery {
+ pub fn new(config: &Config) -> Self {
Recovery {
loss_detection_timer: None,
- crypto_count: 0,
-
pto_count: 0,
- time_of_last_sent_crypto_pkt: now,
-
- time_of_last_sent_ack_eliciting_pkt: now,
+ time_of_last_sent_ack_eliciting_pkt: [None; packet::EPOCH_COUNT],
largest_acked_pkt: [std::u64::MAX; packet::EPOCH_COUNT],
@@ -162,51 +137,39 @@
lost_count: 0,
- bytes_in_flight: 0,
+ loss_probes: [0; packet::EPOCH_COUNT],
- crypto_bytes_in_flight: 0,
+ cc: cc::new_congestion_control(config.cc_algorithm),
- cwnd: INITIAL_WINDOW,
-
- recovery_start_time: None,
-
- ssthresh: std::usize::MAX,
-
- probes: 0,
+ app_limited: false,
}
}
-}
-impl Recovery {
pub fn on_packet_sent(
- &mut self, pkt: Sent, epoch: packet::Epoch, now: Instant, trace_id: &str,
+ &mut self, pkt: Sent, epoch: packet::Epoch, handshake_completed: bool,
+ now: Instant, trace_id: &str,
) {
- let pkt_num = pkt.pkt_num;
let ack_eliciting = pkt.ack_eliciting;
let in_flight = pkt.in_flight;
- let is_crypto = pkt.is_crypto;
let sent_bytes = pkt.size;
self.largest_sent_pkt[epoch] =
cmp::max(self.largest_sent_pkt[epoch], pkt.pkt_num);
- self.sent[epoch].insert(pkt_num, pkt);
+ self.sent[epoch].insert(pkt.pkt_num, pkt);
if in_flight {
- if is_crypto {
- self.time_of_last_sent_crypto_pkt = now;
-
- self.crypto_bytes_in_flight += sent_bytes;
- }
-
if ack_eliciting {
- self.time_of_last_sent_ack_eliciting_pkt = now;
+ self.time_of_last_sent_ack_eliciting_pkt[epoch] = Some(now);
}
+ self.app_limited =
+ (self.cc.bytes_in_flight() + sent_bytes) < self.cc.cwnd();
+
// OnPacketSentCC
- self.bytes_in_flight += sent_bytes;
+ self.cc.on_packet_sent_cc(sent_bytes, trace_id);
- self.set_loss_detection_timer();
+ self.set_loss_detection_timer(handshake_completed);
}
trace!("{} {:?}", trace_id, self);
@@ -214,11 +177,12 @@
pub fn on_ack_received(
&mut self, ranges: &ranges::RangeSet, ack_delay: u64,
- epoch: packet::Epoch, now: Instant, trace_id: &str,
+ epoch: packet::Epoch, handshake_completed: bool, now: Instant,
+ trace_id: &str,
) -> Result<()> {
let largest_acked = ranges.largest().unwrap();
- // If the largest packet number ACKed exceeds any packet number we have
+ // If the largest packet number acked exceeds any packet number we have
// sent, then the ACK is obviously invalid, so there's no need to
// continue further.
if largest_acked > self.largest_sent_pkt[epoch] {
@@ -252,10 +216,24 @@
let mut has_newly_acked = false;
- // Processing ACKed packets in reverse order (from largest to smallest)
+ // Processing acked packets in reverse order (from largest to smallest)
// appears to be faster, possibly due to the BTreeMap implementation.
for pn in ranges.flatten().rev() {
- let newly_acked = self.on_packet_acked(pn, epoch);
+ // If the acked packet number is lower than the lowest unacked packet
+ // number it means that the packet is not newly acked, so return
+ // early.
+ //
+ // Since we process acked packets from largest to lowest, this means
+ // that as soon as we see an already-acked packet number
+ // all following packet numbers will also be already
+ // acked.
+ if let Some(lowest) = self.sent[epoch].values().nth(0) {
+ if pn < lowest.pkt_num {
+ break;
+ }
+ }
+
+ let newly_acked = self.on_packet_acked(pn, epoch, trace_id);
has_newly_acked = cmp::max(has_newly_acked, newly_acked);
if newly_acked {
@@ -269,56 +247,57 @@
self.detect_lost_packets(epoch, now, trace_id);
- self.crypto_count = 0;
self.pto_count = 0;
- self.set_loss_detection_timer();
+ self.set_loss_detection_timer(handshake_completed);
trace!("{} {:?}", trace_id, self);
Ok(())
}
- pub fn on_loss_detection_timeout(&mut self, now: Instant, trace_id: &str) {
- let (loss_time, epoch) = self.earliest_loss_time();
+ pub fn on_loss_detection_timeout(
+ &mut self, handshake_completed: bool, now: Instant, trace_id: &str,
+ ) {
+ let (earliest_loss_time, epoch) =
+ self.earliest_loss_time(self.loss_time, handshake_completed);
- if loss_time.is_some() {
+ if earliest_loss_time.is_some() {
self.detect_lost_packets(epoch, now, trace_id);
- } else if self.crypto_bytes_in_flight > 0 {
- // Retransmit unacked data from all packet number spaces.
- for e in packet::EPOCH_INITIAL..packet::EPOCH_COUNT {
- for p in self.sent[e].values().filter(|p| p.is_crypto) {
- self.lost[e].extend_from_slice(&p.frames);
- }
- }
+ self.set_loss_detection_timer(handshake_completed);
- trace!("{} resend unacked crypto data ({:?})", trace_id, self);
-
- self.crypto_count += 1;
- } else {
- self.pto_count += 1;
- self.probes = 2;
+ trace!("{} {:?}", trace_id, self);
+ return;
}
- self.set_loss_detection_timer();
+ // TODO: handle client without 1-RTT keys case.
+
+ let (_, epoch) = self.earliest_loss_time(
+ self.time_of_last_sent_ack_eliciting_pkt,
+ handshake_completed,
+ );
+
+ self.loss_probes[epoch] = 2;
+
+ self.pto_count += 1;
+
+ self.set_loss_detection_timer(handshake_completed);
trace!("{} {:?}", trace_id, self);
}
pub fn drop_unacked_data(&mut self, epoch: packet::Epoch) {
let mut unacked_bytes = 0;
- let mut crypto_unacked_bytes = 0;
for p in self.sent[epoch].values_mut().filter(|p| p.in_flight) {
unacked_bytes += p.size;
-
- if p.is_crypto {
- crypto_unacked_bytes += p.size;
- }
}
- self.crypto_bytes_in_flight -= crypto_unacked_bytes;
- self.bytes_in_flight -= unacked_bytes;
+ self.cc.decrease_bytes_in_flight(unacked_bytes);
+
+ self.loss_time[epoch] = None;
+ self.loss_probes[epoch] = 0;
+ self.time_of_last_sent_ack_eliciting_pkt[epoch] = None;
self.sent[epoch].clear();
self.lost[epoch].clear();
@@ -329,17 +308,13 @@
self.loss_detection_timer
}
- pub fn cwnd(&self) -> usize {
+ pub fn cwnd_available(&self) -> usize {
// Ignore cwnd when sending probe packets.
- if self.probes > 0 {
+ if self.loss_probes.iter().any(|&x| x > 0) {
return std::usize::MAX;
}
- if self.bytes_in_flight > self.cwnd {
- return 0;
- }
-
- self.cwnd - self.bytes_in_flight
+ self.cc.cwnd().saturating_sub(self.cc.bytes_in_flight())
}
pub fn rtt(&self) -> Duration {
@@ -385,12 +360,21 @@
}
}
- fn earliest_loss_time(&mut self) -> (Option<Instant>, packet::Epoch) {
+ fn earliest_loss_time(
+ &mut self, times: [Option<Instant>; packet::EPOCH_COUNT],
+ handshake_completed: bool,
+ ) -> (Option<Instant>, packet::Epoch) {
let mut epoch = packet::EPOCH_INITIAL;
- let mut time = self.loss_time[epoch];
+ let mut time = times[epoch];
+ // Iterate over all packet number spaces starting from Handshake.
+ #[allow(clippy::needless_range_loop)]
for e in packet::EPOCH_HANDSHAKE..packet::EPOCH_COUNT {
- let new_time = self.loss_time[e];
+ let new_time = times[e];
+
+ if e == packet::EPOCH_APPLICATION && !handshake_completed {
+ continue;
+ }
if new_time.is_some() && (time.is_none() || new_time < time) {
time = new_time;
@@ -401,55 +385,59 @@
(time, epoch)
}
- fn set_loss_detection_timer(&mut self) {
- let (loss_time, _) = self.earliest_loss_time();
- if loss_time.is_some() {
+ fn set_loss_detection_timer(&mut self, handshake_completed: bool) {
+ let (earliest_loss_time, _) =
+ self.earliest_loss_time(self.loss_time, handshake_completed);
+
+ if earliest_loss_time.is_some() {
// Time threshold loss detection.
- self.loss_detection_timer = loss_time;
+ self.loss_detection_timer = earliest_loss_time;
return;
}
- if self.crypto_bytes_in_flight > 0 {
- // Crypto retransmission timer.
- let mut timeout = self.rtt() * 2;
-
- timeout = cmp::max(timeout, GRANULARITY);
- timeout *= 2_u32.pow(self.crypto_count);
-
- self.loss_detection_timer =
- Some(self.time_of_last_sent_crypto_pkt + timeout);
-
- return;
- }
-
- if self.bytes_in_flight == 0 {
+ if self.cc.bytes_in_flight() == 0 {
+ // TODO: check if peer is awaiting address validation.
self.loss_detection_timer = None;
return;
}
// PTO timer.
- let timeout = self.pto() * 2_u32.pow(self.pto_count);
+ let timeout = match self.smoothed_rtt {
+ None => INITIAL_RTT * 2,
- self.loss_detection_timer =
- Some(self.time_of_last_sent_ack_eliciting_pkt + timeout);
+ Some(_) => self.pto() * 2_u32.pow(self.pto_count),
+ };
+
+ let (sent_time, _) = self.earliest_loss_time(
+ self.time_of_last_sent_ack_eliciting_pkt,
+ handshake_completed,
+ );
+
+ if let Some(sent_time) = sent_time {
+ self.loss_detection_timer = Some(sent_time + timeout);
+ }
}
fn detect_lost_packets(
&mut self, epoch: packet::Epoch, now: Instant, trace_id: &str,
) {
- let mut lost_pkt: Vec<u64> = Vec::new();
-
let largest_acked = self.largest_acked_pkt[epoch];
- let loss_delay =
- cmp::max(self.latest_rtt, self.rtt()).mul_f64(TIME_THRESHOLD);
- let loss_delay = cmp::max(loss_delay, GRANULARITY);
-
- let lost_send_time = now - loss_delay;
+ let mut lost_pkt: Vec<u64> = Vec::new();
self.loss_time[epoch] = None;
+ let loss_delay =
+ cmp::max(self.latest_rtt, self.rtt()).mul_f64(TIME_THRESHOLD);
+
+ // Minimum time of kGranularity before packets are deemed lost.
+ let loss_delay = cmp::max(loss_delay, GRANULARITY);
+
+ // Packets sent before this time are deemed lost.
+ let lost_send_time = now - loss_delay;
+
for (_, unacked) in self.sent[epoch].range(..=largest_acked) {
+ // Mark packet as lost, or set time when it should be marked.
if unacked.time <= lost_send_time ||
largest_acked >= unacked.pkt_num + PACKET_THRESHOLD
{
@@ -478,42 +466,26 @@
}
if !lost_pkt.is_empty() {
- self.on_packets_lost(lost_pkt, epoch, now);
+ self.on_packets_lost(lost_pkt, epoch, now, trace_id);
}
}
- fn in_recovery(&self, sent_time: Instant) -> bool {
- match self.recovery_start_time {
- Some(recovery_start_time) => sent_time <= recovery_start_time,
-
- None => false,
- }
- }
-
- fn on_packet_acked(&mut self, pkt_num: u64, epoch: packet::Epoch) -> bool {
+ fn on_packet_acked(
+ &mut self, pkt_num: u64, epoch: packet::Epoch, trace_id: &str,
+ ) -> bool {
// Check if packet is newly acked.
if let Some(mut p) = self.sent[epoch].remove(&pkt_num) {
self.acked[epoch].append(&mut p.frames);
if p.in_flight {
- // OnPacketAckedCC
- self.bytes_in_flight -= p.size;
-
- if p.is_crypto {
- self.crypto_bytes_in_flight -= p.size;
- }
-
- if self.in_recovery(p.time) {
- return true;
- }
-
- if self.cwnd < self.ssthresh {
- // Slow start.
- self.cwnd += p.size;
- } else {
- // Congestion avoidance.
- self.cwnd += (MAX_DATAGRAM_SIZE * p.size) / self.cwnd;
- }
+ // OnPacketAckedCC(acked_packet)
+ self.cc.on_packet_acked_cc(
+ &p,
+ self.rtt(),
+ self.min_rtt,
+ self.app_limited,
+ trace_id,
+ );
}
return true;
@@ -523,6 +495,7 @@
false
}
+ // TODO: move to Congestion Control and implement draft 24
fn in_persistent_congestion(&mut self, _largest_lost_pkt: &Sent) -> bool {
let _congestion_period = self.pto() * PERSISTENT_CONGESTION_THRESHOLD;
@@ -530,8 +503,10 @@
false
}
+ // TODO: move to Congestion Control
fn on_packets_lost(
&mut self, lost_pkt: Vec<u64>, epoch: packet::Epoch, now: Instant,
+ trace_id: &str,
) {
// Differently from OnPacketsLost(), we need to handle both
// in-flight and non-in-flight packets, so need to keep track
@@ -548,11 +523,7 @@
continue;
}
- self.bytes_in_flight -= p.size;
-
- if p.is_crypto {
- self.crypto_bytes_in_flight -= p.size;
- }
+ self.cc.decrease_bytes_in_flight(p.size);
self.lost[epoch].append(&mut p.frames);
@@ -561,16 +532,11 @@
if let Some(largest_lost_pkt) = largest_lost_pkt {
// CongestionEvent
- if !self.in_recovery(largest_lost_pkt.time) {
- self.recovery_start_time = Some(now);
-
- self.cwnd /= 2;
- self.cwnd = cmp::max(self.cwnd, MINIMUM_WINDOW);
- self.ssthresh = self.cwnd;
- }
+ self.cc
+ .congestion_event(largest_lost_pkt.time, now, trace_id);
if self.in_persistent_congestion(&largest_lost_pkt) {
- self.cwnd = MINIMUM_WINDOW;
+ self.cc.collapse_cwnd();
}
}
}
@@ -595,14 +561,13 @@
},
};
- write!(f, "crypto={} ", self.crypto_bytes_in_flight)?;
- write!(f, "inflight={} ", self.bytes_in_flight)?;
- write!(f, "cwnd={} ", self.cwnd)?;
write!(f, "latest_rtt={:?} ", self.latest_rtt)?;
write!(f, "srtt={:?} ", self.smoothed_rtt)?;
write!(f, "min_rtt={:?} ", self.min_rtt)?;
write!(f, "rttvar={:?} ", self.rttvar)?;
- write!(f, "probes={} ", self.probes)?;
+ write!(f, "loss_time={:?} ", self.loss_time)?;
+ write!(f, "loss_probes={:?} ", self.loss_probes)?;
+ write!(f, "{:?} ", self.cc)?;
Ok(())
}
diff --git a/src/stream.rs b/src/stream.rs
index be32b07..6658a42 100644
--- a/src/stream.rs
+++ b/src/stream.rs
@@ -1,5 +1,4 @@
-// Copyright (C) 2018, Cloudflare, Inc.
-// Copyright (C) 2018, Alessandro Ghedini
+// Copyright (C) 2018-2019, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -46,6 +45,13 @@
/// Map of streams indexed by stream ID.
streams: HashMap<u64, Stream>,
+ /// Set of streams that were completed and garbage collected.
+ ///
+ /// Instead of keeping the full stream state forever, we collect completed
+ /// streams to save memory, but we still need to keep track of previously
+ /// created streams, to prevent peers from re-creating them.
+ collected: HashSet<u64>,
+
/// Peer's maximum bidirectional stream count limit.
peer_max_streams_bidi: u64,
@@ -142,6 +148,11 @@
return Err(Error::InvalidStreamState);
}
+ // Stream has already been closed and garbage collected.
+ if self.collected.contains(&id) {
+ return Err(Error::Done);
+ }
+
let (max_rx_data, max_tx_data) = match (local, is_bidi(id)) {
// Locally-initiated bidirectional stream.
(true, true) => (
@@ -316,6 +327,9 @@
self.local_max_streams_uni_next.saturating_add(1);
}
}
+
+ self.streams.remove(&stream_id);
+ self.collected.insert(stream_id);
}
/// Creates an iterator over streams that have outstanding data to read.
@@ -360,10 +374,10 @@
self.local_max_streams_uni - self.peer_opened_streams_uni
}
- /// Creates an iterator over all streams.
+ /// Returns the number of active streams in the map.
#[cfg(test)]
- pub fn iter_mut(&mut self) -> hash_map::IterMut<u64, Stream> {
- self.streams.iter_mut()
+ pub fn len(&self) -> usize {
+ self.streams.len()
}
}
@@ -381,6 +395,9 @@
/// Whether the stream was created by the local endpoint.
pub local: bool,
+
+ /// Application data.
+ pub data: Option<Box<dyn std::any::Any>>,
}
impl Stream {
@@ -393,6 +410,7 @@
send: SendBuf::new(max_tx_data),
bidi,
local,
+ data: None,
}
}
@@ -419,7 +437,7 @@
///
/// For bidirectional streams this happens when both the receive and send
/// sides are complete. That is when all incoming data has been read by the
- /// application, and when all outgoing data has been ACKed by the peer.
+ /// application, and when all outgoing data has been acked by the peer.
///
/// For unidirectional streams this happens when either the receive or send
/// side is complete, depending on whether the stream was created locally
@@ -552,6 +570,12 @@
return Ok(());
}
+ // No need to process an empty buffer with the fin flag, if we already
+ // know the final size.
+ if buf.fin() && buf.is_empty() && self.fin_off.is_some() {
+ return Ok(());
+ }
+
if buf.fin() {
self.fin_off = Some(buf.max_off());
}
@@ -769,7 +793,7 @@
/// Whether the stream's send-side has been shut down.
shutdown: bool,
- /// Ranges of data offsets that have been ACKed.
+ /// Ranges of data offsets that have been acked.
acked: ranges::RangeSet,
}
@@ -850,17 +874,17 @@
return Ok(());
}
- // Don't queue data that was already fully ACK'd.
+ if buf.fin() {
+ self.fin_off = Some(buf.max_off());
+ }
+
+ // Don't queue data that was already fully acked.
if self.ack_off() >= buf.max_off() {
return Ok(());
}
self.len += buf.len() as u64;
- if buf.fin() {
- self.fin_off = Some(buf.max_off());
- }
-
// We already recorded the final offset, so we can just discard the
// empty buffer now.
if buf.is_empty() {
@@ -877,9 +901,10 @@
let mut out = RangeBuf::default();
out.data =
Vec::with_capacity(cmp::min(max_data as u64, self.len) as usize);
+ out.off = self.off;
let mut out_len = max_data;
- let mut out_off = self.data.peek().map_or_else(|| 0, RangeBuf::off);
+ let mut out_off = self.data.peek().map_or_else(|| out.off, RangeBuf::off);
while out_len > 0 &&
self.ready() &&
@@ -928,7 +953,7 @@
self.max_data = cmp::max(self.max_data, max_data);
}
- /// Increments the ACK'd data offset.
+ /// Increments the acked data offset.
pub fn ack(&mut self, off: u64, len: usize) {
self.acked.insert(off..off + len as u64);
}
@@ -946,6 +971,15 @@
Ok(())
}
+ /// Returns the lowest offset of data buffered.
+ pub fn off(&self) -> u64 {
+ match self.data.peek() {
+ Some(v) => v.off(),
+
+ None => self.off,
+ }
+ }
+
/// Returns true if all data in the stream has been sent.
///
/// This happens when the stream's send final size is knwon, and the
@@ -961,7 +995,7 @@
/// Returns true if the send-side of the stream is complete.
///
/// This happens when the stream's send final size is known, and the peer
- /// has already ACKed all stream data up to that point.
+ /// has already acked all stream data up to that point.
pub fn is_complete(&self) -> bool {
if let Some(fin_off) = self.fin_off {
if self.acked == (0..fin_off) {
@@ -977,16 +1011,7 @@
!self.data.is_empty()
}
- /// Returns the lowest offset of data buffered.
- fn off(&self) -> u64 {
- match self.data.peek() {
- Some(v) => v.off(),
-
- None => self.off,
- }
- }
-
- /// Returns the highest contiguously ACKed offset.
+ /// Returns the highest contiguously acked offset.
fn ack_off(&self) -> u64 {
match self.acked.iter().next() {
// Only consider the initial range if it contiguously covers the
@@ -1778,7 +1803,7 @@
assert_eq!(send.len, 0);
let write = send.pop(10).unwrap();
- assert_eq!(write.off(), 0);
+ assert_eq!(write.off(), 5);
assert_eq!(write.len(), 0);
assert_eq!(write.fin(), false);
assert_eq!(&write[..], b"");
@@ -2002,7 +2027,7 @@
assert_eq!(write.data, b"helloworldsomet");
let write = stream.send.pop(25).unwrap();
- assert_eq!(write.off(), 0);
+ assert_eq!(write.off(), 15);
assert_eq!(write.len(), 0);
assert_eq!(write.fin(), false);
assert_eq!(write.data, b"");
@@ -2215,4 +2240,27 @@
assert!(stream.is_complete());
}
+
+ #[test]
+ fn send_fin_zero_length_output() {
+ let mut stream = Stream::new(0, 15, true, true);
+
+ assert_eq!(stream.send.push_slice(b"hello", false), Ok(5));
+ assert!(!stream.send.is_fin());
+
+ let write = stream.send.pop(5).unwrap();
+ assert_eq!(write.off(), 0);
+ assert_eq!(write.len(), 5);
+ assert_eq!(write.fin(), false);
+ assert_eq!(write.data, b"hello");
+
+ assert_eq!(stream.send.push_slice(b"", true), Ok(0));
+ assert!(stream.send.is_fin());
+
+ let write = stream.send.pop(5).unwrap();
+ assert_eq!(write.off(), 5);
+ assert_eq!(write.len(), 0);
+ assert_eq!(write.fin(), true);
+ assert_eq!(write.data, b"");
+ }
}
diff --git a/src/tls.rs b/src/tls.rs
index 5238b0e..24b2962 100644
--- a/src/tls.rs
+++ b/src/tls.rs
@@ -1,5 +1,4 @@
-// Copyright (C) 2018, Cloudflare, Inc.
-// Copyright (C) 2018, Alessandro Ghedini
+// Copyright (C) 2018-2019, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -80,7 +79,6 @@
#[allow(non_camel_case_types)]
#[repr(transparent)]
-#[cfg(windows)]
struct X509(c_void);
#[repr(C)]
@@ -251,6 +249,14 @@
})
}
+ pub fn set_early_data_enabled(&mut self, enabled: bool) {
+ let enabled = if enabled { 1 } else { 0 };
+
+ unsafe {
+ SSL_CTX_set_early_data_enabled(self.as_ptr(), enabled);
+ }
+ }
+
fn as_ptr(&self) -> *mut SSL_CTX {
self.0
}
@@ -439,10 +445,43 @@
Some(sigalg.to_string())
}
+ pub fn peer_cert(&self) -> Option<Vec<u8>> {
+ let peer_cert = unsafe {
+ let mut out: *mut libc::c_uchar = ptr::null_mut();
+
+ let x509 = SSL_get_peer_certificate(self.as_ptr());
+ if x509.is_null() {
+ return None;
+ }
+
+ let out_len = i2d_X509(x509, &mut out);
+ if out_len <= 0 {
+ return None;
+ }
+
+ let der = slice::from_raw_parts(out, out_len as usize);
+ let der = der.to_vec();
+
+ OPENSSL_free(out as *mut c_void);
+
+ der
+ };
+
+ Some(peer_cert)
+ }
+
+ pub fn is_completed(&self) -> bool {
+ unsafe { SSL_in_init(self.as_ptr()) == 0 }
+ }
+
pub fn is_resumed(&self) -> bool {
unsafe { SSL_session_reused(self.as_ptr()) == 1 }
}
+ pub fn is_in_early_data(&self) -> bool {
+ unsafe { SSL_in_early_data(self.as_ptr()) == 1 }
+ }
+
pub fn clear(&mut self) -> Result<()> {
map_result_ssl(self, unsafe { SSL_clear(self.as_ptr()) })
}
@@ -499,8 +538,8 @@
let space = match level {
crypto::Level::Initial => &mut conn.pkt_num_spaces[packet::EPOCH_INITIAL],
- // TODO: implement 0-RTT
- crypto::Level::ZeroRTT => unimplemented!("0-RTT"),
+ crypto::Level::ZeroRTT =>
+ &mut conn.pkt_num_spaces[packet::EPOCH_APPLICATION],
crypto::Level::Handshake =>
&mut conn.pkt_num_spaces[packet::EPOCH_HANDSHAKE],
crypto::Level::OneRTT =>
@@ -520,50 +559,61 @@
let mut iv = vec![0; nonce_len];
let mut pn_key = vec![0; key_len];
- let secret = unsafe { slice::from_raw_parts(read_secret, secret_len) };
+ // 0-RTT read secrets are present only on the server.
+ if level != crypto::Level::ZeroRTT || conn.is_server {
+ let secret = unsafe { slice::from_raw_parts(read_secret, secret_len) };
- if crypto::derive_pkt_key(aead, &secret, &mut key).is_err() {
- return 0;
+ if crypto::derive_pkt_key(aead, &secret, &mut key).is_err() {
+ return 0;
+ }
+
+ if crypto::derive_pkt_iv(aead, &secret, &mut iv).is_err() {
+ return 0;
+ }
+
+ if crypto::derive_hdr_key(aead, &secret, &mut pn_key).is_err() {
+ return 0;
+ }
+
+ let open = match crypto::Open::new(aead, &key, &iv, &pn_key) {
+ Ok(v) => v,
+
+ Err(_) => return 0,
+ };
+
+ if level == crypto::Level::ZeroRTT {
+ space.crypto_0rtt_open = Some(open);
+ return 1;
+ }
+
+ space.crypto_open = Some(open);
}
- if crypto::derive_pkt_iv(aead, &secret, &mut iv).is_err() {
- return 0;
+ // 0-RTT write secrets are present only on the client.
+ if level != crypto::Level::ZeroRTT || !conn.is_server {
+ let secret = unsafe { slice::from_raw_parts(write_secret, secret_len) };
+
+ if crypto::derive_pkt_key(aead, &secret, &mut key).is_err() {
+ return 0;
+ }
+
+ if crypto::derive_pkt_iv(aead, &secret, &mut iv).is_err() {
+ return 0;
+ }
+
+ if crypto::derive_hdr_key(aead, &secret, &mut pn_key).is_err() {
+ return 0;
+ }
+
+ let seal = match crypto::Seal::new(aead, &key, &iv, &pn_key) {
+ Ok(v) => v,
+
+ Err(_) => return 0,
+ };
+
+ space.crypto_seal = Some(seal);
}
- if crypto::derive_hdr_key(aead, &secret, &mut pn_key).is_err() {
- return 0;
- }
-
- let open = match crypto::Open::new(aead, &key, &iv, &pn_key) {
- Ok(v) => v,
-
- Err(_) => return 0,
- };
-
- space.crypto_open = Some(open);
-
- let secret = unsafe { slice::from_raw_parts(write_secret, secret_len) };
-
- if crypto::derive_pkt_key(aead, &secret, &mut key).is_err() {
- return 0;
- }
-
- if crypto::derive_pkt_iv(aead, &secret, &mut iv).is_err() {
- return 0;
- }
-
- if crypto::derive_hdr_key(aead, &secret, &mut pn_key).is_err() {
- return 0;
- }
-
- let seal = match crypto::Seal::new(aead, &key, &iv, &pn_key) {
- Ok(v) => v,
-
- Err(_) => return 0,
- };
-
- space.crypto_seal = Some(seal);
-
1
}
@@ -668,6 +718,12 @@
while let Ok(proto) = protos.get_bytes_with_u8_length() {
let found = conn.application_protos.iter().any(|expected| {
+ trace!(
+ "checking peer ALPN {:?} against {:?}",
+ std::str::from_utf8(proto.as_ref()),
+ std::str::from_utf8(expected.as_slice())
+ );
+
if expected.len() == proto.len() &&
expected.as_slice() == proto.as_ref()
{
@@ -814,6 +870,8 @@
arg: *mut c_void,
);
+ fn SSL_CTX_set_early_data_enabled(ctx: *mut SSL_CTX, enabled: i32);
+
// SSL
fn SSL_get_ex_new_index(
argl: c_long, argp: *const c_void, unused: *const c_void,
@@ -842,6 +900,8 @@
sigalg: u16, include_curve: i32,
) -> *const c_char;
+ fn SSL_get_peer_certificate(ssl: *mut SSL) -> *const X509;
+
fn SSL_set_min_proto_version(ssl: *mut SSL, version: u16);
fn SSL_set_max_proto_version(ssl: *mut SSL, version: u16);
@@ -875,6 +935,10 @@
fn SSL_session_reused(ssl: *mut SSL) -> c_int;
+ fn SSL_in_init(ssl: *mut SSL) -> c_int;
+
+ fn SSL_in_early_data(ssl: *mut SSL) -> c_int;
+
fn SSL_clear(ssl: *mut SSL) -> c_int;
fn SSL_free(ssl: *mut SSL);
@@ -897,8 +961,13 @@
#[cfg(windows)]
fn d2i_X509(px: *mut X509, input: *const *const u8, len: c_int) -> *mut X509;
+ fn i2d_X509(px: *const X509, out: *mut *mut u8) -> c_int;
+
// ERR
fn ERR_peek_error() -> c_uint;
fn ERR_error_string_n(err: c_uint, buf: *const u8, len: usize);
+
+ // OPENSSL
+ fn OPENSSL_free(ptr: *mut c_void);
}
diff --git a/tools/apps/Cargo.toml b/tools/apps/Cargo.toml
new file mode 100644
index 0000000..6c9aed0
--- /dev/null
+++ b/tools/apps/Cargo.toml
@@ -0,0 +1,25 @@
+[package]
+name = "quiche_apps"
+version = "0.1.0"
+authors = ["Lucas Pardue <lucaspardue.24.7@gmail.com>"]
+edition = "2018"
+publish = false
+
+[features]
+# Enable quiche's fuzzing mode.
+fuzzing = ["quiche/fuzzing"]
+
+[dependencies]
+docopt = "1"
+env_logger = "0.6"
+mio = "0.6"
+url = "1"
+log = "0.4"
+ring = "0.16"
+quiche = { path = "../../" }
+
+[profile.release]
+debug = true
+
+[lib]
+crate-type = ["lib"]
diff --git a/tools/apps/src/bin/cert.crt b/tools/apps/src/bin/cert.crt
new file mode 100644
index 0000000..adfed31
--- /dev/null
+++ b/tools/apps/src/bin/cert.crt
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIUaj26Dyzr2W9R8juKm2pNyrtati0wDQYJKoZIhvcNAQEL
+BQAwWTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
+GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJcXVpYy50ZWNoMB4X
+DTE4MDkzMDIyMTE0OFoXDTE5MDkzMDIyMTE0OFowWTELMAkGA1UEBhMCQVUxEzAR
+BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5
+IEx0ZDESMBAGA1UEAwwJcXVpYy50ZWNoMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAqrS30fnkI6Q+5SKsBXkIwnhO61x/Wgt0zo5P+0yTAZDYVYtEhRlf
+mJ3esEleO1nq5MtM3d+6aVBJlwtTi8pBOzVfJklnxd07N3rKh3HZbGHybjhJFGT9
+U4sUrcKcCpSKJaEu7IQsQQs1Hh0B67MeqJG3F7OcYCF3OXC11WK3CtDDKcLcsa2x
++WImzsPfayzEjQ4ELTVDP73oQGR6D3HaWauKES4JjI9CMn8EJRCcxjwet+c4U3kQ
+g2z5KDbooBfCfrzmX3/EpMf/RaASaUtZF3kgfDT648dICWUoiparo1V73pg2vDe5
+RsAp4n1A7VCY48VvGEz9Qgcp8QFztpFJnwIDAQABo1MwUTAdBgNVHQ4EFgQUFOlS
+IeYH/41CN5BP/8w8F3e/fkYwHwYDVR0jBBgwFoAUFOlSIeYH/41CN5BP/8w8F3e/
+fkYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAZa7XK3My4Jpe
+SLz0BAj44QtghGdg98QFR3iZEnn0XC09HrkhbjaR8Ma3dn0QyMDRuPLLNl5j3VWu
+rDqngENbuJJBPGkCTzozFfMU6MZzGLK1ljIiGzkMXVEaamSj7GDJ2eR2i2cBugiM
+Yv7N/e8FbSMRBXoYVPjukoA8QwDJhS/oN47vt0+VsTi5wah9d3t0RCruAe/4TETo
+jPxjbEGTQ71dmU66xPZMrnqlGCNa4kN2alCDNfSg1yRp4j10zSmK0jHEHOuiHliW
+/Zc+aLEFcVB1QHmIyvcBIhKiuDbfbkWrqSiel6nLScIvhJaJOrGzQYBfjeZ4TO0m
+IHJUojcgZA==
+-----END CERTIFICATE-----
diff --git a/tools/apps/src/bin/cert.key b/tools/apps/src/bin/cert.key
new file mode 100644
index 0000000..0a2c39b
--- /dev/null
+++ b/tools/apps/src/bin/cert.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCqtLfR+eQjpD7l
+IqwFeQjCeE7rXH9aC3TOjk/7TJMBkNhVi0SFGV+Ynd6wSV47Werky0zd37ppUEmX
+C1OLykE7NV8mSWfF3Ts3esqHcdlsYfJuOEkUZP1TixStwpwKlIoloS7shCxBCzUe
+HQHrsx6okbcXs5xgIXc5cLXVYrcK0MMpwtyxrbH5YibOw99rLMSNDgQtNUM/vehA
+ZHoPcdpZq4oRLgmMj0IyfwQlEJzGPB635zhTeRCDbPkoNuigF8J+vOZff8Skx/9F
+oBJpS1kXeSB8NPrjx0gJZSiKlqujVXvemDa8N7lGwCnifUDtUJjjxW8YTP1CBynx
+AXO2kUmfAgMBAAECggEAdWR0KT1NS8luy0qtu9HBWWM8+pSQq87HFClADZRaYDBI
+5YMxqsqJOD4Q33CFEhHC/HZmtQpfen8RLINIgBCmDV6lwYGnkKWUTJHv53c+y08M
+Vgn1D8Zng+VYYio7/vapjjkrONGoUU6wx7WxFXMHuWsD25PUDTPWdrTxBv6s3A0X
+Le7UtuCdo/xNY4YS6S64SfiEPsBddj1NhoiwOHkXekpNRoAwnizjngubEkiznScu
+gwKCW4nPV8y4CoIYyncGayrKieg03llgRngFiGJKpKeyL2UkX07Fqb2tXuJ36+RA
+9DrluEkYWZCjOS+aaQu+NwxCkUV5pq+HcXQmF5VX+QKBgQDTrgF4sKwcIjm+k3Fp
+bqhMS5stuSQJVn85fCIeQLq3u5DRq9n+UOvq6GvdEXz0SiupLfkXx/pDwiOux2sn
+CcwMaPqWbFE4mSsCFCBkL/PvXSzH2zYesHOplztvcV+gexAjmoCikMBCcM00QpN1
+GScUmQGTk/7BKJYGnVchJOXbfQKBgQDOcoZryCDxUPsg2ZMwkrnpcM+fSTT1gcgf
+I3gbGohagiXVTDU4+S7I7WmsJv+lBUJCWRG0p8JJZb0NsgnrGyOfCKL59xAV5PyT
+xSXMIi2+OH+fQXblII76GqWCs7A7NxtEU2geSy4ePPzSS4G81FN2oeV1OxZ9a6fk
+6cFIzmqsSwKBgQDIBQlg6NiI8RJNcXdeH/EpvtuQNfzGUhR/1jtLCPEmgjcS2Odx
+Nzflzd92knrXP2rIPye7//wMoNsk4UzwI4LLSztWfl21NI5+NVRyNxmyWgHhi9M0
+5pk0bDH+WUv6Ea8rZWgdtNfnMD3HHw3FPZI/FWF2+QZlsRsqfuyA5iPI5QKBgQCu
+D7F2Po5H6FdUIx4O3icRw6PKURbtyDbKykUB1SUR6pmrdU2Kc84WatWl6Fuy7vQm
+rKJZBviwma8EVRA3wfIOrGF9D+noC+FJVffAXTDkKQ6xX6i3FvR1uvHBeW8k/hln
+SkuG/ywrIpCnXjJM21hjtayZYvBbXuF4B/6HPEKEcQKBgQC+DVoOVjsoyd9udTcp
+1v2xvwRVvU/OrPOLXwac1IbTgmb5FJYd8EZI0hdxJhialoTK3OONk04uxdn5tlAB
+QwKBmkXZEr9EIreMp18gbzmDGalx8UcS0j+nIZvmpZXWsIimAKDGEwFc8w+NAN5a
+X5UkSGjM6dnJocH0sLI7hXuVJw==
+-----END PRIVATE KEY-----
diff --git a/tools/apps/src/bin/quiche-client.rs b/tools/apps/src/bin/quiche-client.rs
new file mode 100644
index 0000000..f6ad7fa
--- /dev/null
+++ b/tools/apps/src/bin/quiche-client.rs
@@ -0,0 +1,380 @@
+// Copyright (C) 2020, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#[macro_use]
+extern crate log;
+
+use std::net::ToSocketAddrs;
+
+use std::io::prelude::*;
+
+use ring::rand::*;
+
+use quiche_apps::*;
+
+const MAX_DATAGRAM_SIZE: usize = 1350;
+
+const USAGE: &str = "Usage:
+ quiche-client [options] URL...
+ quiche-client -h | --help
+
+Options:
+ --method METHOD Use the given HTTP request method [default: GET].
+ --body FILE Send the given file as request body.
+ --max-data BYTES Connection-wide flow control limit [default: 10000000].
+ --max-stream-data BYTES Per-stream flow control limit [default: 1000000].
+ --max-streams-bidi STREAMS Number of allowed concurrent streams [default: 100].
+ --max-streams-uni STREAMS Number of allowed concurrent streams [default: 100].
+ --wire-version VERSION The version number to send to the server [default: babababa].
+ --http-version VERSION HTTP version to use [default: all].
+ --dump-packets PATH Dump the incoming packets as files in the given directory.
+ --dump-responses PATH Dump response payload as files in the given directory.
+ --no-verify Don't verify server's certificate.
+ --no-grease Don't send GREASE.
+ -H --header HEADER ... Add a request header.
+ -n --requests REQUESTS Send the given number of identical requests [default: 1].
+ -h --help Show this screen.
+";
+
+fn main() {
+ let mut buf = [0; 65535];
+ let mut out = [0; MAX_DATAGRAM_SIZE];
+
+ env_logger::builder()
+ .default_format_timestamp_nanos(true)
+ .init();
+
+ // Parse CLI parameters.
+ let docopt = docopt::Docopt::new(USAGE).unwrap();
+ let conn_args = CommonArgs::with_docopt(&docopt);
+ let args = ClientArgs::with_docopt(&docopt);
+
+ // Setup the event loop.
+ let poll = mio::Poll::new().unwrap();
+ let mut events = mio::Events::with_capacity(1024);
+
+ // We'll only connect to the first server provided in URL list.
+ let connect_url = &args.urls[0];
+
+ // Resolve server address.
+ let peer_addr = connect_url.to_socket_addrs().unwrap().next().unwrap();
+
+ // Bind to INADDR_ANY or IN6ADDR_ANY depending on the IP family of the
+ // server address. This is needed on macOS and BSD variants that don't
+ // support binding to IN6ADDR_ANY for both v4 and v6.
+ let bind_addr = match peer_addr {
+ std::net::SocketAddr::V4(_) => "0.0.0.0:0",
+ std::net::SocketAddr::V6(_) => "[::]:0",
+ };
+
+ // Create the UDP socket backing the QUIC connection, and register it with
+ // the event loop.
+ let socket = std::net::UdpSocket::bind(bind_addr).unwrap();
+ socket.connect(peer_addr).unwrap();
+
+ let socket = mio::net::UdpSocket::from_socket(socket).unwrap();
+ poll.register(
+ &socket,
+ mio::Token(0),
+ mio::Ready::readable(),
+ mio::PollOpt::edge(),
+ )
+ .unwrap();
+
+ // Create the configuration for the QUIC connection.
+ let mut config = quiche::Config::new(args.version).unwrap();
+
+ config.verify_peer(!args.no_verify);
+
+ config.set_application_protos(&conn_args.alpns).unwrap();
+
+ config.set_max_idle_timeout(5000);
+ config.set_max_packet_size(MAX_DATAGRAM_SIZE as u64);
+ config.set_initial_max_data(conn_args.max_data);
+ config.set_initial_max_stream_data_bidi_local(conn_args.max_stream_data);
+ config.set_initial_max_stream_data_bidi_remote(conn_args.max_stream_data);
+ config.set_initial_max_stream_data_uni(conn_args.max_stream_data);
+ config.set_initial_max_streams_bidi(conn_args.max_streams_bidi);
+ config.set_initial_max_streams_uni(conn_args.max_streams_uni);
+ config.set_disable_active_migration(true);
+
+ if conn_args.no_grease {
+ config.grease(false);
+ }
+
+ if std::env::var_os("SSLKEYLOGFILE").is_some() {
+ config.log_keys();
+ }
+
+ let mut http_conn: Option<Box<dyn HttpConn>> = None;
+
+ // Generate a random source connection ID for the connection.
+ let mut scid = [0; quiche::MAX_CONN_ID_LEN];
+ SystemRandom::new().fill(&mut scid[..]).unwrap();
+
+ // Create a QUIC connection and initiate handshake.
+ let mut conn =
+ quiche::connect(connect_url.domain(), &scid, &mut config).unwrap();
+
+ info!(
+ "connecting to {:} from {:} with scid {}",
+ peer_addr,
+ socket.local_addr().unwrap(),
+ hex_dump(&scid)
+ );
+
+ let write = conn.send(&mut out).expect("initial send failed");
+
+ while let Err(e) = socket.send(&out[..write]) {
+ if e.kind() == std::io::ErrorKind::WouldBlock {
+ debug!("send() would block");
+ continue;
+ }
+
+ panic!("send() failed: {:?}", e);
+ }
+
+ debug!("written {}", write);
+
+ let req_start = std::time::Instant::now();
+
+ let mut pkt_count = 0;
+
+ loop {
+ poll.poll(&mut events, conn.timeout()).unwrap();
+
+ // Read incoming UDP packets from the socket and feed them to quiche,
+ // until there are no more packets to read.
+ 'read: loop {
+ // If the event loop reported no events, it means that the timeout
+ // has expired, so handle it without attempting to read packets. We
+ // will then proceed with the send loop.
+ if events.is_empty() {
+ debug!("timed out");
+
+ conn.on_timeout();
+
+ break 'read;
+ }
+
+ let len = match socket.recv(&mut buf) {
+ Ok(v) => v,
+
+ Err(e) => {
+ // There are no more UDP packets to read, so end the read
+ // loop.
+ if e.kind() == std::io::ErrorKind::WouldBlock {
+ debug!("recv() would block");
+ break 'read;
+ }
+
+ panic!("recv() failed: {:?}", e);
+ },
+ };
+
+ debug!("got {} bytes", len);
+
+ if let Some(target_path) = conn_args.dump_packet_path.as_ref() {
+ let path = format!("{}/{}.pkt", target_path, pkt_count);
+
+ if let Ok(f) = std::fs::File::create(&path) {
+ let mut f = std::io::BufWriter::new(f);
+ f.write_all(&buf[..len]).ok();
+ }
+ }
+
+ pkt_count += 1;
+
+ // Process potentially coalesced packets.
+ let read = match conn.recv(&mut buf[..len]) {
+ Ok(v) => v,
+
+ Err(quiche::Error::Done) => {
+ debug!("done reading");
+ break;
+ },
+
+ Err(e) => {
+ error!("recv failed: {:?}", e);
+ break 'read;
+ },
+ };
+
+ debug!("processed {} bytes", read);
+ }
+
+ if conn.is_closed() {
+ info!("connection closed, {:?}", conn.stats());
+
+ if let Some(h_conn) = http_conn {
+ h_conn.report_incomplete(&req_start);
+ }
+
+ break;
+ }
+
+ // Create a new HTTP connection once the QUIC connection is established.
+ if conn.is_established() && http_conn.is_none() {
+ // At this stage the ALPN negotiation succeeded and selected a
+ // single application protocol name. We'll use this to construct
+ // the correct type of HttpConn but `application_proto()`
+ // returns a slice, so we have to convert it to a str in order
+ // to compare to our lists of protocols. We `unwrap()` because
+ // we need the value and if something fails at this stage, there
+ // is not much anyone can do to recover.
+
+ let app_proto = conn.application_proto();
+ let app_proto = &std::str::from_utf8(&app_proto).unwrap();
+
+ if alpns::HTTP_09.contains(app_proto) {
+ http_conn =
+ Some(Http09Conn::with_urls(&args.urls, args.reqs_cardinal));
+ } else if alpns::HTTP_3.contains(app_proto) {
+ http_conn = Some(Http3Conn::with_urls(
+ &mut conn,
+ &args.urls,
+ args.reqs_cardinal,
+ &args.req_headers,
+ &args.body,
+ &args.method,
+ ));
+ }
+ }
+
+ // If we have an HTTP connection, first issue the requests then
+ // process received data.
+ if let Some(h_conn) = http_conn.as_mut() {
+ h_conn.send_requests(&mut conn, &args.dump_response_path);
+ h_conn.handle_responses(&mut conn, &mut buf, &req_start);
+ }
+
+ // Generate outgoing QUIC packets and send them on the UDP socket, until
+ // quiche reports that there are no more packets to be sent.
+ loop {
+ let write = match conn.send(&mut out) {
+ Ok(v) => v,
+
+ Err(quiche::Error::Done) => {
+ debug!("done writing");
+ break;
+ },
+
+ Err(e) => {
+ error!("send failed: {:?}", e);
+
+ conn.close(false, 0x1, b"fail").ok();
+ break;
+ },
+ };
+
+ if let Err(e) = socket.send(&out[..write]) {
+ if e.kind() == std::io::ErrorKind::WouldBlock {
+ debug!("send() would block");
+ break;
+ }
+
+ panic!("send() failed: {:?}", e);
+ }
+
+ debug!("written {}", write);
+ }
+
+ if conn.is_closed() {
+ info!("connection closed, {:?}", conn.stats());
+
+ if let Some(h_conn) = http_conn {
+ h_conn.report_incomplete(&req_start);
+ }
+
+ break;
+ }
+ }
+}
+
+/// Application-specific arguments that compliment the `CommonArgs`.
+struct ClientArgs {
+ version: u32,
+ dump_response_path: Option<String>,
+ urls: Vec<url::Url>,
+ reqs_cardinal: u64,
+ req_headers: Vec<String>,
+ no_verify: bool,
+ body: Option<Vec<u8>>,
+ method: String,
+}
+
+impl Args for ClientArgs {
+ fn with_docopt(docopt: &docopt::Docopt) -> Self {
+ let args = docopt.parse().unwrap_or_else(|e| e.exit());
+
+ let version = args.get_str("--wire-version");
+ let version = u32::from_str_radix(version, 16).unwrap();
+
+ let dump_response_path = if args.get_str("--dump-responses") != "" {
+ Some(args.get_str("--dump-responses").to_string())
+ } else {
+ None
+ };
+
+ // URLs (can be multiple).
+ let urls: Vec<url::Url> = args
+ .get_vec("URL")
+ .into_iter()
+ .map(|x| url::Url::parse(x).unwrap())
+ .collect();
+
+ // Request headers (can be multiple).
+ let req_headers = args
+ .get_vec("--header")
+ .into_iter()
+ .map(|x| x.to_string())
+ .collect();
+
+ let reqs_cardinal = args.get_str("--requests");
+ let reqs_cardinal = u64::from_str_radix(reqs_cardinal, 10).unwrap();
+
+ let no_verify = args.get_bool("--no-verify");
+
+ let body = if args.get_bool("--body") {
+ std::fs::read(args.get_str("--body")).ok()
+ } else {
+ None
+ };
+
+ let method = args.get_str("--method").to_string();
+
+ ClientArgs {
+ version,
+ dump_response_path,
+ urls,
+ req_headers,
+ reqs_cardinal,
+ no_verify,
+ body,
+ method,
+ }
+ }
+}
diff --git a/tools/apps/src/bin/quiche-server.rs b/tools/apps/src/bin/quiche-server.rs
new file mode 100644
index 0000000..8380b7a
--- /dev/null
+++ b/tools/apps/src/bin/quiche-server.rs
@@ -0,0 +1,508 @@
+// Copyright (C) 2020, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#[macro_use]
+extern crate log;
+
+use std::net;
+
+use std::io::prelude::*;
+
+use std::collections::HashMap;
+
+use ring::rand::*;
+
+use quiche_apps::*;
+
+const MAX_DATAGRAM_SIZE: usize = 1350;
+
+const USAGE: &str = "Usage:
+ quiche-server [options]
+ quiche-server -h | --help
+
+Options:
+ --listen <addr> Listen on the given IP:port [default: 127.0.0.1:4433]
+ --cert <file> TLS certificate path [default: src/bin/cert.crt]
+ --key <file> TLS certificate key path [default: src/bin/cert.key]
+ --root <dir> Root directory [default: src/bin/root/]
+ --name <str> Name of the server [default: quic.tech]
+ --max-data BYTES Connection-wide flow control limit [default: 10000000].
+ --max-stream-data BYTES Per-stream flow control limit [default: 1000000].
+ --max-streams-bidi STREAMS Number of allowed concurrent streams [default: 100].
+ --max-streams-uni STREAMS Number of allowed concurrent streams [default: 100].
+ --dump-packets PATH Dump the incoming packets as files in the given directory.
+ --early-data Enables receiving early data.
+ --no-retry Disable stateless retry.
+ --no-grease Don't send GREASE.
+ --http-version VERSION HTTP version to use [default: all].
+ -h --help Show this screen.
+";
+
+fn main() {
+ let mut buf = [0; 65535];
+ let mut out = [0; MAX_DATAGRAM_SIZE];
+
+ env_logger::builder()
+ .default_format_timestamp_nanos(true)
+ .init();
+
+ // Parse CLI parameters.
+ let docopt = docopt::Docopt::new(USAGE).unwrap();
+ let conn_args = CommonArgs::with_docopt(&docopt);
+ let args = ServerArgs::with_docopt(&docopt);
+
+ // Setup the event loop.
+ let poll = mio::Poll::new().unwrap();
+ let mut events = mio::Events::with_capacity(1024);
+
+ // Create the UDP listening socket, and register it with the event loop.
+ let socket = net::UdpSocket::bind(args.listen).unwrap();
+
+ let socket = mio::net::UdpSocket::from_socket(socket).unwrap();
+ poll.register(
+ &socket,
+ mio::Token(0),
+ mio::Ready::readable(),
+ mio::PollOpt::edge(),
+ )
+ .unwrap();
+
+ // Create the configuration for the QUIC connections.
+ let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
+
+ config.load_cert_chain_from_pem_file(&args.cert).unwrap();
+ config.load_priv_key_from_pem_file(&args.key).unwrap();
+
+ config.set_application_protos(&conn_args.alpns).unwrap();
+
+ config.set_max_idle_timeout(5000);
+ config.set_max_packet_size(MAX_DATAGRAM_SIZE as u64);
+ config.set_initial_max_data(conn_args.max_data);
+ config.set_initial_max_stream_data_bidi_local(conn_args.max_stream_data);
+ config.set_initial_max_stream_data_bidi_remote(conn_args.max_stream_data);
+ config.set_initial_max_stream_data_uni(conn_args.max_stream_data);
+ config.set_initial_max_streams_bidi(conn_args.max_streams_bidi);
+ config.set_initial_max_streams_uni(conn_args.max_streams_uni);
+ config.set_disable_active_migration(true);
+
+ if args.early_data {
+ config.enable_early_data();
+ }
+
+ if conn_args.no_grease {
+ config.grease(false);
+ }
+
+ if std::env::var_os("SSLKEYLOGFILE").is_some() {
+ config.log_keys();
+ }
+
+ let rng = SystemRandom::new();
+ let conn_id_seed =
+ ring::hmac::Key::generate(ring::hmac::HMAC_SHA256, &rng).unwrap();
+
+ let mut clients = ClientMap::new();
+
+ let mut pkt_count = 0;
+
+ loop {
+ // Find the shorter timeout from all the active connections.
+ //
+ // TODO: use event loop that properly supports timers
+ let timeout =
+ clients.values().filter_map(|(_, c)| c.conn.timeout()).min();
+
+ poll.poll(&mut events, timeout).unwrap();
+
+ // Read incoming UDP packets from the socket and feed them to quiche,
+ // until there are no more packets to read.
+ 'read: loop {
+ // If the event loop reported no events, it means that the timeout
+ // has expired, so handle it without attempting to read packets. We
+ // will then proceed with the send loop.
+ if events.is_empty() {
+ debug!("timed out");
+
+ clients.values_mut().for_each(|(_, c)| c.conn.on_timeout());
+
+ break 'read;
+ }
+
+ let (len, src) = match socket.recv_from(&mut buf) {
+ Ok(v) => v,
+
+ Err(e) => {
+ // There are no more UDP packets to read, so end the read
+ // loop.
+ if e.kind() == std::io::ErrorKind::WouldBlock {
+ debug!("recv() would block");
+ break 'read;
+ }
+
+ panic!("recv() failed: {:?}", e);
+ },
+ };
+
+ debug!("got {} bytes", len);
+
+ let pkt_buf = &mut buf[..len];
+
+ if let Some(target_path) = conn_args.dump_packet_path.as_ref() {
+ let path = format!("{}/{}.pkt", target_path, pkt_count);
+
+ if let Ok(f) = std::fs::File::create(&path) {
+ let mut f = std::io::BufWriter::new(f);
+ f.write_all(pkt_buf).ok();
+ }
+ }
+
+ pkt_count += 1;
+
+ // Parse the QUIC packet's header.
+ let hdr = match quiche::Header::from_slice(
+ pkt_buf,
+ quiche::MAX_CONN_ID_LEN,
+ ) {
+ Ok(v) => v,
+
+ Err(e) => {
+ error!("Parsing packet header failed: {:?}", e);
+ continue;
+ },
+ };
+
+ trace!("got packet {:?}", hdr);
+
+ let conn_id = ring::hmac::sign(&conn_id_seed, &hdr.dcid);
+ let conn_id = &conn_id.as_ref()[..quiche::MAX_CONN_ID_LEN];
+
+ // Lookup a connection based on the packet's connection ID. If there
+ // is no connection matching, create a new one.
+ let (_, client) = if !clients.contains_key(&hdr.dcid) &&
+ !clients.contains_key(conn_id)
+ {
+ if hdr.ty != quiche::Type::Initial {
+ error!("Packet is not Initial");
+ continue;
+ }
+
+ if !quiche::version_is_supported(hdr.version) {
+ warn!("Doing version negotiation");
+
+ let len =
+ quiche::negotiate_version(&hdr.scid, &hdr.dcid, &mut out)
+ .unwrap();
+
+ let out = &out[..len];
+
+ if let Err(e) = socket.send_to(out, &src) {
+ if e.kind() == std::io::ErrorKind::WouldBlock {
+ debug!("send() would block");
+ break;
+ }
+
+ panic!("send() failed: {:?}", e);
+ }
+ continue;
+ }
+
+ let mut scid = [0; quiche::MAX_CONN_ID_LEN];
+ scid.copy_from_slice(&conn_id);
+
+ let mut odcid = None;
+
+ if !args.no_retry {
+ // Token is always present in Initial packets.
+ let token = hdr.token.as_ref().unwrap();
+
+ // Do stateless retry if the client didn't send a token.
+ if token.is_empty() {
+ warn!("Doing stateless retry");
+
+ let new_token = mint_token(&hdr, &src);
+
+ let len = quiche::retry(
+ &hdr.scid, &hdr.dcid, &scid, &new_token, &mut out,
+ )
+ .unwrap();
+ let out = &out[..len];
+
+ if let Err(e) = socket.send_to(out, &src) {
+ if e.kind() == std::io::ErrorKind::WouldBlock {
+ debug!("send() would block");
+ break;
+ }
+
+ panic!("send() failed: {:?}", e);
+ }
+ continue;
+ }
+
+ odcid = validate_token(&src, token);
+
+ // The token was not valid, meaning the retry failed, so
+ // drop the packet.
+ if odcid == None {
+ error!("Invalid address validation token");
+ continue;
+ }
+
+ if scid.len() != hdr.dcid.len() {
+ error!("Invalid destination connection ID");
+ continue;
+ }
+
+ // Reuse the source connection ID we sent in the Retry
+ // packet, instead of changing it again.
+ scid.copy_from_slice(&hdr.dcid);
+ }
+
+ debug!(
+ "New connection: dcid={} scid={}",
+ hex_dump(&hdr.dcid),
+ hex_dump(&scid)
+ );
+
+ let conn = quiche::accept(&scid, odcid, &mut config).unwrap();
+
+ let client = Client {
+ conn,
+ http_conn: None,
+ partial_responses: HashMap::new(),
+ };
+
+ clients.insert(scid.to_vec(), (src, client));
+
+ clients.get_mut(&scid[..]).unwrap()
+ } else {
+ match clients.get_mut(&hdr.dcid) {
+ Some(v) => v,
+
+ None => clients.get_mut(conn_id).unwrap(),
+ }
+ };
+
+ // Process potentially coalesced packets.
+ let read = match client.conn.recv(pkt_buf) {
+ Ok(v) => v,
+
+ Err(quiche::Error::Done) => {
+ debug!("{} done reading", client.conn.trace_id());
+ break;
+ },
+
+ Err(e) => {
+ error!("{} recv failed: {:?}", client.conn.trace_id(), e);
+ break 'read;
+ },
+ };
+
+ debug!("{} processed {} bytes", client.conn.trace_id(), read);
+
+ // Create a new HTTP connection as soon as the QUIC connection
+ // is established.
+ if client.http_conn.is_none() &&
+ (client.conn.is_in_early_data() ||
+ client.conn.is_established())
+ {
+ // At this stage the ALPN negotiation succeeded and selected a
+ // single application protocol name. We'll use this to construct
+ // the correct type of HttpConn but `application_proto()`
+ // returns a slice, so we have to convert it to a str in order
+ // to compare to our lists of protocols. We `unwrap()` because
+ // we need the value and if something fails at this stage, there
+ // is not much anyone can do to recover.
+ let app_proto = client.conn.application_proto();
+ let app_proto = &std::str::from_utf8(&app_proto).unwrap();
+
+ if alpns::HTTP_09.contains(app_proto) {
+ client.http_conn = Some(Box::new(Http09Conn::default()));
+ } else if alpns::HTTP_3.contains(app_proto) {
+ client.http_conn =
+ Some(Http3Conn::with_conn(&mut client.conn));
+ }
+ }
+
+ if client.http_conn.is_some() {
+ let conn = &mut client.conn;
+ let http_conn = client.http_conn.as_mut().unwrap();
+ let partials = &mut client.partial_responses;
+
+ // Handle writable streams.
+ for stream_id in conn.writable() {
+ http_conn.handle_writable(conn, partials, stream_id);
+ }
+
+ if http_conn
+ .handle_requests(conn, partials, &args.root, &mut buf)
+ .is_err()
+ {
+ break 'read;
+ }
+ }
+ }
+
+ // Generate outgoing QUIC packets for all active connections and send
+ // them on the UDP socket, until quiche reports that there are no more
+ // packets to be sent.
+ for (peer, client) in clients.values_mut() {
+ loop {
+ let write = match client.conn.send(&mut out) {
+ Ok(v) => v,
+
+ Err(quiche::Error::Done) => {
+ debug!("{} done writing", client.conn.trace_id());
+ break;
+ },
+
+ Err(e) => {
+ error!("{} send failed: {:?}", client.conn.trace_id(), e);
+
+ client.conn.close(false, 0x1, b"fail").ok();
+ break;
+ },
+ };
+
+ // TODO: coalesce packets.
+ if let Err(e) = socket.send_to(&out[..write], &peer) {
+ if e.kind() == std::io::ErrorKind::WouldBlock {
+ debug!("send() would block");
+ break;
+ }
+
+ panic!("send() failed: {:?}", e);
+ }
+
+ debug!("{} written {} bytes", client.conn.trace_id(), write);
+ }
+ }
+
+ // Garbage collect closed connections.
+ clients.retain(|_, (_, ref mut c)| {
+ debug!("Collecting garbage");
+
+ if c.conn.is_closed() {
+ info!(
+ "{} connection collected {:?}",
+ c.conn.trace_id(),
+ c.conn.stats()
+ );
+ }
+
+ !c.conn.is_closed()
+ });
+ }
+}
+
+/// Generate a stateless retry token.
+///
+/// The token includes the static string `"quiche"` followed by the IP address
+/// of the client and by the original destination connection ID generated by the
+/// client.
+///
+/// Note that this function is only an example and doesn't do any cryptographic
+/// authenticate of the token. *It should not be used in production system*.
+fn mint_token(hdr: &quiche::Header, src: &net::SocketAddr) -> Vec<u8> {
+ let mut token = Vec::new();
+
+ token.extend_from_slice(b"quiche");
+
+ let addr = match src.ip() {
+ std::net::IpAddr::V4(a) => a.octets().to_vec(),
+ std::net::IpAddr::V6(a) => a.octets().to_vec(),
+ };
+
+ token.extend_from_slice(&addr);
+ token.extend_from_slice(&hdr.dcid);
+
+ token
+}
+
+/// Validates a stateless retry token.
+///
+/// This checks that the ticket includes the `"quiche"` static string, and that
+/// the client IP address matches the address stored in the ticket.
+///
+/// Note that this function is only an example and doesn't do any cryptographic
+/// authenticate of the token. *It should not be used in production system*.
+fn validate_token<'a>(
+ src: &net::SocketAddr, token: &'a [u8],
+) -> Option<&'a [u8]> {
+ if token.len() < 6 {
+ return None;
+ }
+
+ if &token[..6] != b"quiche" {
+ return None;
+ }
+
+ let token = &token[6..];
+
+ let addr = match src.ip() {
+ std::net::IpAddr::V4(a) => a.octets().to_vec(),
+ std::net::IpAddr::V6(a) => a.octets().to_vec(),
+ };
+
+ if token.len() < addr.len() || &token[..addr.len()] != addr.as_slice() {
+ return None;
+ }
+
+ let token = &token[addr.len()..];
+
+ Some(&token[..])
+}
+
+// Application-specific arguments that compliment the `CommonArgs`.
+struct ServerArgs {
+ listen: String,
+ no_retry: bool,
+ root: String,
+ cert: String,
+ key: String,
+ early_data: bool,
+}
+
+impl Args for ServerArgs {
+ fn with_docopt(docopt: &docopt::Docopt) -> Self {
+ let args = docopt.parse().unwrap_or_else(|e| e.exit());
+
+ let listen = args.get_str("--listen").to_string();
+ let no_retry = args.get_bool("--no-retry");
+ let early_data = args.get_bool("--early-data");
+ let root = args.get_str("--root").to_string();
+ let cert = args.get_str("--cert").to_string();
+ let key = args.get_str("--key").to_string();
+
+ ServerArgs {
+ listen,
+ no_retry,
+ root,
+ cert,
+ key,
+ early_data,
+ }
+ }
+}
diff --git a/tools/apps/src/lib.rs b/tools/apps/src/lib.rs
new file mode 100644
index 0000000..baa1e62
--- /dev/null
+++ b/tools/apps/src/lib.rs
@@ -0,0 +1,892 @@
+// Copyright (C) 2020, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//! Quiche application utilities.
+//!
+//! This module provides some utility functions that are common to quiche
+//! applications.
+
+#[macro_use]
+extern crate log;
+
+use std::io::prelude::*;
+
+use std::collections::HashMap;
+
+use std::net;
+
+/// Returns a String containing a pretty printed version of the `buf` slice.
+pub fn hex_dump(buf: &[u8]) -> String {
+ let vec: Vec<String> = buf.iter().map(|b| format!("{:02x}", b)).collect();
+
+ vec.join("")
+}
+
+/// ALPN helpers.
+///
+/// This module contains constants and functions for working with ALPN.
+pub mod alpns {
+ pub const HTTP_09: [&str; 4] = ["hq-25", "hq-24", "hq-23", "http/0.9"];
+ pub const HTTP_3: [&str; 3] = ["h3-25", "h3-24", "h3-23"];
+
+ pub fn length_prefixed(alpns: &[&str]) -> Vec<u8> {
+ let mut out = Vec::new();
+
+ for s in alpns {
+ out.push(s.len() as u8);
+ out.extend_from_slice(s.as_bytes());
+ }
+
+ out
+ }
+}
+
+pub trait Args {
+ fn with_docopt(docopt: &docopt::Docopt) -> Self;
+}
+
+/// Contains commons arguments for creating a quiche QUIC connection.
+pub struct CommonArgs {
+ pub alpns: Vec<u8>,
+ pub max_data: u64,
+ pub max_stream_data: u64,
+ pub max_streams_bidi: u64,
+ pub max_streams_uni: u64,
+ pub dump_packet_path: Option<String>,
+ pub no_grease: bool,
+}
+
+/// Creates a new `CommonArgs` structure using the provided [`Docopt`].
+///
+/// The `Docopt` usage String needs to include the following:
+///
+/// --http-version VERSION HTTP version to use
+/// --max-data BYTES Connection-wide flow control limit.
+/// --max-stream-data BYTES Per-stream flow control limit.
+/// --max-streams-bidi STREAMS Number of allowed concurrent streams.
+/// --max-streams-uni STREAMS Number of allowed concurrent streams.
+/// --dump-packets PATH Dump the incoming packets as files in the
+/// given directory. --no-grease Don't send GREASE.
+///
+/// [`Docopt`]: https://docs.rs/docopt/1.1.0/docopt/
+impl Args for CommonArgs {
+ fn with_docopt(docopt: &docopt::Docopt) -> Self {
+ let args = docopt.parse().unwrap_or_else(|e| e.exit());
+
+ let http_version = args.get_str("--http-version");
+ let alpns = match http_version {
+ "HTTP/0.9" => alpns::length_prefixed(&alpns::HTTP_09),
+
+ "HTTP/3" => alpns::length_prefixed(&alpns::HTTP_3),
+
+ "all" => [
+ alpns::length_prefixed(&alpns::HTTP_3),
+ alpns::length_prefixed(&alpns::HTTP_09),
+ ]
+ .concat(),
+
+ _ => panic!("Unsupported HTTP version"),
+ };
+
+ let max_data = args.get_str("--max-data");
+ let max_data = u64::from_str_radix(max_data, 10).unwrap();
+
+ let max_stream_data = args.get_str("--max-stream-data");
+ let max_stream_data = u64::from_str_radix(max_stream_data, 10).unwrap();
+
+ let max_streams_bidi = args.get_str("--max-streams-bidi");
+ let max_streams_bidi = u64::from_str_radix(max_streams_bidi, 10).unwrap();
+
+ let max_streams_uni = args.get_str("--max-streams-uni");
+ let max_streams_uni = u64::from_str_radix(max_streams_uni, 10).unwrap();
+
+ let dump_packet_path = if args.get_str("--dump-packets") != "" {
+ Some(args.get_str("--dump-packets").to_string())
+ } else {
+ None
+ };
+
+ let no_grease = args.get_bool("--no-grease");
+
+ CommonArgs {
+ alpns,
+ max_data,
+ max_stream_data,
+ max_streams_bidi,
+ max_streams_uni,
+ dump_packet_path,
+ no_grease,
+ }
+ }
+}
+
+pub struct PartialResponse {
+ pub body: Vec<u8>,
+
+ pub written: usize,
+}
+
+pub struct Client {
+ pub conn: std::pin::Pin<Box<quiche::Connection>>,
+
+ pub http_conn: Option<Box<dyn crate::HttpConn>>,
+
+ pub partial_responses: std::collections::HashMap<u64, PartialResponse>,
+}
+
+pub type ClientMap = HashMap<Vec<u8>, (net::SocketAddr, Client)>;
+
+/// Makes a buffered writer for a resource with a target URL.
+///
+/// The file will have the same name as the resource's last path segment value.
+/// Multiple requests for the same URL are indicated by the value of `cardinal`,
+/// any value "N" greater than 1, will cause ".N" to be appended to the
+/// filename.
+fn make_writer(
+ url: &url::Url, target_path: &Option<String>, cardinal: u64,
+) -> Option<std::io::BufWriter<std::fs::File>> {
+ if let Some(tp) = target_path {
+ let resource =
+ url.path_segments().map(|c| c.collect::<Vec<_>>()).unwrap();
+
+ let mut path = format!("{}/{}", tp, resource.iter().last().unwrap());
+
+ if cardinal > 1 {
+ path = format!("{}.{}", path, cardinal);
+ }
+
+ match std::fs::File::create(&path) {
+ Ok(f) => return Some(std::io::BufWriter::new(f)),
+
+ Err(e) => panic!("Bad times: {}", e),
+ }
+ }
+
+ None
+}
+
+pub trait HttpConn {
+ fn send_requests(
+ &mut self, conn: &mut quiche::Connection, target_path: &Option<String>,
+ );
+
+ fn handle_responses(
+ &mut self, conn: &mut quiche::Connection, buf: &mut [u8],
+ req_start: &std::time::Instant,
+ );
+
+ fn report_incomplete(&self, start: &std::time::Instant);
+
+ fn handle_requests(
+ &mut self, conn: &mut std::pin::Pin<Box<quiche::Connection>>,
+ partial_responses: &mut HashMap<u64, PartialResponse>, root: &str,
+ buf: &mut [u8],
+ ) -> quiche::h3::Result<()>;
+
+ fn handle_writable(
+ &mut self, conn: &mut std::pin::Pin<Box<quiche::Connection>>,
+ partial_responses: &mut HashMap<u64, PartialResponse>, stream_id: u64,
+ );
+}
+
+/// Represents an HTTP/0.9 formatted request.
+pub struct Http09Request {
+ url: url::Url,
+ cardinal: u64,
+ request_line: String,
+ stream_id: Option<u64>,
+ response_writer: Option<std::io::BufWriter<std::fs::File>>,
+}
+
+/// Represents an HTTP/3 formatted request.
+struct Http3Request {
+ url: url::Url,
+ cardinal: u64,
+ stream_id: Option<u64>,
+ hdrs: Vec<quiche::h3::Header>,
+ response_writer: Option<std::io::BufWriter<std::fs::File>>,
+}
+
+#[derive(Default)]
+pub struct Http09Conn {
+ stream_id: u64,
+ reqs_sent: usize,
+ reqs_complete: usize,
+ reqs: Vec<Http09Request>,
+}
+
+impl Http09Conn {
+ pub fn with_urls(urls: &[url::Url], reqs_cardinal: u64) -> Box<dyn HttpConn> {
+ let mut reqs = Vec::new();
+ for url in urls {
+ for i in 1..=reqs_cardinal {
+ let request_line = format!("GET {}\r\n", url.path());
+ reqs.push(Http09Request {
+ url: url.clone(),
+ cardinal: i,
+ request_line,
+ stream_id: None,
+ response_writer: None,
+ });
+ }
+ }
+
+ let h_conn = Http09Conn {
+ stream_id: 0,
+ reqs_sent: 0,
+ reqs_complete: 0,
+ reqs,
+ };
+
+ Box::new(h_conn)
+ }
+}
+
+impl HttpConn for Http09Conn {
+ fn send_requests(
+ &mut self, conn: &mut quiche::Connection, target_path: &Option<String>,
+ ) {
+ let mut reqs_done = 0;
+
+ for req in self.reqs.iter_mut().skip(self.reqs_sent) {
+ info!("sending HTTP request {:?}", req.request_line);
+
+ match conn.stream_send(
+ self.stream_id,
+ req.request_line.as_bytes(),
+ true,
+ ) {
+ Ok(v) => v,
+
+ Err(quiche::Error::StreamLimit) => {
+ debug!("not enough stream credits, retry later...");
+ break;
+ },
+
+ Err(e) => {
+ error!("failed to send request {:?}", e);
+ break;
+ },
+ };
+
+ req.stream_id = Some(self.stream_id);
+ req.response_writer =
+ make_writer(&req.url, target_path, req.cardinal);
+
+ self.stream_id += 4;
+
+ reqs_done += 1;
+ }
+
+ self.reqs_sent += reqs_done;
+ }
+
+ fn handle_responses(
+ &mut self, conn: &mut quiche::Connection, buf: &mut [u8],
+ req_start: &std::time::Instant,
+ ) {
+ // Process all readable streams.
+ for s in conn.readable() {
+ while let Ok((read, fin)) = conn.stream_recv(s, buf) {
+ debug!("received {} bytes", read);
+
+ let stream_buf = &buf[..read];
+
+ debug!(
+ "stream {} has {} bytes (fin? {})",
+ s,
+ stream_buf.len(),
+ fin
+ );
+
+ let req = self
+ .reqs
+ .iter_mut()
+ .find(|r| r.stream_id == Some(s))
+ .unwrap();
+
+ match &mut req.response_writer {
+ Some(rw) => {
+ rw.write_all(&buf[..read]).ok();
+ },
+
+ None => {
+ print!("{}", unsafe {
+ std::str::from_utf8_unchecked(&stream_buf)
+ });
+ },
+ }
+
+ // The server reported that it has no more data to send on
+ // a client-initiated
+ // bidirectional stream, which means
+ // we got the full response. If all responses are received
+ // then close the connection.
+ if &s % 4 == 0 && fin {
+ self.reqs_complete += 1;
+ let reqs_count = self.reqs.len();
+
+ debug!(
+ "{}/{} responses received",
+ self.reqs_complete, reqs_count
+ );
+
+ if self.reqs_complete == reqs_count {
+ info!(
+ "{}/{} response(s) received in {:?}, closing...",
+ self.reqs_complete,
+ reqs_count,
+ req_start.elapsed()
+ );
+
+ match conn.close(true, 0x00, b"kthxbye") {
+ // Already closed.
+ Ok(_) | Err(quiche::Error::Done) => (),
+
+ Err(e) => panic!("error closing conn: {:?}", e),
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ fn report_incomplete(&self, start: &std::time::Instant) {
+ if self.reqs_complete != self.reqs.len() {
+ error!(
+ "connection timed out after {:?} and only completed {}/{} requests",
+ start.elapsed(),
+ self.reqs_complete,
+ self.reqs.len()
+ );
+ }
+ }
+
+ fn handle_requests(
+ &mut self, conn: &mut std::pin::Pin<Box<quiche::Connection>>,
+ partial_responses: &mut HashMap<u64, PartialResponse>, root: &str,
+ buf: &mut [u8],
+ ) -> quiche::h3::Result<()> {
+ // Process all readable streams.
+ for s in conn.readable() {
+ while let Ok((read, fin)) = conn.stream_recv(s, buf) {
+ debug!("{} received {} bytes", conn.trace_id(), read);
+
+ let stream_buf = &buf[..read];
+
+ debug!(
+ "{} stream {} has {} bytes (fin? {})",
+ conn.trace_id(),
+ s,
+ stream_buf.len(),
+ fin
+ );
+
+ if stream_buf.len() > 4 && &stream_buf[..4] == b"GET " {
+ let uri = &buf[4..stream_buf.len()];
+ let uri = String::from_utf8(uri.to_vec()).unwrap();
+ let uri = String::from(uri.lines().next().unwrap());
+ let uri = std::path::Path::new(&uri);
+ let mut path = std::path::PathBuf::from(root);
+
+ for c in uri.components() {
+ if let std::path::Component::Normal(v) = c {
+ path.push(v)
+ }
+ }
+
+ info!(
+ "{} got GET request for {:?} on stream {}",
+ conn.trace_id(),
+ path,
+ s
+ );
+
+ let body = std::fs::read(path.as_path())
+ .unwrap_or_else(|_| b"Not Found!\r\n".to_vec());
+
+ info!(
+ "{} sending response of size {} on stream {}",
+ conn.trace_id(),
+ body.len(),
+ s
+ );
+
+ let written = match conn.stream_send(s, &body, true) {
+ Ok(v) => v,
+
+ Err(quiche::Error::Done) => 0,
+
+ Err(e) => {
+ error!(
+ "{} stream send failed {:?}",
+ conn.trace_id(),
+ e
+ );
+ return Err(From::from(e));
+ },
+ };
+
+ if written < body.len() {
+ let response = PartialResponse { body, written };
+ partial_responses.insert(s, response);
+ }
+ }
+ }
+ }
+
+ Ok(())
+ }
+
+ fn handle_writable(
+ &mut self, conn: &mut std::pin::Pin<Box<quiche::Connection>>,
+ partial_responses: &mut HashMap<u64, PartialResponse>, stream_id: u64,
+ ) {
+ debug!("{} stream {} is writable", conn.trace_id(), stream_id);
+
+ if !partial_responses.contains_key(&stream_id) {
+ return;
+ }
+
+ let resp = partial_responses.get_mut(&stream_id).unwrap();
+ let body = &resp.body[resp.written..];
+
+ let written = match conn.stream_send(stream_id, &body, true) {
+ Ok(v) => v,
+
+ Err(quiche::Error::Done) => 0,
+
+ Err(e) => {
+ error!("{} stream send failed {:?}", conn.trace_id(), e);
+ return;
+ },
+ };
+
+ resp.written += written;
+
+ if resp.written == resp.body.len() {
+ partial_responses.remove(&stream_id);
+ }
+ }
+}
+
+pub struct Http3Conn {
+ h3_conn: quiche::h3::Connection,
+ reqs_sent: usize,
+ reqs_complete: usize,
+ reqs: Vec<Http3Request>,
+ body: Option<Vec<u8>>,
+}
+
+impl Http3Conn {
+ pub fn with_urls(
+ conn: &mut quiche::Connection, urls: &[url::Url], reqs_cardinal: u64,
+ req_headers: &[String], body: &Option<Vec<u8>>, method: &str,
+ ) -> Box<dyn HttpConn> {
+ let mut reqs = Vec::new();
+ for url in urls {
+ for i in 1..=reqs_cardinal {
+ let mut hdrs = vec![
+ quiche::h3::Header::new(":method", &method),
+ quiche::h3::Header::new(":scheme", url.scheme()),
+ quiche::h3::Header::new(
+ ":authority",
+ url.host_str().unwrap(),
+ ),
+ quiche::h3::Header::new(
+ ":path",
+ &url[url::Position::BeforePath..],
+ ),
+ quiche::h3::Header::new("user-agent", "quiche"),
+ ];
+
+ // Add custom headers to the request.
+ for header in req_headers {
+ let header_split: Vec<&str> =
+ header.splitn(2, ": ").collect();
+ if header_split.len() != 2 {
+ panic!("malformed header provided - \"{}\"", header);
+ }
+
+ hdrs.push(quiche::h3::Header::new(
+ header_split[0],
+ header_split[1],
+ ));
+ }
+
+ if body.is_some() {
+ hdrs.push(quiche::h3::Header::new(
+ "content-length",
+ &body.as_ref().unwrap().len().to_string(),
+ ));
+ }
+
+ reqs.push(Http3Request {
+ url: url.clone(),
+ cardinal: i,
+ hdrs,
+ stream_id: None,
+ response_writer: None,
+ });
+ }
+ }
+
+ let h_conn = Http3Conn {
+ h3_conn: quiche::h3::Connection::with_transport(
+ conn,
+ &quiche::h3::Config::new().unwrap(),
+ )
+ .unwrap(),
+ reqs_sent: 0,
+ reqs_complete: 0,
+ reqs,
+ body: None,
+ };
+
+ Box::new(h_conn)
+ }
+
+ pub fn with_conn(conn: &mut quiche::Connection) -> Box<dyn HttpConn> {
+ let h_conn = Http3Conn {
+ h3_conn: quiche::h3::Connection::with_transport(
+ conn,
+ &quiche::h3::Config::new().unwrap(),
+ )
+ .unwrap(),
+ reqs_sent: 0,
+ reqs_complete: 0,
+ reqs: Vec::new(),
+ body: None,
+ };
+
+ Box::new(h_conn)
+ }
+
+ /// Builds an HTTP/3 response given a request.
+ fn build_h3_response(
+ root: &str, request: &[quiche::h3::Header],
+ ) -> (Vec<quiche::h3::Header>, Vec<u8>) {
+ let mut file_path = std::path::PathBuf::from(root);
+ let mut path = std::path::Path::new("");
+ let mut method = "";
+
+ // Look for the request's path and method.
+ for hdr in request {
+ match hdr.name() {
+ ":path" => {
+ path = std::path::Path::new(hdr.value());
+ },
+
+ ":method" => {
+ method = hdr.value();
+ },
+
+ _ => (),
+ }
+ }
+
+ let (status, body) = match method {
+ "GET" => {
+ for c in path.components() {
+ if let std::path::Component::Normal(v) = c {
+ file_path.push(v)
+ }
+ }
+
+ match std::fs::read(file_path.as_path()) {
+ Ok(data) => (200, data),
+
+ Err(_) => (404, b"Not Found!".to_vec()),
+ }
+ },
+
+ _ => (405, Vec::new()),
+ };
+
+ let headers = vec![
+ quiche::h3::Header::new(":status", &status.to_string()),
+ quiche::h3::Header::new("server", "quiche"),
+ quiche::h3::Header::new("content-length", &body.len().to_string()),
+ ];
+
+ (headers, body)
+ }
+}
+
+impl HttpConn for Http3Conn {
+ fn send_requests(
+ &mut self, conn: &mut quiche::Connection, target_path: &Option<String>,
+ ) {
+ let mut reqs_done = 0;
+
+ for req in self.reqs.iter_mut().skip(self.reqs_sent) {
+ info!("sending HTTP request {:?}", req.hdrs);
+
+ let s = match self.h3_conn.send_request(
+ conn,
+ &req.hdrs,
+ self.body.is_none(),
+ ) {
+ Ok(v) => v,
+
+ Err(quiche::h3::Error::TransportError(
+ quiche::Error::StreamLimit,
+ )) => {
+ debug!("not enough stream credits, retry later...");
+ break;
+ },
+
+ Err(e) => {
+ error!("failed to send request {:?}", e);
+ break;
+ },
+ };
+
+ req.stream_id = Some(s);
+ req.response_writer =
+ make_writer(&req.url, target_path, req.cardinal);
+
+ if let Some(body) = &self.body {
+ if let Err(e) = self.h3_conn.send_body(conn, s, body, true) {
+ error!("failed to send request body {:?}", e);
+ break;
+ }
+ }
+
+ reqs_done += 1;
+ }
+
+ self.reqs_sent += reqs_done;
+ }
+
+ fn handle_responses(
+ &mut self, conn: &mut quiche::Connection, buf: &mut [u8],
+ req_start: &std::time::Instant,
+ ) {
+ loop {
+ match self.h3_conn.poll(conn) {
+ Ok((stream_id, quiche::h3::Event::Headers { list, .. })) => {
+ info!(
+ "got response headers {:?} on stream id {}",
+ list, stream_id
+ );
+ },
+
+ Ok((stream_id, quiche::h3::Event::Data)) => {
+ if let Ok(read) = self.h3_conn.recv_body(conn, stream_id, buf)
+ {
+ debug!(
+ "got {} bytes of response data on stream {}",
+ read, stream_id
+ );
+
+ let req = self
+ .reqs
+ .iter_mut()
+ .find(|r| r.stream_id == Some(stream_id))
+ .unwrap();
+
+ match &mut req.response_writer {
+ Some(rw) => {
+ rw.write_all(&buf[..read]).ok();
+ },
+
+ None => {
+ print!("{}", unsafe {
+ std::str::from_utf8_unchecked(&buf[..read])
+ });
+ },
+ }
+ }
+ },
+
+ Ok((_stream_id, quiche::h3::Event::Finished)) => {
+ self.reqs_complete += 1;
+ let reqs_count = self.reqs.len();
+
+ debug!(
+ "{}/{} responses received",
+ self.reqs_complete, reqs_count
+ );
+
+ if self.reqs_complete == reqs_count {
+ info!(
+ "{}/{} response(s) received in {:?}, closing...",
+ self.reqs_complete,
+ reqs_count,
+ req_start.elapsed()
+ );
+
+ match conn.close(true, 0x00, b"kthxbye") {
+ // Already closed.
+ Ok(_) | Err(quiche::Error::Done) => (),
+
+ Err(e) => panic!("error closing conn: {:?}", e),
+ }
+
+ break;
+ }
+ },
+
+ Err(quiche::h3::Error::Done) => {
+ break;
+ },
+
+ Err(e) => {
+ error!("HTTP/3 processing failed: {:?}", e);
+
+ break;
+ },
+ }
+ }
+ }
+
+ fn report_incomplete(&self, start: &std::time::Instant) {
+ if self.reqs_complete != self.reqs.len() {
+ error!(
+ "connection timed out after {:?} and only completed {}/{} requests",
+ start.elapsed(),
+ self.reqs_complete,
+ self.reqs.len()
+ );
+ }
+ }
+
+ fn handle_requests(
+ &mut self, conn: &mut std::pin::Pin<Box<quiche::Connection>>,
+ partial_responses: &mut HashMap<u64, PartialResponse>, root: &str,
+ _buf: &mut [u8],
+ ) -> quiche::h3::Result<()> {
+ // Process HTTP events.
+ loop {
+ match self.h3_conn.poll(conn) {
+ Ok((stream_id, quiche::h3::Event::Headers { list, .. })) => {
+ info!(
+ "{} got request {:?} on stream id {}",
+ conn.trace_id(),
+ &list,
+ stream_id
+ );
+
+ // We decide the response based on headers alone, so
+ // stop reading the request stream so that any body
+ // is ignored and pointless Data events are not
+ // generated.
+ conn.stream_shutdown(stream_id, quiche::Shutdown::Read, 0)
+ .unwrap();
+
+ let (headers, body) =
+ Http3Conn::build_h3_response(root, &list);
+
+ if let Err(e) = self
+ .h3_conn
+ .send_response(conn, stream_id, &headers, false)
+ {
+ error!("{} stream send failed {:?}", conn.trace_id(), e);
+ }
+
+ let written = match self
+ .h3_conn
+ .send_body(conn, stream_id, &body, true)
+ {
+ Ok(v) => v,
+
+ Err(quiche::h3::Error::Done) => 0,
+
+ Err(e) => {
+ error!(
+ "{} stream send failed {:?}",
+ conn.trace_id(),
+ e
+ );
+ break;
+ },
+ };
+
+ if written < body.len() {
+ let response = PartialResponse { body, written };
+ partial_responses.insert(stream_id, response);
+ }
+ },
+
+ Ok((stream_id, quiche::h3::Event::Data)) => {
+ info!(
+ "{} got data on stream id {}",
+ conn.trace_id(),
+ stream_id
+ );
+ },
+
+ Ok((_stream_id, quiche::h3::Event::Finished)) => (),
+
+ Err(quiche::h3::Error::Done) => {
+ break;
+ },
+
+ Err(e) => {
+ error!("{} HTTP/3 error {:?}", conn.trace_id(), e);
+
+ return Err(e);
+ },
+ }
+ }
+
+ Ok(())
+ }
+
+ fn handle_writable(
+ &mut self, conn: &mut std::pin::Pin<Box<quiche::Connection>>,
+ partial_responses: &mut HashMap<u64, PartialResponse>, stream_id: u64,
+ ) {
+ debug!("{} stream {} is writable", conn.trace_id(), stream_id);
+
+ if !partial_responses.contains_key(&stream_id) {
+ return;
+ }
+
+ let resp = partial_responses.get_mut(&stream_id).unwrap();
+ let body = &resp.body[resp.written..];
+
+ let written = match self.h3_conn.send_body(conn, stream_id, body, true) {
+ Ok(v) => v,
+
+ Err(quiche::h3::Error::Done) => 0,
+
+ Err(e) => {
+ error!("{} stream send failed {:?}", conn.trace_id(), e);
+ return;
+ },
+ };
+
+ resp.written += written;
+
+ if resp.written == resp.body.len() {
+ partial_responses.remove(&stream_id);
+ }
+ }
+}
diff --git a/tools/build_android_ndk19.sh b/tools/build_android_ndk19.sh
new file mode 100755
index 0000000..aff1dec
--- /dev/null
+++ b/tools/build_android_ndk19.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# Build quiche for Android NDK 19 or higher
+#
+# ANDROID_NDK_HOME : android ndk location
+# TOOLCHAIN_DIR : where create a toolchain (optional)
+#
+set -eu
+
+# Change this value if you need a different API level
+# 21 is the minimum API tested
+API_LEVEL=21
+
+if [ ! -d "${ANDROID_NDK_HOME-}" ]; then
+ ANDROID_NDK_HOME=/usr/local/share/android-ndk
+fi
+
+if [ ! -d "${TOOLCHAIN_DIR-}" ]; then
+ TOOLCHAIN_DIR=$(pwd)
+fi
+
+echo "> building quiche for android API $API_LEVEL..."
+
+for target in \
+ aarch64-linux-android \
+ armv7-linux-androideabi \
+ i686-linux-android
+do
+ echo "> buliding $target..."
+ cargo ndk --target $target --android-platform $API_LEVEL -- build $*
+done
diff --git a/tools/http3_test/.gitignore b/tools/http3_test/.gitignore
deleted file mode 100644
index 6936990..0000000
--- a/tools/http3_test/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-/target
-**/*.rs.bk
-Cargo.lock
diff --git a/tools/http3_test/README.md b/tools/http3_test/README.md
index 666396d..9cf16f1 100644
--- a/tools/http3_test/README.md
+++ b/tools/http3_test/README.md
@@ -1,6 +1,6 @@
-This crate provides an API to build httpbin test requests and expected
-outcomes. These can be used with the quiche HTTP/3 module to
-communicate with an httpbin test server.
+This crate provides an API to build httpbin test requests and expected outcomes.
+These can be used with the quiche HTTP/3 module to communicate with an httpbin
+test server.
Building
--------
@@ -11,13 +11,45 @@
Running
--------
-We use cargo test to execute different httpbin tests. By default this points to https://cloudflare-quic.com/b
+We use cargo test to execute different httpbin tests. By default this points to
+https://cloudflare-quic.com/b
```bash
$ cargo test
```
-To test a different server, use the HTTPBIN_ENDPOINT environment variable
+Environment Variable Overrides
+------------------------------
+
+A set of environment variables allow tuning of test beheviour:
+
+* HTTPBIN_ENDPOINT - httpbin server URL, the authority is used in SNI.
+ Default value is https://cloudflare-quic.com/b
+
+* HTTPBIN_HOST - httpbin server IP address and port (<SERVER>:<PORT>).
+ Overrides the effective target authority defined in
+ HTTPBIN_ENDPOINT. Default value does not override.
+
+* VERIFY_PEER - boolean value ("true" or "false") that controls if
+ the test client attempts to validate the httpbin
+ server certificate. Default value is true.
+
+* IDLE_TIMEOUT - client's idle timeout in integer milliseconds.
+ Default value is 60000 (60 seconds).
+
+* QUIC_VERSION - wire protocol version used by the client as a hex value
+ (e.g. "0xbabababa"). By default an invalid version is used
+ to trigger version negotiation. If set to "current", the
+ current supported version is used.
+
+* EXTRA_HEADERS - additional request headers in JSON format.
+ Currently used by `headers` test only.
+
+* EXPECT_REQ_HEADERS - expected request header/value pairs in JSON format.
+ Currently used by `headers` test only.
+
+For example, to test a non-default server, use the HTTPBIN_ENDPOINT environment
+variable
```bash
$ HTTPBIN_ENDPOINT=https://<some_other_endpoint> cargo test
diff --git a/tools/http3_test/src/lib.rs b/tools/http3_test/src/lib.rs
index cd6d0be..197f46b 100644
--- a/tools/http3_test/src/lib.rs
+++ b/tools/http3_test/src/lib.rs
@@ -157,8 +157,8 @@
//! # let h3_config = quiche::h3::Config::new()?;
//! # let mut http3_conn = quiche::h3::Connection::with_transport(&mut conn, &h3_config)?;
//! match http3_conn.poll(&mut conn) {
-//! Ok((stream_id, quiche::h3::Event::Headers(headers))) => {
-//! test.add_response_headers(stream_id, &headers);
+//! Ok((stream_id, quiche::h3::Event::Headers{list, has_body})) => {
+//! test.add_response_headers(stream_id, &list);
//! },
//!
//! Ok((stream_id, quiche::h3::Event::Data)) => {
@@ -274,7 +274,7 @@
url: url.clone(),
hdrs,
body,
- expect_resp_hdrs: expect_resp_hdrs.clone(),
+ expect_resp_hdrs,
resp_hdrs: Vec::new(),
resp_body: Vec::new(),
}
diff --git a/tools/http3_test/src/runner.rs b/tools/http3_test/src/runner.rs
index e919f96..31cdabf 100644
--- a/tools/http3_test/src/runner.rs
+++ b/tools/http3_test/src/runner.rs
@@ -39,8 +39,17 @@
let max_stream_data = 1_000_000;
- let version = "babababa";
- let version = u32::from_str_radix(version, 16).unwrap();
+ let version = if let Some(v) = std::env::var_os("QUIC_VERSION") {
+ match v.to_str() {
+ Some("current") => quiche::PROTOCOL_VERSION,
+
+ Some(v) => u32::from_str_radix(v, 16).unwrap(),
+
+ _ => 0xbaba_baba,
+ }
+ } else {
+ 0xbaba_baba
+ };
let mut reqs_count = 0;
@@ -83,7 +92,7 @@
.set_application_protos(quiche::h3::APPLICATION_PROTOCOL)
.unwrap();
- config.set_idle_timeout(idle_timeout);
+ config.set_max_idle_timeout(idle_timeout);
config.set_max_packet_size(MAX_DATAGRAM_SIZE as u64);
config.set_initial_max_data(max_data);
config.set_initial_max_stream_data_bidi_local(max_stream_data);
@@ -209,13 +218,13 @@
// Process HTTP/3 events.
loop {
match http3_conn.poll(&mut conn) {
- Ok((stream_id, quiche::h3::Event::Headers(headers))) => {
+ Ok((stream_id, quiche::h3::Event::Headers { list, .. })) => {
info!(
"got response headers {:?} on stream id {}",
- headers, stream_id
+ &list, stream_id
);
- test.add_response_headers(stream_id, &headers);
+ test.add_response_headers(stream_id, &list);
},
Ok((stream_id, quiche::h3::Event::Data)) => {
diff --git a/tools/http3_test/tests/httpbin_tests.rs b/tools/http3_test/tests/httpbin_tests.rs
index 6ce997d..cea89e1 100644
--- a/tools/http3_test/tests/httpbin_tests.rs
+++ b/tools/http3_test/tests/httpbin_tests.rs
@@ -1,3 +1,29 @@
+// Copyright (C) 2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
mod httpbin_tests {
use std::collections::HashMap;
use std::net::ToSocketAddrs;
@@ -68,6 +94,29 @@
}
}
+ fn extra_headers() -> Option<serde_json::Map<String, serde_json::Value>> {
+ if let Some(val) = std::env::var_os("EXTRA_HEADERS") {
+ let json_string = val.into_string().unwrap();
+ let parsed: serde_json::Value =
+ serde_json::from_str(&json_string).unwrap();
+ return Some(parsed.as_object().unwrap().clone());
+ }
+
+ return None;
+ }
+
+ fn expect_req_headers() -> Option<serde_json::Map<String, serde_json::Value>>
+ {
+ if let Some(val) = std::env::var_os("EXPECT_REQ_HEADERS") {
+ let json_string = val.into_string().unwrap();
+ let parsed: serde_json::Value =
+ serde_json::from_str(&json_string).unwrap();
+ return Some(parsed.as_object().unwrap().clone());
+ }
+
+ return None;
+ }
+
// A rudimentary structure to hold httpbin response data
#[derive(Debug, serde::Deserialize)]
struct HttpBinResponseBody {
@@ -204,14 +253,44 @@
#[test]
fn headers() {
- let reqs = request_check_status("headers", 200);
+ let mut reqs = Vec::new();
+ let expect_hdrs = Some(vec![Header::new(":status", "200")]);
+
+ let mut url = endpoint(Some("headers"));
+ url.set_query(Some("show_env=1")); // reveal X-Forwarded-* headers
+ reqs.push(Http3Req::new("GET", &url, None, expect_hdrs.clone()));
+
+ if let Some(headers) = &extra_headers() {
+ for (name, val) in headers {
+ reqs[0].hdrs.push(Header::new(&name, val.as_str().unwrap()));
+ }
+ };
let assert = |reqs: &[Http3Req]| {
assert_headers!(reqs[0]);
let json = jsonify(&reqs[0].resp_body);
- if let Some(args) = json.args {
- assert_eq!(args["Host"], reqs[0].url.host_str().unwrap());
+ assert_ne!(json.headers, None);
+ if let Some(headers) = json.headers {
+ if let Some(expected_headers) = &expect_req_headers() {
+ for (name, val) in expected_headers {
+ if let Some(expected_value) = val.as_str() {
+ assert_eq!(
+ headers.get(name),
+ Some(&String::from(expected_value)),
+ "Header '{}' doesn't match",
+ name
+ );
+ } else {
+ assert_eq!(
+ headers.get(name),
+ None,
+ "Header '{}' exists",
+ name
+ );
+ }
+ }
+ }
}
};
diff --git a/tools/qlog/Cargo.toml b/tools/qlog/Cargo.toml
new file mode 100644
index 0000000..aa7e473
--- /dev/null
+++ b/tools/qlog/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "qlog"
+version = "0.1.0"
+authors = ["Lucas Pardue <lucaspardue.24.7@gmail.com>"]
+edition = "2018"
+description = "qlog data model for QUIC and HTTP/3"
+repository = "https://github.com/cloudflare/quiche"
+readme = "README.md"
+keywords = ["qlog", "quic", "http3"]
+categories = ["network-programming"]
+license = "BSD-2-Clause"
+
+[dependencies]
+serde = { version = "1", features = ["derive"] }
+serde_json = "1.0"
+serde_derive = "1.0"
+serde_with = "1.3.1"
diff --git a/tools/qlog/README.md b/tools/qlog/README.md
new file mode 100644
index 0000000..6da28aa
--- /dev/null
+++ b/tools/qlog/README.md
@@ -0,0 +1,139 @@
+The qlog crate is an implementation of the [qlog main schema] and [qlog QUIC and
+HTTP/3 events] that attempts to closely follow the format of the qlog
+[TypeScript schema]. This is just a data model and no support is provided for
+logging IO, applications can decide themselves the most appropriate method.
+
+The crate uses Serde for conversion between Rust and JSON.
+
+[qlog main schema]: https://tools.ietf.org/html/draft-marx-qlog-main-schema
+[qlog QUIC and HTTP/3 events]: https://quiclog.github.io/internet-drafts/draft-marx-qlog-event-definitions-quic-h3
+[TypeScript schema]: https://github.com/quiclog/qlog/blob/master/TypeScript/draft-01/QLog.ts
+
+Getting Started
+---------------
+
+### Creating a trace
+
+A typical application needs a single qlog trace that it appends QUIC and/or
+HTTP/3 events to:
+
+```rust
+let trace = Trace {
+ vantage_point: VantagePoint {
+ name: "Example client",
+ ty: VantagePointType::Client,
+ flow: None,
+ },
+ title: Some("Example qlog trace".to_string()),
+ description: Some("Example qlog trace description".to_string()),
+ configuration: Some(Configuration {
+ time_offset: Some("0".to_string()),
+ time_units: Some(TimeUnits::Ms),
+ original_uris: None,
+ }),
+ common_fields: None,
+ event_fields: vec![
+ "relative_time".to_string(),
+ "category".to_string(),
+ "event".to_string(),
+ "data".to_string(),
+ ],
+ events: Vec::new(),
+};
+
+```
+
+### Adding events
+
+Qlog Events are added to `qlog::Trace.events`. Utility method are provided for
+the various types of QUIC and HTTP/3 events. The following example demonstrates
+how to log a QUIC packet containing a single Crypto frame, it uses the
+`push_transport_event()` and `QuicFrame::crypto()` methods to capture a
+PacketSent event and its EventData.
+
+```rust
+trace.push_transport_event(
+ "0".to_string(),
+ TransportEventType::PacketSent,
+ EventData::PacketSent {
+ raw_encrypted: None,
+ raw_decrypted: None,
+ packet_type: PacketType::Initial,
+ header: PacketHeader {
+ packet_number: "0".to_string(),
+ packet_size: Some(1251),
+ payload_length: Some(1224),
+ version: Some("0xff000018".to_string()),
+ scil: Some("8".to_string()),
+ dcil: Some("8".to_string()),
+ scid: Some("7e37e4dcc6682da8".to_string()),
+ dcid: Some("36ce104eee50101c".to_string()),
+ },
+ frames: Some(vec![
+ QuicFrame::crypto(
+ "0".to_string(),
+ "1000".to_string(),
+ )
+ ]),
+ is_coalesced: None,
+ },
+);
+```
+
+### Serializing
+
+Simply:
+
+```rust
+serde_json::to_string_pretty(&trace).unwrap();
+```
+
+which would generate the following:
+
+```
+{
+ "vantage_point": {
+ "name": "Example client",
+ "type": "client"
+ },
+ "title": "Example qlog trace",
+ "description": "Example qlog trace description",
+ "configuration": {
+ "time_units": "ms",
+ "time_offset": "0"
+ },
+ "event_fields": [
+ "relative_time",
+ "category",
+ "event",
+ "data"
+ ],
+ "events": [
+ [
+ "0",
+ "transport",
+ "packet_sent",
+ {
+ "packet_type": "initial",
+ "header": {
+ "packet_number": "0",
+ "packet_size": 1251,
+ "payload_length": 1224,
+ "version": "0xff000018",
+ "scil": "8",
+ "dcil": "8",
+ "scid": "7e37e4dcc6682da8",
+ "dcid": "36ce104eee50101c"
+ },
+ "frames": [
+ {
+ "frame_type": "crypto",
+ "offset": "0",
+ "length": "100",
+ }
+ ]
+ }
+ ]
+ ]
+}
+```
diff --git a/tools/qlog/src/lib.rs b/tools/qlog/src/lib.rs
new file mode 100644
index 0000000..15dc8dd
--- /dev/null
+++ b/tools/qlog/src/lib.rs
@@ -0,0 +1,1904 @@
+// Copyright (C) 2019, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use serde::{
+ Deserialize,
+ Serialize,
+};
+
+pub const QLOG_VERSION: &str = "draft-01";
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Deserialize, Clone)]
+pub struct Qlog {
+ pub qlog_version: String,
+ pub title: Option<String>,
+ pub description: Option<String>,
+ pub summary: Option<String>,
+
+ pub traces: Vec<Trace>,
+}
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Deserialize, Clone)]
+pub struct Trace {
+ pub vantage_point: VantagePoint,
+ pub title: Option<String>,
+ pub description: Option<String>,
+
+ pub configuration: Option<Configuration>,
+
+ pub common_fields: Option<CommonFields>,
+ pub event_fields: Vec<String>,
+
+ pub events: Vec<Vec<EventField>>,
+}
+
+/// Helper functions for using a qlog trace.
+impl Trace {
+ fn push_event(
+ &mut self, relative_time: String, category: EventCategory,
+ event: EventType, data: EventData,
+ ) {
+ self.events.push(vec![
+ EventField::RelativeTime(relative_time),
+ EventField::Category(category),
+ EventField::Event(event),
+ EventField::Data(data),
+ ]);
+ }
+
+ /// Appends an `ConnectivityEventType` to the back of a qlog trace.
+ pub fn push_connectivity_event(
+ &mut self, relative_time: String, event: ConnectivityEventType,
+ data: EventData,
+ ) {
+ self.push_event(
+ relative_time,
+ EventCategory::Connectivity,
+ EventType::ConnectivityEventType(event),
+ data,
+ );
+ }
+
+ /// Appends a `TransportEventType` to the back of a qlog trace.
+ pub fn push_transport_event(
+ &mut self, relative_time: String, event: TransportEventType,
+ data: EventData,
+ ) {
+ self.push_event(
+ relative_time,
+ EventCategory::Transport,
+ EventType::TransportEventType(event),
+ data,
+ );
+ }
+
+ /// Appends a `TransportEventType` to the back of a qlog trace.
+ pub fn push_security_event(
+ &mut self, relative_time: String, event: SecurityEventType,
+ data: EventData,
+ ) {
+ self.push_event(
+ relative_time,
+ EventCategory::Security,
+ EventType::SecurityEventType(event),
+ data,
+ );
+ }
+
+ /// Appends a `TransportEventType` to the back of a qlog trace.
+ pub fn push_recovery_event(
+ &mut self, relative_time: String, event: RecoveryEventType,
+ data: EventData,
+ ) {
+ self.push_event(
+ relative_time,
+ EventCategory::Recovery,
+ EventType::RecoveryEventType(event),
+ data,
+ );
+ }
+}
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Deserialize, Clone)]
+pub struct VantagePoint {
+ pub name: Option<String>,
+
+ #[serde(rename = "type")]
+ pub ty: VantagePointType,
+
+ pub flow: Option<VantagePointType>,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum VantagePointType {
+ Client,
+ Server,
+ Network,
+ Unknown,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum TimeUnits {
+ Ms,
+ Us,
+}
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Deserialize, Clone)]
+pub struct Configuration {
+ pub time_units: Option<TimeUnits>,
+ pub time_offset: Option<String>,
+
+ pub original_uris: Option<Vec<String>>,
+ /* TODO
+ * additionalUserSpecifiedProperty */
+}
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Deserialize, Clone)]
+pub struct CommonFields {
+ pub group_id: Option<String>,
+ pub protocol_type: Option<String>,
+
+ pub reference_time: Option<String>,
+ /* TODO
+ * additionalUserSpecifiedProperty */
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(untagged)]
+pub enum EventType {
+ ConnectivityEventType(ConnectivityEventType),
+
+ TransportEventType(TransportEventType),
+
+ SecurityEventType(SecurityEventType),
+
+ RecoveryEventType(RecoveryEventType),
+
+ Http3EventType(Http3EventType),
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(untagged)]
+#[allow(clippy::large_enum_variant)]
+pub enum EventField {
+ RelativeTime(String),
+
+ Category(EventCategory),
+
+ Event(EventType),
+
+ Data(EventData),
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum EventCategory {
+ Connectivity,
+ Security,
+ Transport,
+ Recovery,
+ Http,
+ Qpack,
+
+ Error,
+ Warning,
+ Info,
+ Debug,
+ Verbose,
+ Simulation,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum ConnectivityEventType {
+ ServerListening,
+ ConnectionStarted,
+ ConnectionIdUpdated,
+ SpinBitUpdated,
+ ConnectionStateUpdated,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum TransportEventType {
+ ParametersSet,
+
+ DatagramsSent,
+ DatagramsReceived,
+ DatagramDropped,
+
+ PacketSent,
+ PacketReceived,
+ PacketDropped,
+ PacketBuffered,
+
+ FramesProcessed,
+
+ StreamStateUpdated,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum TransportEventTrigger {
+ Line,
+ Retransmit,
+ KeysUnavailable,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum SecurityEventType {
+ KeyUpdated,
+ KeyRetired,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum SecurityEventTrigger {
+ Tls,
+ Implicit,
+ RemoteUpdate,
+ LocalUpdate,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum RecoveryEventType {
+ ParametersSet,
+ MetricsUpdated,
+ CongestionStateUpdated,
+ LossTimerSet,
+ LossTimerTriggered,
+ PacketLost,
+ MarkedForRetransmit,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum RecoveryEventTrigger {
+ AckReceived,
+ PacketSent,
+ Alarm,
+ Unknown,
+}
+
+// ================================================================== //
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum KeyType {
+ ServerInitialSecret,
+ ClientInitialSecret,
+
+ ServerHandshakeSecret,
+ ClientHandshakeSecret,
+
+ Server0RttSecret,
+ Client0RttSecret,
+
+ Server1RttSecret,
+ Client1RttSecret,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum ConnectionState {
+ Attempted,
+ Reset,
+ Handshake,
+ Active,
+ Keepalive,
+ Draining,
+ Closed,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum TransportOwner {
+ Local,
+ Remote,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct PreferredAddress {
+ ip_v4: String,
+ ip_v6: String,
+
+ port_v4: u64,
+ port_v6: u64,
+
+ connection_id: String,
+ stateless_reset_token: String,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum StreamSide {
+ Sending,
+ Receiving,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum StreamState {
+ // bidirectional stream states, draft-23 3.4.
+ Idle,
+ Open,
+ HalfClosedLocal,
+ HalfClosedRemote,
+ Closed,
+
+ // sending-side stream states, draft-23 3.1.
+ Ready,
+ Send,
+ DataSent,
+ ResetSent,
+ ResetReceived,
+
+ // receive-side stream states, draft-23 3.2.
+ Receive,
+ SizeKnown,
+ DataRead,
+ ResetRead,
+
+ // both-side states
+ DataReceived,
+
+ // qlog-defined
+ Destroyed,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum TimerType {
+ Ack,
+ Pto,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum H3Owner {
+ Local,
+ Remote,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum H3StreamType {
+ Data,
+ Control,
+ Push,
+ Reserved,
+ QpackEncode,
+ QpackDecode,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum H3DataRecipient {
+ Application,
+ Transport,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum H3PushDecision {
+ Claimed,
+ Abandoned,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum QpackOwner {
+ Local,
+ Remote,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum QpackStreamState {
+ Blocked,
+ Unblocked,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum QpackUpdateType {
+ Added,
+ Evicted,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct QpackDynamicTableEntry {
+ index: u64,
+ name: Option<String>,
+ value: Option<String>,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct QpackHeaderBlockPrefix {
+ required_insert_count: u64,
+ sign_bit: bool,
+ delta_base: u64,
+}
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(untagged)]
+#[allow(clippy::large_enum_variant)]
+pub enum EventData {
+ // ================================================================== //
+ // CONNECTIVITY
+ ServerListening {
+ ip_v4: Option<String>,
+ ip_v6: Option<String>,
+ port_v4: u64,
+ port_v6: u64,
+
+ quic_versions: Option<Vec<String>>,
+ alpn_values: Option<Vec<String>>,
+
+ stateless_reset_required: Option<bool>,
+ },
+
+ ConnectionStarted {
+ ip_version: String,
+ src_ip: String,
+ dst_ip: String,
+
+ protocol: Option<String>,
+ src_port: u64,
+ dst_port: u64,
+
+ quic_version: Option<String>,
+ src_cid: Option<String>,
+ dst_cid: Option<String>,
+ },
+
+ ConnectionIdUpdated {
+ src_old: Option<String>,
+ src_new: Option<String>,
+
+ dst_old: Option<String>,
+ dst_new: Option<String>,
+ },
+
+ SpinBitUpdated {
+ state: bool,
+ },
+
+ ConnectionStateUpdated {
+ old: Option<ConnectionState>,
+ new: ConnectionState,
+ },
+
+ // ================================================================== //
+ // SECURITY
+ KeyUpdated {
+ key_type: KeyType,
+ old: Option<String>,
+ new: String,
+ generation: Option<u64>,
+ },
+
+ KeyRetired {
+ key_type: KeyType,
+ key: Option<String>,
+ generation: Option<u64>,
+ },
+
+ // ================================================================== //
+ // TRANSPORT
+ TransportParametersSet {
+ owner: Option<TransportOwner>,
+
+ resumption_allowed: Option<bool>,
+ early_data_enabled: Option<bool>,
+ alpn: Option<String>,
+ version: Option<String>,
+ tls_cipher: Option<String>,
+
+ original_connection_id: Option<String>,
+ stateless_reset_token: Option<String>,
+ disable_active_migration: Option<bool>,
+
+ idle_timeout: Option<u64>,
+ max_packet_size: Option<u64>,
+ ack_delay_exponent: Option<u64>,
+ max_ack_delay: Option<u64>,
+ active_connection_id_limit: Option<u64>,
+
+ initial_max_data: Option<String>,
+ initial_max_stream_data_bidi_local: Option<String>,
+ initial_max_stream_data_bidi_remote: Option<String>,
+ initial_max_stream_data_uni: Option<String>,
+ initial_max_streams_bidi: Option<String>,
+ initial_max_streams_uni: Option<String>,
+
+ preferred_address: Option<PreferredAddress>,
+ },
+
+ DatagramsReceived {
+ count: Option<u64>,
+ byte_length: Option<u64>,
+ },
+
+ DatagramsSent {
+ count: Option<u64>,
+ byte_length: Option<u64>,
+ },
+
+ DatagramDropped {
+ byte_length: Option<u64>,
+ },
+
+ PacketReceived {
+ packet_type: PacketType,
+ header: PacketHeader,
+ frames: Option<Vec<QuicFrame>>,
+
+ is_coalesced: Option<bool>,
+
+ raw_encrypted: Option<String>,
+ raw_decrypted: Option<String>,
+ },
+
+ PacketSent {
+ packet_type: PacketType,
+ header: PacketHeader,
+ frames: Option<Vec<QuicFrame>>,
+
+ is_coalesced: Option<bool>,
+
+ raw_encrypted: Option<String>,
+ raw_decrypted: Option<String>,
+ },
+
+ PacketDropped {
+ packet_type: Option<PacketType>,
+ packet_size: Option<u64>,
+
+ raw: Option<String>,
+ },
+
+ PacketBuffered {
+ packet_type: PacketType,
+ packet_number: String,
+ },
+
+ SteamStateUpdated {
+ stream_id: String,
+ stream_type: Option<StreamType>,
+
+ old: Option<StreamState>,
+ new: StreamState,
+
+ stream_side: Option<StreamSide>,
+ },
+
+ FramesProcessed {
+ frames: Vec<QuicFrame>,
+ },
+
+ // ================================================================== //
+ // RECOVERY
+ RecoveryParametersSet {
+ reordering_threshold: Option<u64>,
+ time_threshold: Option<u64>,
+ timer_granularity: Option<u64>,
+ initial_rtt: Option<u64>,
+
+ max_datagram_size: Option<u64>,
+ initial_congestion_window: Option<u64>,
+ minimum_congestion_window: Option<u64>,
+ loss_reduction_factor: Option<u64>,
+ persistent_congestion_threshold: Option<u64>,
+ },
+
+ MetricsUpdated {
+ min_rtt: Option<u64>,
+ smoothed_rtt: Option<u64>,
+ latest_rtt: Option<u64>,
+ rtt_variance: Option<u64>,
+
+ max_ack_delay: Option<u64>,
+ pto_count: Option<u64>,
+
+ congestion_window: Option<u64>,
+ bytes_in_flight: Option<u64>,
+
+ ssthresh: Option<u64>,
+
+ // qlog defined
+ packets_in_flight: Option<u64>,
+ in_recovery: Option<bool>,
+
+ pacing_rate: Option<u64>,
+ },
+
+ CongestionStateUpdated {
+ old: Option<String>,
+ new: String,
+ },
+
+ LossTimerSet {
+ timer_type: Option<TimerType>,
+ timeout: Option<String>,
+ },
+
+ PacketLost {
+ packet_type: PacketType,
+ packet_number: String,
+
+ header: Option<PacketHeader>,
+ frames: Vec<QuicFrame>,
+ },
+
+ MarkedForRetransmit {
+ frames: Vec<QuicFrame>,
+ },
+
+ // ================================================================== //
+ // HTTP/3
+ H3ParametersSet {
+ owner: Option<H3Owner>,
+
+ max_header_list_size: Option<u64>,
+ max_table_capacity: Option<u64>,
+ blocked_streams_count: Option<u64>,
+
+ push_allowed: Option<bool>,
+
+ waits_for_settings: Option<bool>,
+ },
+
+ H3StreamTypeSet {
+ stream_id: String,
+ owner: Option<H3Owner>,
+
+ old: Option<H3StreamType>,
+ new: H3StreamType,
+ },
+
+ H3FrameCreated {
+ stream_id: String,
+ frame: Http3Frame,
+ byte_length: Option<String>,
+
+ raw: Option<String>,
+ },
+
+ H3FrameParsed {
+ stream_id: String,
+ frame: Http3Frame,
+ byte_length: Option<String>,
+
+ raw: Option<String>,
+ },
+
+ H3DataMoved {
+ stream_id: String,
+ offset: Option<String>,
+
+ from: Option<H3DataRecipient>,
+ to: Option<H3DataRecipient>,
+
+ raw: Option<String>,
+ },
+
+ H3PushResolved {
+ push_id: Option<String>,
+ stream_id: Option<String>,
+
+ decision: Option<H3PushDecision>,
+ },
+
+ // ================================================================== //
+ // QPACK
+ QpackStateUpdated {
+ owner: Option<QpackOwner>,
+
+ dynamic_table_capacity: Option<u64>,
+ dynamic_table_size: Option<u64>,
+
+ known_received_count: Option<u64>,
+ current_insert_count: Option<u64>,
+ },
+
+ QpackStreamStateUpdated {
+ stream_id: String,
+
+ state: QpackStreamState,
+ },
+
+ QpackDynamicTableUpdated {
+ update_type: QpackUpdateType,
+
+ entries: Vec<QpackDynamicTableEntry>,
+ },
+
+ QpackHeadersEncoded {
+ stream_id: Option<String>,
+
+ headers: Option<HttpHeader>,
+
+ block_prefix: QpackHeaderBlockPrefix,
+ header_block: Vec<QpackHeaderBlockRepresentation>,
+
+ raw: Option<String>,
+ },
+
+ QpackHeadersDecoded {
+ stream_id: Option<String>,
+
+ headers: Option<HttpHeader>,
+
+ block_prefix: QpackHeaderBlockPrefix,
+ header_block: Vec<QpackHeaderBlockRepresentation>,
+
+ raw: Option<String>,
+ },
+
+ QpackInstructionSent {
+ instruction: QPackInstruction,
+ byte_length: Option<String>,
+
+ raw: Option<String>,
+ },
+
+ QpackInstructionReceived {
+ instruction: QPackInstruction,
+ byte_length: Option<String>,
+
+ raw: Option<String>,
+ },
+
+ // ================================================================== //
+ // Generic
+ ConnectionError {
+ code: Option<ConnectionErrorCode>,
+ description: Option<String>,
+ },
+
+ ApplicationError {
+ code: Option<ApplicationErrorCode>,
+ description: Option<String>,
+ },
+
+ InternalError {
+ code: Option<u64>,
+ description: Option<String>,
+ },
+
+ InternalWarning {
+ code: Option<u64>,
+ description: Option<String>,
+ },
+
+ Message {
+ message: String,
+ },
+
+ Marker {
+ marker_type: String,
+ message: Option<String>,
+ },
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum PacketType {
+ Initial,
+ Handshake,
+
+ #[serde(rename = "0RTT")]
+ ZeroRtt,
+
+ #[serde(rename = "1RTT")]
+ OneRtt,
+
+ Retry,
+ VersionNegotiation,
+ Unknown,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum Http3EventType {
+ StreamStateUpdate,
+ StreamTypeUpdate,
+ FrameCreated,
+ FrameParsed,
+ DataMoved,
+ DatagramReceived,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum QpackEventType {
+ StateUpdated,
+ StreamStateUpdate,
+ DynamicTableUpdated,
+ HeadersEncoded,
+ HeadersDecoded,
+ InstructionSent,
+ InstructionReceived,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum QuicFrameTypeName {
+ Padding,
+ Ping,
+ Ack,
+ ResetStream,
+ StopSending,
+ Crypto,
+ NewToken,
+ Stream,
+ MaxData,
+ MaxStreamData,
+ MaxStreams,
+ DataBlocked,
+ StreamDataBlocked,
+ StreamsBlocked,
+ NewConnectionId,
+ RetireConnectionId,
+ PathChallenge,
+ PathResponse,
+ ConnectionClose,
+ ApplicationClose,
+ Unknown,
+}
+
+// TODO: search for pub enum Error { to see how best to encode errors in qlog.
+#[serde_with::skip_serializing_none]
+#[derive(Clone, Serialize, Deserialize)]
+pub struct PacketHeader {
+ pub packet_number: String,
+ pub packet_size: Option<u64>,
+ pub payload_length: Option<u64>,
+ pub version: Option<String>,
+ pub scil: Option<String>,
+ pub dcil: Option<String>,
+ pub scid: Option<String>,
+ pub dcid: Option<String>,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum StreamType {
+ Bidirectional,
+ Unidirectional,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum ErrorSpace {
+ TransportError,
+ ApplicationError,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum GenericEventType {
+ ConnectionError,
+ ApplicationError,
+ InternalError,
+ InternalWarning,
+
+ Message,
+ Marker,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(untagged)]
+pub enum ConnectionErrorCode {
+ TransportError(TransportError),
+ CryptoError(CryptoError),
+ Value(u64),
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(untagged)]
+pub enum ApplicationErrorCode {
+ ApplicationError(ApplicationError),
+ Value(u64),
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum TransportError {
+ NoError,
+ InternalError,
+ ServerBusy,
+ FlowControlError,
+ StreamLimitError,
+ StreamStateError,
+ FinalSizeError,
+ FrameEncodingError,
+ TransportParameterError,
+ ProtocolViolation,
+ InvalidMigration,
+ CryptoBufferExceeded,
+ Unknown,
+}
+
+// TODO
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum CryptoError {
+ Prefix,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum ApplicationError {
+ HttpNoError,
+ HttpGeneralProtocolError,
+ HttpInternalError,
+ HttpRequestCancelled,
+ HttpIncompleteRequest,
+ HttpConnectError,
+ HttpFrameError,
+ HttpExcessiveLoad,
+ HttpVersionFallback,
+ HttpIdError,
+ HttpStreamCreationError,
+ HttpClosedCriticalStream,
+ HttpEarlyResponse,
+ HttpMissingSettings,
+ HttpUnexpectedFrame,
+ HttpRequestRejection,
+ HttpSettingsError,
+ Unknown,
+}
+
+#[serde_with::skip_serializing_none]
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(untagged)]
+pub enum QuicFrame {
+ Padding {
+ frame_type: QuicFrameTypeName,
+ },
+
+ Ping {
+ frame_type: QuicFrameTypeName,
+ },
+
+ Ack {
+ frame_type: QuicFrameTypeName,
+ ack_delay: Option<String>,
+ acked_ranges: Option<Vec<(u64, u64)>>,
+
+ ect1: Option<String>,
+
+ ect0: Option<String>,
+
+ ce: Option<String>,
+ },
+
+ ResetStream {
+ frame_type: QuicFrameTypeName,
+ stream_id: String,
+ error_code: u64,
+ final_size: String,
+ },
+
+ StopSending {
+ frame_type: QuicFrameTypeName,
+ stream_id: String,
+ error_code: u64,
+ },
+
+ Crypto {
+ frame_type: QuicFrameTypeName,
+ offset: String,
+ length: String,
+ },
+
+ NewToken {
+ frame_type: QuicFrameTypeName,
+ length: String,
+ token: String,
+ },
+
+ Stream {
+ frame_type: QuicFrameTypeName,
+ stream_id: String,
+ offset: String,
+ length: String,
+ fin: bool,
+
+ raw: Option<String>,
+ },
+
+ MaxData {
+ frame_type: QuicFrameTypeName,
+ maximum: String,
+ },
+
+ MaxStreamData {
+ frame_type: QuicFrameTypeName,
+ stream_id: String,
+ maximum: String,
+ },
+
+ MaxStreams {
+ frame_type: QuicFrameTypeName,
+ stream_type: StreamType,
+ maximum: String,
+ },
+
+ DataBlocked {
+ frame_type: QuicFrameTypeName,
+ limit: String,
+ },
+
+ StreamDataBlocked {
+ frame_type: QuicFrameTypeName,
+ stream_id: String,
+ limit: String,
+ },
+
+ StreamsBlocked {
+ frame_type: QuicFrameTypeName,
+ stream_type: StreamType,
+ limit: String,
+ },
+
+ NewConnectionId {
+ frame_type: QuicFrameTypeName,
+ sequence_number: String,
+ retire_prior_to: String,
+ length: u64,
+ connection_id: String,
+ reset_token: String,
+ },
+
+ RetireConnectionId {
+ frame_type: QuicFrameTypeName,
+ sequence_number: String,
+ },
+
+ PathChallenge {
+ frame_type: QuicFrameTypeName,
+
+ data: Option<String>,
+ },
+
+ PathResponse {
+ frame_type: QuicFrameTypeName,
+
+ data: Option<String>,
+ },
+
+ ConnectionClose {
+ frame_type: QuicFrameTypeName,
+ error_space: ErrorSpace,
+ error_code: u64,
+ raw_error_code: u64,
+ reason: String,
+
+ trigger_frame_type: Option<String>,
+ },
+
+ Unknown {
+ frame_type: QuicFrameTypeName,
+ raw_frame_type: u64,
+ },
+}
+
+impl QuicFrame {
+ pub fn padding() -> Self {
+ QuicFrame::Padding {
+ frame_type: QuicFrameTypeName::Padding,
+ }
+ }
+
+ pub fn ping() -> Self {
+ QuicFrame::Ping {
+ frame_type: QuicFrameTypeName::Ping,
+ }
+ }
+
+ pub fn ack(
+ ack_delay: Option<String>, acked_ranges: Option<Vec<(u64, u64)>>,
+ ect1: Option<String>, ect0: Option<String>, ce: Option<String>,
+ ) -> Self {
+ QuicFrame::Ack {
+ frame_type: QuicFrameTypeName::Ack,
+ ack_delay,
+ acked_ranges,
+ ect1,
+ ect0,
+ ce,
+ }
+ }
+
+ pub fn reset_stream(
+ stream_id: String, error_code: u64, final_size: String,
+ ) -> Self {
+ QuicFrame::ResetStream {
+ frame_type: QuicFrameTypeName::ResetStream,
+ stream_id,
+ error_code,
+ final_size,
+ }
+ }
+
+ pub fn stop_sending(stream_id: String, error_code: u64) -> Self {
+ QuicFrame::StopSending {
+ frame_type: QuicFrameTypeName::StopSending,
+ stream_id,
+ error_code,
+ }
+ }
+
+ pub fn crypto(offset: String, length: String) -> Self {
+ QuicFrame::Crypto {
+ frame_type: QuicFrameTypeName::Crypto,
+ offset,
+ length,
+ }
+ }
+
+ pub fn new_token(length: String, token: String) -> Self {
+ QuicFrame::NewToken {
+ frame_type: QuicFrameTypeName::NewToken,
+ length,
+ token,
+ }
+ }
+
+ pub fn stream(
+ stream_id: String, offset: String, length: String, fin: bool,
+ raw: Option<String>,
+ ) -> Self {
+ QuicFrame::Stream {
+ frame_type: QuicFrameTypeName::Stream,
+ stream_id,
+ offset,
+ length,
+ fin,
+ raw,
+ }
+ }
+
+ pub fn max_data(maximum: String) -> Self {
+ QuicFrame::MaxData {
+ frame_type: QuicFrameTypeName::MaxData,
+ maximum,
+ }
+ }
+
+ pub fn max_stream_data(stream_id: String, maximum: String) -> Self {
+ QuicFrame::MaxStreamData {
+ frame_type: QuicFrameTypeName::MaxStreamData,
+ stream_id,
+ maximum,
+ }
+ }
+
+ pub fn max_streams(stream_type: StreamType, maximum: String) -> Self {
+ QuicFrame::MaxStreams {
+ frame_type: QuicFrameTypeName::MaxStreams,
+ stream_type,
+ maximum,
+ }
+ }
+
+ pub fn data_blocked(limit: String) -> Self {
+ QuicFrame::DataBlocked {
+ frame_type: QuicFrameTypeName::DataBlocked,
+ limit,
+ }
+ }
+
+ pub fn stream_data_blocked(stream_id: String, limit: String) -> Self {
+ QuicFrame::StreamDataBlocked {
+ frame_type: QuicFrameTypeName::StreamDataBlocked,
+ stream_id,
+ limit,
+ }
+ }
+
+ pub fn streams_blocked(stream_type: StreamType, limit: String) -> Self {
+ QuicFrame::StreamsBlocked {
+ frame_type: QuicFrameTypeName::StreamsBlocked,
+ stream_type,
+ limit,
+ }
+ }
+
+ pub fn new_connection_id(
+ sequence_number: String, retire_prior_to: String, length: u64,
+ connection_id: String, reset_token: String,
+ ) -> Self {
+ QuicFrame::NewConnectionId {
+ frame_type: QuicFrameTypeName::NewConnectionId,
+ sequence_number,
+ retire_prior_to,
+ length,
+ connection_id,
+ reset_token,
+ }
+ }
+
+ pub fn retire_connection_id(sequence_number: String) -> Self {
+ QuicFrame::RetireConnectionId {
+ frame_type: QuicFrameTypeName::RetireConnectionId,
+ sequence_number,
+ }
+ }
+
+ pub fn path_challenge(data: Option<String>) -> Self {
+ QuicFrame::PathChallenge {
+ frame_type: QuicFrameTypeName::PathChallenge,
+ data,
+ }
+ }
+
+ pub fn path_response(data: Option<String>) -> Self {
+ QuicFrame::PathResponse {
+ frame_type: QuicFrameTypeName::PathResponse,
+ data,
+ }
+ }
+
+ pub fn connection_close(
+ error_space: ErrorSpace, error_code: u64, raw_error_code: u64,
+ reason: String, trigger_frame_type: Option<String>,
+ ) -> Self {
+ QuicFrame::ConnectionClose {
+ frame_type: QuicFrameTypeName::ConnectionClose,
+ error_space,
+ error_code,
+ raw_error_code,
+ reason,
+ trigger_frame_type,
+ }
+ }
+
+ pub fn unknown(raw_frame_type: u64) -> Self {
+ QuicFrame::Unknown {
+ frame_type: QuicFrameTypeName::Unknown,
+ raw_frame_type,
+ }
+ }
+}
+
+// ================================================================== //
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum Http3FrameTypeName {
+ Data,
+ Headers,
+ CancelPush,
+ Settings,
+ PushPromise,
+ Goaway,
+ MaxPushId,
+ DuplicatePush,
+ Reserved,
+ Unknown,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct HttpHeader {
+ name: String,
+ value: String,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct Setting {
+ name: String,
+ value: String,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub enum Http3Frame {
+ Data {
+ frame_type: Http3FrameTypeName,
+
+ raw: Option<String>,
+ },
+
+ Headers {
+ frame_type: Http3FrameTypeName,
+ headers: Vec<HttpHeader>,
+ },
+
+ CancelPush {
+ frame_type: Http3FrameTypeName,
+ push_id: String,
+ },
+
+ Settings {
+ frame_type: Http3FrameTypeName,
+ settings: Vec<Setting>,
+ },
+
+ PushPromise {
+ frame_type: Http3FrameTypeName,
+ push_id: String,
+ headers: Vec<HttpHeader>,
+ },
+
+ Goaway {
+ frame_type: Http3FrameTypeName,
+ stream_id: String,
+ },
+
+ MaxPushId {
+ frame_type: Http3FrameTypeName,
+ push_id: String,
+ },
+
+ DuplicatePush {
+ frame_type: Http3FrameTypeName,
+ push_id: String,
+ },
+
+ Reserved {
+ frame_type: Http3FrameTypeName,
+ },
+
+ Unknown {
+ frame_type: Http3FrameTypeName,
+ },
+}
+
+impl Http3Frame {
+ pub fn data(raw: Option<String>) -> Self {
+ Http3Frame::Data {
+ frame_type: Http3FrameTypeName::Data,
+ raw,
+ }
+ }
+
+ pub fn headers(headers: Vec<HttpHeader>) -> Self {
+ Http3Frame::Headers {
+ frame_type: Http3FrameTypeName::Headers,
+ headers,
+ }
+ }
+
+ pub fn cancel_push(push_id: String) -> Self {
+ Http3Frame::CancelPush {
+ frame_type: Http3FrameTypeName::CancelPush,
+ push_id,
+ }
+ }
+
+ pub fn settings(settings: Vec<Setting>) -> Self {
+ Http3Frame::Settings {
+ frame_type: Http3FrameTypeName::Settings,
+ settings,
+ }
+ }
+
+ pub fn push_promise(push_id: String, headers: Vec<HttpHeader>) -> Self {
+ Http3Frame::PushPromise {
+ frame_type: Http3FrameTypeName::PushPromise,
+ push_id,
+ headers,
+ }
+ }
+
+ pub fn goaway(stream_id: String) -> Self {
+ Http3Frame::Goaway {
+ frame_type: Http3FrameTypeName::Goaway,
+ stream_id,
+ }
+ }
+
+ pub fn max_push_id(push_id: String) -> Self {
+ Http3Frame::MaxPushId {
+ frame_type: Http3FrameTypeName::MaxPushId,
+ push_id,
+ }
+ }
+
+ pub fn duplicate_push(push_id: String) -> Self {
+ Http3Frame::DuplicatePush {
+ frame_type: Http3FrameTypeName::DuplicatePush,
+ push_id,
+ }
+ }
+
+ pub fn reserved() -> Self {
+ Http3Frame::Reserved {
+ frame_type: Http3FrameTypeName::Reserved,
+ }
+ }
+
+ pub fn unknown() -> Self {
+ Http3Frame::Unknown {
+ frame_type: Http3FrameTypeName::Unknown,
+ }
+ }
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum QpackInstructionTypeName {
+ SetDynamicTableCapacityInstruction,
+ InsertWithNameReferenceInstruction,
+ InsertWithoutNameReferenceInstruction,
+ DuplicateInstruction,
+ HeaderAcknowledgementInstruction,
+ StreamCancellationInstruction,
+ InsertCountIncrementInstruction,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum QpackTableType {
+ Static,
+ Dynamic,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub enum QPackInstruction {
+ SetDynamicTableCapacityInstruction {
+ instruction_type: QpackInstructionTypeName,
+
+ capacity: u64,
+ },
+
+ InsertWithNameReferenceInstruction {
+ instruction_type: QpackInstructionTypeName,
+
+ table_type: QpackTableType,
+
+ name_index: u64,
+
+ huffman_encoded_value: bool,
+ value_length: u64,
+ value: String,
+ },
+
+ InsertWithoutNameReferenceInstruction {
+ instruction_type: QpackInstructionTypeName,
+
+ huffman_encoded_name: bool,
+ name_length: u64,
+ name: String,
+
+ huffman_encoded_value: bool,
+ value_length: u64,
+ value: String,
+ },
+
+ DuplicateInstruction {
+ instruction_type: QpackInstructionTypeName,
+
+ index: u64,
+ },
+
+ HeaderAcknowledgementInstruction {
+ instruction_type: QpackInstructionTypeName,
+
+ stream_id: String,
+ },
+
+ StreamCancellationInstruction {
+ instruction_type: QpackInstructionTypeName,
+
+ stream_id: String,
+ },
+
+ InsertCountIncrementInstruction {
+ instruction_type: QpackInstructionTypeName,
+
+ increment: u64,
+ },
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum QpackHeaderBlockRepresentationTypeName {
+ IndexedHeaderField,
+ LiteralHeaderFieldWithName,
+ LiteralHeaderFieldWithoutName,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub enum QpackHeaderBlockRepresentation {
+ IndexedHeaderField {
+ header_field_type: QpackHeaderBlockRepresentationTypeName,
+
+ table_type: QpackTableType,
+ index: u64,
+
+ is_post_base: Option<bool>,
+ },
+
+ LiteralHeaderFieldWithName {
+ header_field_type: QpackHeaderBlockRepresentationTypeName,
+
+ preserve_literal: bool,
+ table_type: QpackTableType,
+ name_index: u64,
+
+ huffman_encoded_value: bool,
+ value_length: u64,
+ value: String,
+
+ is_post_base: Option<bool>,
+ },
+
+ LiteralHeaderFieldWithoutName {
+ header_field_type: QpackHeaderBlockRepresentationTypeName,
+
+ preserve_literal: bool,
+ table_type: QpackTableType,
+ name_index: u64,
+
+ huffman_encoded_name: bool,
+ name_length: u64,
+ name: String,
+
+ huffman_encoded_value: bool,
+ value_length: u64,
+ value: String,
+
+ is_post_base: Option<bool>,
+ },
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "snake_case")]
+pub struct QPackHeaderBlockPrefix {
+ required_insert_count: u64,
+ sign_bit: bool,
+ delta_base: u64,
+}
+
+pub struct HexSlice<'a>(&'a [u8]);
+
+impl<'a> HexSlice<'a> {
+ pub fn new<T>(data: &'a T) -> HexSlice<'a>
+ where
+ T: ?Sized + AsRef<[u8]> + 'a,
+ {
+ HexSlice(data.as_ref())
+ }
+}
+
+impl<'a> std::fmt::Display for HexSlice<'a> {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ for byte in self.0 {
+ write!(f, "{:02x}", byte)?;
+ }
+ Ok(())
+ }
+}
+
+#[doc(hidden)]
+pub mod testing {}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn packet_header() {
+ let pkt_hdr = PacketHeader {
+ packet_number: "0".to_string(),
+ packet_size: Some(1251),
+ payload_length: Some(1224),
+ version: Some("0xff000018".to_string()),
+ scil: Some("8".to_string()),
+ dcil: Some("8".to_string()),
+ scid: Some("7e37e4dcc6682da8".to_string()),
+ dcid: Some("36ce104eee50101c".to_string()),
+ };
+
+ let log_string = r#"{
+ "packet_number": "0",
+ "packet_size": 1251,
+ "payload_length": 1224,
+ "version": "0xff000018",
+ "scil": "8",
+ "dcil": "8",
+ "scid": "7e37e4dcc6682da8",
+ "dcid": "36ce104eee50101c"
+}"#;
+
+ assert_eq!(serde_json::to_string_pretty(&pkt_hdr).unwrap(), log_string);
+ }
+
+ #[test]
+ fn packet_sent_event_no_frames() {
+ let log_string = r#"{
+ "packet_type": "initial",
+ "header": {
+ "packet_number": "0",
+ "packet_size": 1251,
+ "payload_length": 1224,
+ "version": "0xff000018",
+ "scil": "8",
+ "dcil": "8",
+ "scid": "7e37e4dcc6682da8",
+ "dcid": "36ce104eee50101c"
+ }
+}"#;
+
+ let pkt_hdr = PacketHeader {
+ packet_number: "0".to_string(),
+ packet_size: Some(1251),
+ payload_length: Some(1224),
+ version: Some("0xff000018".to_string()),
+ scil: Some("8".to_string()),
+ dcil: Some("8".to_string()),
+ scid: Some("7e37e4dcc6682da8".to_string()),
+ dcid: Some("36ce104eee50101c".to_string()),
+ };
+
+ let pkt_sent_evt = EventData::PacketSent {
+ raw_encrypted: None,
+ raw_decrypted: None,
+ packet_type: PacketType::Initial,
+ header: pkt_hdr.clone(),
+ frames: None,
+ is_coalesced: None,
+ };
+
+ assert_eq!(
+ serde_json::to_string_pretty(&pkt_sent_evt).unwrap(),
+ log_string
+ );
+ }
+
+ #[test]
+ fn packet_sent_event_some_frames() {
+ let log_string = r#"{
+ "packet_type": "initial",
+ "header": {
+ "packet_number": "0",
+ "packet_size": 1251,
+ "payload_length": 1224,
+ "version": "0xff000018",
+ "scil": "8",
+ "dcil": "8",
+ "scid": "7e37e4dcc6682da8",
+ "dcid": "36ce104eee50101c"
+ },
+ "frames": [
+ {
+ "frame_type": "padding"
+ },
+ {
+ "frame_type": "ping"
+ },
+ {
+ "frame_type": "stream",
+ "stream_id": "0",
+ "offset": "0",
+ "length": "100",
+ "fin": true
+ }
+ ]
+}"#;
+
+ let pkt_hdr = PacketHeader {
+ packet_number: "0".to_string(),
+ packet_size: Some(1251),
+ payload_length: Some(1224),
+ version: Some("0xff000018".to_string()),
+ scil: Some("8".to_string()),
+ dcil: Some("8".to_string()),
+ scid: Some("7e37e4dcc6682da8".to_string()),
+ dcid: Some("36ce104eee50101c".to_string()),
+ };
+
+ let mut frames = Vec::new();
+ frames.push(QuicFrame::padding());
+
+ frames.push(QuicFrame::ping());
+
+ frames.push(QuicFrame::stream(
+ "0".to_string(),
+ "0".to_string(),
+ "100".to_string(),
+ true,
+ None,
+ ));
+
+ let pkt_sent_evt = EventData::PacketSent {
+ raw_encrypted: None,
+ raw_decrypted: None,
+ packet_type: PacketType::Initial,
+ header: pkt_hdr.clone(),
+ frames: Some(frames),
+ is_coalesced: None,
+ };
+
+ assert_eq!(
+ serde_json::to_string_pretty(&pkt_sent_evt).unwrap(),
+ log_string
+ );
+ }
+
+ #[test]
+ fn trace_no_events() {
+ let log_string = r#"{
+ "vantage_point": {
+ "type": "server"
+ },
+ "title": "Quiche qlog trace",
+ "description": "Quiche qlog trace description",
+ "configuration": {
+ "time_units": "ms",
+ "time_offset": "0"
+ },
+ "event_fields": [
+ "relative_time",
+ "category",
+ "event",
+ "data"
+ ],
+ "events": []
+}"#;
+
+ let trace = Trace {
+ vantage_point: VantagePoint {
+ name: None,
+ ty: VantagePointType::Server,
+ flow: None,
+ },
+ title: Some("Quiche qlog trace".to_string()),
+ description: Some("Quiche qlog trace description".to_string()),
+ configuration: Some(Configuration {
+ time_offset: Some("0".to_string()),
+ time_units: Some(TimeUnits::Ms),
+ original_uris: None,
+ }),
+ common_fields: None,
+ event_fields: vec![
+ "relative_time".to_string(),
+ "category".to_string(),
+ "event".to_string(),
+ "data".to_string(),
+ ], // TODO: hack
+ events: Vec::new(), // vec![vec![rt, cat, ev, data]],
+ };
+
+ assert_eq!(serde_json::to_string_pretty(&trace).unwrap(), log_string);
+ }
+}
+
+#[test]
+fn trace_single_transport_event() {
+ let log_string = r#"{
+ "vantage_point": {
+ "type": "server"
+ },
+ "title": "Quiche qlog trace",
+ "description": "Quiche qlog trace description",
+ "configuration": {
+ "time_units": "ms",
+ "time_offset": "0"
+ },
+ "event_fields": [
+ "relative_time",
+ "category",
+ "event",
+ "data"
+ ],
+ "events": [
+ [
+ "0",
+ "transport",
+ "packet_sent",
+ {
+ "packet_type": "initial",
+ "header": {
+ "packet_number": "0",
+ "packet_size": 1251,
+ "payload_length": 1224,
+ "version": "0xff000018",
+ "scil": "8",
+ "dcil": "8",
+ "scid": "7e37e4dcc6682da8",
+ "dcid": "36ce104eee50101c"
+ },
+ "frames": [
+ {
+ "frame_type": "stream",
+ "stream_id": "0",
+ "offset": "0",
+ "length": "100",
+ "fin": true
+ }
+ ]
+ }
+ ]
+ ]
+}"#;
+
+ let mut trace = Trace {
+ vantage_point: VantagePoint {
+ name: None,
+ ty: VantagePointType::Server,
+ flow: None,
+ },
+ title: Some("Quiche qlog trace".to_string()),
+ description: Some("Quiche qlog trace description".to_string()),
+ configuration: Some(Configuration {
+ time_offset: Some("0".to_string()),
+ time_units: Some(TimeUnits::Ms),
+ original_uris: None,
+ }),
+ common_fields: None,
+ event_fields: vec![
+ "relative_time".to_string(),
+ "category".to_string(),
+ "event".to_string(),
+ "data".to_string(),
+ ], // TODO: hack
+ events: Vec::new(), // vec![vec![rt, cat, ev, data]],
+ };
+
+ trace.push_transport_event(
+ "0".to_string(),
+ TransportEventType::PacketSent,
+ EventData::PacketSent {
+ raw_encrypted: None,
+ raw_decrypted: None,
+ packet_type: PacketType::Initial,
+ header: PacketHeader {
+ packet_number: "0".to_string(),
+ packet_size: Some(1251),
+ payload_length: Some(1224),
+ version: Some("0xff000018".to_string()),
+ scil: Some("8".to_string()),
+ dcil: Some("8".to_string()),
+ scid: Some("7e37e4dcc6682da8".to_string()),
+ dcid: Some("36ce104eee50101c".to_string()),
+ },
+ frames: Some(vec![QuicFrame::stream(
+ "0".to_string(),
+ "0".to_string(),
+ "100".to_string(),
+ true,
+ None,
+ )]),
+ is_coalesced: None,
+ },
+ );
+
+ assert_eq!(serde_json::to_string_pretty(&trace).unwrap(), log_string);
+}
diff --git a/tools/quic-trace-log/.gitignore b/tools/quic-trace-log/.gitignore
deleted file mode 100644
index e8fc5e3..0000000
--- a/tools/quic-trace-log/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-src/quic_trace.rs
-/target
-**/*.rs.bk
-Cargo.lock
diff --git a/tools/quic-trace-log/Cargo.toml b/tools/quic-trace-log/Cargo.toml
deleted file mode 100644
index cf7f1b0..0000000
--- a/tools/quic-trace-log/Cargo.toml
+++ /dev/null
@@ -1,15 +0,0 @@
-[package]
-name = "quic-trace-log"
-version = "0.1.0"
-authors = ["Alessandro Ghedini <alessandro@ghedini.me>"]
-edition = "2018"
-build = "src/build.rs"
-publish = false
-
-[build-dependencies]
-protoc-rust = "2"
-
-[dependencies]
-regex = "1"
-protobuf = "2"
-humantime = "1"
diff --git a/tools/quic-trace-log/quic_trace.proto b/tools/quic-trace-log/quic_trace.proto
deleted file mode 100644
index 9924634..0000000
--- a/tools/quic-trace-log/quic_trace.proto
+++ /dev/null
@@ -1,195 +0,0 @@
-// Copyright 2018 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-syntax = "proto2";
-
-package quic_trace;
-
-enum FrameType {
- UNKNOWN_FRAME = 0;
-
- STREAM = 1;
- ACK = 2;
- RESET_STREAM = 3;
- CONNECTION_CLOSE = 4;
- MAX_DATA = 5;
- MAX_STREAM_DATA = 6;
- PING = 7;
- BLOCKED = 8;
- STREAM_BLOCKED = 9;
- PADDING = 10;
-};
-
-// Metadata for STREAM frames.
-message StreamFrameInfo {
- optional uint64 stream_id = 1;
- optional bool fin = 2;
- optional uint64 length = 3;
- optional uint64 offset = 4;
-};
-
-// The intervals are closed, i.e. the interval represented here is
-// [first_packet, last_packet].
-message AckBlock {
- optional uint64 first_packet = 1;
- optional uint64 last_packet = 2;
-};
-
-// Metadata for ACK frames.
-message AckInfo {
- repeated AckBlock acked_packets = 1;
- optional uint64 ack_delay_us = 2;
-};
-
-// Metadata for RST_STREAM frames.
-message ResetStreamInfo {
- optional uint64 stream_id = 1;
- optional uint32 application_error_code = 2;
- optional uint64 final_offset = 3;
-};
-
-// Metadata for CONNECTION_CLOSE/APPLICATION_CLOSE frames.
-message CloseInfo {
- optional uint32 error_code = 1;
- optional string reason_phrase = 2;
-};
-
-// Metadata for MAX_DATA/MAX_STREAM_DATA frames.
-message FlowControlInfo {
- optional uint64 max_data = 1;
- optional uint64 stream_id = 2;
-};
-
-// A message representing a frame, either sent or received.
-message Frame {
- optional FrameType frame_type = 1;
-
- optional StreamFrameInfo stream_frame_info = 2;
- optional AckInfo ack_info = 3;
- optional ResetStreamInfo reset_stream_info = 4;
- optional CloseInfo close_info = 5;
- optional FlowControlInfo flow_control_info = 6;
-};
-
-// Metadata that represents transport stack's understanding of the current state
-// of the transport channel.
-message TransportState {
- optional uint64 min_rtt_us = 1;
- // Smoothed RTT, usually computed using EWMA.
- optional uint64 smoothed_rtt_us = 2;
- // The latest RTT measureent available.
- optional uint64 last_rtt_us = 3;
-
- optional uint64 in_flight_bytes = 4;
- optional uint64 cwnd_bytes = 5;
- // Pacing rate, in bits per second.
- optional uint64 pacing_rate_bps = 6;
-
- // Any arbitrary information about congestion control state that is not
- // representable via parameters above.
- optional string congestion_control_state = 7;
-};
-
-// Documents external network parameters supplied to the sender. Typically not
-// all of those would be supplied (e.g. if bandwidth and RTT are supplied, you
-// can infer the suggested CWND), but there are no restrictions on which fields
-// may or may not be set.
-message ExternalNetworkParameters {
- optional uint64 bandwidth_bps = 1; // in bits per second
- optional uint64 rtt_us = 2;
- optional uint64 cwnd_bytes = 3;
-};
-
-enum EncryptionLevel {
- ENCRYPTION_UNKNOWN = 0;
-
- ENCRYPTION_INITIAL = 1;
- ENCRYPTION_0RTT = 2;
- ENCRYPTION_1RTT = 3;
- ENCRYPTION_HANDSHAKE = 4;
-};
-
-enum EventType {
- UNKNOWN_EVENT = 0;
-
- PACKET_SENT = 1;
- PACKET_RECEIVED = 2;
- PACKET_LOST = 3;
-
- // An APPLICATION_LIMITED event occurs when the sender is capable of sending
- // more data and tries to send it, but discovers that it does not have any
- // outstanding data to send. Such events are important to some congestion
- // control algorithms (for example, BBR) since they are trying to measure the
- // largest achievable throughput, but it is impossible to measure it when the
- // application does not send anything.
- APPLICATION_LIMITED = 4;
-
- // Record when external information about expected network conditions
- // (available bandwidth, RTT, congestion window, etc) is supplied to the
- // sender.
- EXTERNAL_PARAMETERS = 5;
-};
-
-enum TransmissionReason {
- // Indicates that there was not any particular special reason the packet was
- // sent.
- NORMAL_TRANSMISSION = 0;
-
- // Indicates that the packet sent is a tail loss probe, cf.
- // https://tools.ietf.org/html/draft-ietf-quic-recovery-14#section-4.3.2
- TAIL_LOSS_PROBE = 1;
-
- // Indicates that the packet is sent due to retransmission timeout, cf
- // https://tools.ietf.org/html/draft-ietf-quic-recovery-14#section-4.3.3
- RTO_TRANSMISSION = 2;
-
- // Indicates that the packet is sent in order to probe whether there is extra
- // bandwidth available in cases where the sender needs an estimate of
- // available bandwidth, but the application does not provide enough data for
- // such estimate to become naturally available. This is usually only used in
- // real-time protocols.
- PROBING_TRANSMISSION = 3;
-};
-
-// An event that has occurred over duration of the connection.
-message Event {
- optional uint64 time_us = 1;
- optional EventType event_type = 2;
-
- optional uint64 packet_number = 3;
- repeated Frame frames = 4;
- optional uint64 packet_size = 5;
- optional EncryptionLevel encryption_level = 6;
- // State of the transport stack after the event has happened.
- optional TransportState transport_state = 7;
- // For event_type = EXTERNAL_PARAMETERS, record parameters specified.
- optional ExternalNetworkParameters external_network_parameters = 8;
-
- // For sent packets, indicate if there is a special reason for why the packet
- // in question was transmitted.
- optional TransmissionReason transmission_reason = 9
- [default = NORMAL_TRANSMISSION];
-};
-
-message Trace {
- // QUIC version tag, as represented on wire. Should be always 4 bytes long.
- optional bytes protocol_version = 1;
-
- // Source and destination connection ID. If multiple connection IDs are used,
- // record the first one used with short-form header.
- optional bytes source_connection_id = 2;
- optional bytes destination_connection_id = 3;
-
- repeated Event events = 4;
-};
diff --git a/tools/quic-trace-log/src/build.rs b/tools/quic-trace-log/src/build.rs
deleted file mode 100644
index 3adf4ac..0000000
--- a/tools/quic-trace-log/src/build.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-use protoc_rust::Customize;
-
-fn main() {
- protoc_rust::run(protoc_rust::Args {
- out_dir: "src/",
- input: &["quic_trace.proto"],
- includes: &["."],
- customize: Customize {
- ..Default::default()
- },
- })
- .expect("protoc");
-}
diff --git a/tools/quic-trace-log/src/main.rs b/tools/quic-trace-log/src/main.rs
deleted file mode 100644
index e659027..0000000
--- a/tools/quic-trace-log/src/main.rs
+++ /dev/null
@@ -1,306 +0,0 @@
-// Copyright (C) 2018, Cloudflare, Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// * Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
-// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-use std::io::BufRead;
-
-use regex::Regex;
-
-use protobuf::Message;
-
-fn main() {
- let mut args = std::env::args();
- args.next();
-
- let log_file = args.next().unwrap();
- let trace_file = args.next().unwrap();
-
- let inp = std::fs::File::open(log_file).unwrap();
- let mut out = std::fs::File::create(trace_file).unwrap();
-
- let mut trace = quic_trace::Trace::new();
-
- trace.set_protocol_version(b"AAAA".to_vec());
-
- // Log prefix.
- let prefix_re = Regex::new(r"^\[(.*) TRACE quiche.*\]").unwrap();
-
- // Packet events.
- let pkt_re = Regex::new(
- r"(rx|tx) pkt (Initial|Handshake|Application) .* len=(\d+) pn=(\d+)",
- )
- .unwrap();
- let lost_re = Regex::new(r"packet (\d+) lost on epoch (\d)").unwrap();
-
- let rec_re =
- Regex::new(r"timer=(.*) crypto=(.*) inflight=(.*) cwnd=(.*) latest_rtt=(.*) srtt=(.*) min_rtt=(.*) rttvar=(.*) probes=(\d+)").unwrap();
-
- // Frame events.
- let stream_frm_re = Regex::new(
- r"(rx|tx) frm STREAM id=(\d+) off=(\d+) len=(\d+) fin=(true|false)",
- )
- .unwrap();
- let ack_frm_re =
- Regex::new(r"(rx|tx) frm ACK delay=(.*) blocks=\[(.*)\]").unwrap();
- let close_frm_re = Regex::new(
- r"(rx|tx) frm (APPLICATION|CONNECTION)_CLOSE err=(\d+) reason=",
- )
- .unwrap();
-
- let mut start_time = None;
-
- let mut events = Vec::new();
-
- let mut event: Option<quic_trace::Event> = None;
-
- let file = std::io::BufReader::new(&inp);
- for (_, line) in file.lines().enumerate() {
- let l = line.unwrap();
-
- let time = match prefix_re.captures(&l) {
- Some(caps) => {
- let s = caps.get(1).unwrap().as_str();
- humantime::parse_rfc3339(s).unwrap()
- },
-
- None => continue,
- };
-
- if start_time.is_none() {
- start_time = Some(time);
- }
-
- if let Some(caps) = pkt_re.captures(&l) {
- // Flush previous event.
- if let Some(event) = event {
- events.push(event);
- }
-
- let mut ev = quic_trace::Event::new();
-
- let time_us = time.duration_since(start_time.unwrap()).unwrap();
- ev.set_time_us(time_us.as_micros() as u64);
-
- let ty = match caps.get(1).unwrap().as_str() {
- "rx" => quic_trace::EventType::PACKET_RECEIVED,
- "tx" => quic_trace::EventType::PACKET_SENT,
- _ => unreachable!(),
- };
- ev.set_event_type(ty);
-
- let ty = caps.get(2).unwrap().as_str();
- ev.set_encryption_level(str_to_enc_level(ty));
-
- let len = caps.get(3).unwrap().as_str();
- ev.set_packet_size(len.parse::<u64>().unwrap());
-
- let pn = caps.get(4).unwrap().as_str();
- ev.set_packet_number(pn.parse::<u64>().unwrap());
-
- event = Some(ev);
- continue;
- }
-
- if let Some(caps) = lost_re.captures(&l) {
- let mut ev = quic_trace::Event::new();
-
- let time_us = time.duration_since(start_time.unwrap()).unwrap();
- ev.set_time_us(time_us.as_micros() as u64);
-
- ev.set_event_type(quic_trace::EventType::PACKET_LOST);
-
- let pn = caps.get(1).unwrap().as_str();
- ev.set_packet_number(pn.parse::<u64>().unwrap());
-
- let ty = caps.get(2).unwrap().as_str().parse::<u64>().unwrap();
- ev.set_encryption_level(int_to_enc_level(ty));
-
- events.push(ev);
- continue;
- }
-
- if let Some(caps) = rec_re.captures(&l) {
- if event.is_none() {
- unreachable!();
- }
-
- let mut state = quic_trace::TransportState::new();
-
- let inflight = caps.get(3).unwrap().as_str();
- state.set_in_flight_bytes(inflight.parse::<u64>().unwrap());
-
- let cwnd = caps.get(4).unwrap().as_str();
- state.set_cwnd_bytes(cwnd.parse::<u64>().unwrap());
-
- let latest_rtt = caps.get(5).unwrap().as_str();
- let latest_rtt = str_to_duration(latest_rtt);
- state.set_last_rtt_us(latest_rtt.as_micros() as u64);
-
- let srtt = caps.get(6).unwrap().as_str();
- let srtt = if srtt == "None" {
- std::time::Duration::from_micros(0)
- } else {
- let srtt = &srtt[5..srtt.len() - 1];
- str_to_duration(srtt)
- };
- state.set_smoothed_rtt_us(srtt.as_micros() as u64);
-
- let min_rtt = caps.get(7).unwrap().as_str();
- let min_rtt = str_to_duration(min_rtt);
- state.set_smoothed_rtt_us(min_rtt.as_micros() as u64);
-
- event.as_mut().unwrap().set_transport_state(state);
- continue;
- }
-
- if let Some(caps) = stream_frm_re.captures(&l) {
- let mut frame = quic_trace::Frame::new();
- frame.set_frame_type(quic_trace::FrameType::STREAM);
-
- let mut info = quic_trace::StreamFrameInfo::new();
-
- let id = caps.get(2).unwrap().as_str();
- info.set_stream_id(id.parse::<u64>().unwrap());
-
- let off = caps.get(3).unwrap().as_str();
- info.set_offset(off.parse::<u64>().unwrap());
-
- let len = caps.get(4).unwrap().as_str();
- info.set_length(len.parse::<u64>().unwrap());
-
- let fin = caps.get(5).unwrap().as_str();
- match fin {
- "true" => info.set_fin(true),
- "false" => info.set_fin(false),
- _ => unreachable!(),
- }
-
- frame.set_stream_frame_info(info);
-
- event.as_mut().unwrap().mut_frames().push(frame);
- continue;
- }
-
- if let Some(caps) = ack_frm_re.captures(&l) {
- let mut frame = quic_trace::Frame::new();
- frame.set_frame_type(quic_trace::FrameType::ACK);
-
- let mut info = quic_trace::AckInfo::new();
-
- let delay = caps.get(2).unwrap().as_str();
- let delay = delay.parse::<u64>().unwrap() * 2_u64.pow(3_u32);
- info.set_ack_delay_us(delay);
-
- let mut blocks = Vec::new();
-
- let ranges = caps.get(3).unwrap().as_str();
- for r in ranges.split(", ") {
- let mut block = quic_trace::AckBlock::new();
-
- let mut parts = r.split("..");
- block.set_first_packet(
- parts.next().unwrap().parse::<u64>().unwrap(),
- );
- block.set_last_packet(
- parts.next().unwrap().parse::<u64>().unwrap(),
- );
-
- blocks.push(block);
- }
-
- info.set_acked_packets(protobuf::RepeatedField::from_vec(blocks));
-
- frame.set_ack_info(info);
-
- event.as_mut().unwrap().mut_frames().push(frame);
- continue;
- }
-
- if let Some(caps) = close_frm_re.captures(&l) {
- let mut frame = quic_trace::Frame::new();
- frame.set_frame_type(quic_trace::FrameType::CONNECTION_CLOSE);
-
- let mut info = quic_trace::CloseInfo::new();
-
- let err = caps.get(3).unwrap().as_str();
- info.set_error_code(u32::from_str_radix(err, 16).unwrap());
-
- frame.set_close_info(info);
-
- event.as_mut().unwrap().mut_frames().push(frame);
- continue;
- }
- }
-
- println!("Generated {} events", events.len());
-
- trace.set_events(protobuf::RepeatedField::from_vec(events));
-
- let mut cos = protobuf::CodedOutputStream::new(&mut out);
- trace.write_to(&mut cos).unwrap();
- cos.flush().unwrap();
-}
-
-fn str_to_enc_level(ty: &str) -> quic_trace::EncryptionLevel {
- match ty {
- "Initial" => quic_trace::EncryptionLevel::ENCRYPTION_INITIAL,
- "Handshake" => quic_trace::EncryptionLevel::ENCRYPTION_HANDSHAKE,
- "Application" => quic_trace::EncryptionLevel::ENCRYPTION_1RTT,
- _ => unreachable!(),
- }
-}
-
-fn int_to_enc_level(ty: u64) -> quic_trace::EncryptionLevel {
- match ty {
- 0 => quic_trace::EncryptionLevel::ENCRYPTION_INITIAL,
- 1 => quic_trace::EncryptionLevel::ENCRYPTION_HANDSHAKE,
- 2 => quic_trace::EncryptionLevel::ENCRYPTION_1RTT,
- _ => unreachable!(),
- }
-}
-
-fn str_to_duration(d: &str) -> std::time::Duration {
- if let Ok(d) = humantime::parse_duration(d) {
- return d;
- }
-
- // humantime doesn't support parsing float duration, so do it manually.
- let end = d.chars().position(|c| !c.is_numeric() && c != '.').unwrap();
- let num = (&d[..end]).parse::<f64>().unwrap();
- let unit = &d[end..];
-
- let num = match unit {
- "s" => num,
- "ms" => num / 1_000_f64,
- "us" => num / 1_000_000_f64,
- "µs" => num / 1_000_000_f64,
- "ns" => num / 1_000_000_000_f64,
- _ => unreachable!(),
- };
-
- std::time::Duration::from_secs_f64(num)
-}
-
-mod quic_trace;
diff --git a/tools/setup_android.sh b/tools/setup_android.sh
index c8eda8f..8ca4868 100755
--- a/tools/setup_android.sh
+++ b/tools/setup_android.sh
@@ -7,6 +7,10 @@
#
set -eu
+# Change this value if you need a different API level
+# 21 is the minimum API tested
+API_LEVEL=21
+
if [ ! -d "${ANDROID_NDK_HOME-}" ]; then
ANDROID_NDK_HOME=/usr/local/share/android-ndk
fi
@@ -31,9 +35,9 @@
echo "> Toolchain Directory: ${TOOLCHAIN_DIR}"
mkdir -p ${TOOLCHAIN_DIR}/arch
-make_standalone_toolchain arm64 21
-make_standalone_toolchain arm 21
-make_standalone_toolchain x86 21
+make_standalone_toolchain arm64 $API_LEVEL
+make_standalone_toolchain arm $API_LEVEL
+make_standalone_toolchain x86 $API_LEVEL
CARGO_CONFIG=cargo-config.toml
sed 's@$TOOLCHAIN_DIR@'"${TOOLCHAIN_DIR}"'@g' > $CARGO_CONFIG <<CARGO_CONFIG_EOF