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