diff options
author | Mike Yu <yumike@google.com> | 2023-02-01 06:14:18 +0000 |
---|---|---|
committer | Cherrypicker Worker <android-build-cherrypicker-worker@google.com> | 2023-02-03 11:06:51 +0000 |
commit | 4f353252ff715cef7be77a7f16fa83f8d54e2c74 (patch) | |
tree | 7bc83a1cc9c25172a7f1abc8a43f2a294afbfaaa | |
parent | f57cb85c46168700fd06cd83f90c2e9e6cc4df2a (diff) | |
download | quiche-4f353252ff715cef7be77a7f16fa83f8d54e2c74.tar.gz |
Initial stateless reset detection
Backport the stateless reset patch to AOSP (current
version of quiche is 0.14.0) so that we don't need to
wait until the patch is released to a quiche version
newer than 0.16.0 (currently, the latest version is 0.16.0).
This patch is slightly modified because the type of
stateless_reset_token is different (In 0.14.0 the
type is Option<Vec<u8>>; in 0.16.0 the type is
Option<u128>).
Source patch:
https://github.com/cloudflare/quiche/commit/c6357db0b5311010e266637eda2f645b7fa91df4.
Bug: 242832641
Bug: 245074765
Bug: 258767218
Test: cd packages/modules/DnsResolver && atest
Test: cd external/rust/crates/quiche && atest
Test: 1. Applied ag/20124672 to DnsResolver
2. Ran dnschk every 5 minutes for 1 hour
3. Checked the log:
a. Confirmed that some stateless reset packets were received
b. Confirmed that DNS queries fallback'ed to DoT immediately
after DnsResolver received stateless reset packets
Change-Id: Ife933f54ac6ec1098a9046673ca200c6b4e2ebbf
(cherry picked from commit e1d5b663756184f5737c886a01cfa519ef256508)
Merged-In: Ife933f54ac6ec1098a9046673ca200c6b4e2ebbf
-rw-r--r-- | patches/Initial-stateless-reset-detection.patch | 91 | ||||
-rw-r--r-- | src/lib.rs | 36 |
2 files changed, 126 insertions, 1 deletions
diff --git a/patches/Initial-stateless-reset-detection.patch b/patches/Initial-stateless-reset-detection.patch new file mode 100644 index 0000000..24461a7 --- /dev/null +++ b/patches/Initial-stateless-reset-detection.patch @@ -0,0 +1,91 @@ +From 2c1ce8948b8fe1a11ea57ff8d1bcee07d038f49e Mon Sep 17 00:00:00 2001 +From: Mike Yu <yumike@google.com> +Date: Wed, 1 Feb 2023 06:14:18 +0000 +Subject: [PATCH] Initial stateless reset detection + +Backport the stateless reset patch to AOSP (current +version of quiche is 0.14.0) so that we don't need to +wait until the patch is released to a quiche version +newer than 0.16.0 (currently, the latest version is 0.16.0). + +This patch is slightly modified because the type of +stateless_reset_token is different (In 0.14.0 the +type is Option<Vec<u8>>; in 0.16.0 the type is +Option<u128>). + +Source patch: +https://github.com/cloudflare/quiche/commit/c6357db0b5311010e266637eda2f645b7fa91df4. + +Bug: 242832641 +Bug: 245074765 +Bug: 258767218 +Test: cd packages/modules/DnsResolver && atest +Test: cd external/rust/crates/quiche && atest +Test: 1. Applied ag/20124672 to DnsResolver + 2. Ran dnschk every 5 minutes for 1 hour + 3. Checked the log: + a. Confirmed that some stateless reset packets were received + b. Confirmed that DNS queries fallback'ed to DoT immediately + after DnsResolver received stateless reset packets +Change-Id: Ife933f54ac6ec1098a9046673ca200c6b4e2ebbf +--- + src/lib.rs | 36 +++++++++++++++++++++++++++++++++++- + 1 file changed, 35 insertions(+), 1 deletion(-) + +diff --git a/src/lib.rs b/src/lib.rs +index 2e13278..d590979 100644 +--- a/src/lib.rs ++++ b/src/lib.rs +@@ -1864,7 +1864,17 @@ impl Connection { + let read = match self.recv_single(&mut buf[len - left..len], &info) { + Ok(v) => v, + +- Err(Error::Done) => left, ++ Err(Error::Done) => { ++ // If the packet can't be processed or decrypted, check if ++ // it's a stateless reset. ++ if self.is_stateless_reset(&buf[len - left..len]) { ++ trace!("{} packet is a stateless reset", self.trace_id); ++ ++ self.closed = true; ++ } ++ ++ left ++ }, + + Err(e) => { + // In case of error processing the incoming packet, close +@@ -1900,6 +1910,30 @@ impl Connection { + Ok(done) + } + ++ /// Returns true if a QUIC packet is a stateless reset. ++ fn is_stateless_reset(&self, buf: &[u8]) -> bool { ++ // If the packet is too small, then we just throw it away. ++ let buf_len = buf.len(); ++ if buf_len < 21 { ++ return false; ++ } ++ ++ // TODO: we should iterate over all active destination connection IDs ++ // and check against their reset token. ++ match &self.peer_transport_params.stateless_reset_token { ++ Some(token) => { ++ let token_len = 16; ++ ring::constant_time::verify_slices_are_equal( ++ &token, ++ &buf[buf_len - token_len..buf_len], ++ ) ++ .is_ok() ++ }, ++ ++ None => false, ++ } ++ } ++ + /// Processes a single QUIC packet received from the peer. + /// + /// On success the number of bytes processed from the input buffer is +-- +2.39.1.456.gfc5497dd1b-goog + @@ -1864,7 +1864,17 @@ impl Connection { let read = match self.recv_single(&mut buf[len - left..len], &info) { Ok(v) => v, - Err(Error::Done) => left, + Err(Error::Done) => { + // If the packet can't be processed or decrypted, check if + // it's a stateless reset. + if self.is_stateless_reset(&buf[len - left..len]) { + trace!("{} packet is a stateless reset", self.trace_id); + + self.closed = true; + } + + left + }, Err(e) => { // In case of error processing the incoming packet, close @@ -1900,6 +1910,30 @@ impl Connection { Ok(done) } + /// Returns true if a QUIC packet is a stateless reset. + fn is_stateless_reset(&self, buf: &[u8]) -> bool { + // If the packet is too small, then we just throw it away. + let buf_len = buf.len(); + if buf_len < 21 { + return false; + } + + // TODO: we should iterate over all active destination connection IDs + // and check against their reset token. + match &self.peer_transport_params.stateless_reset_token { + Some(token) => { + let token_len = 16; + ring::constant_time::verify_slices_are_equal( + &token, + &buf[buf_len - token_len..buf_len], + ) + .is_ok() + }, + + None => false, + } + } + /// Processes a single QUIC packet received from the peer. /// /// On success the number of bytes processed from the input buffer is |