summaryrefslogtreecommitdiffstats
path: root/third_party/rust/neqo-http3/src/settings.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/neqo-http3/src/settings.rs
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/neqo-http3/src/settings.rs')
-rw-r--r--third_party/rust/neqo-http3/src/settings.rs185
1 files changed, 185 insertions, 0 deletions
diff --git a/third_party/rust/neqo-http3/src/settings.rs b/third_party/rust/neqo-http3/src/settings.rs
new file mode 100644
index 0000000000..9eee0fe4b4
--- /dev/null
+++ b/third_party/rust/neqo-http3/src/settings.rs
@@ -0,0 +1,185 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(clippy::module_name_repetitions)]
+
+use crate::{Error, Res};
+use neqo_common::{Decoder, Encoder};
+use neqo_crypto::{ZeroRttCheckResult, ZeroRttChecker};
+use neqo_qpack::QpackSettings;
+use std::ops::Deref;
+
+type SettingsType = u64;
+
+/// Increment this version number if a new setting is added and that might
+/// cause 0-RTT to be accepted where shouldn't be.
+const SETTINGS_ZERO_RTT_VERSION: u64 = 1;
+
+const SETTINGS_MAX_HEADER_LIST_SIZE: SettingsType = 0x6;
+const SETTINGS_QPACK_MAX_TABLE_CAPACITY: SettingsType = 0x1;
+const SETTINGS_QPACK_BLOCKED_STREAMS: SettingsType = 0x7;
+
+pub const H3_RESERVED_SETTINGS: &[SettingsType] = &[0x2, 0x3, 0x4, 0x5];
+
+#[derive(Clone, PartialEq, Debug, Copy)]
+pub enum HSettingType {
+ MaxHeaderListSize,
+ MaxTableCapacity,
+ BlockedStreams,
+}
+
+fn hsetting_default(setting_type: HSettingType) -> u64 {
+ match setting_type {
+ HSettingType::MaxHeaderListSize => 1 << 62,
+ HSettingType::MaxTableCapacity | HSettingType::BlockedStreams => 0,
+ }
+}
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct HSetting {
+ pub setting_type: HSettingType,
+ pub value: u64,
+}
+
+impl HSetting {
+ pub fn new(setting_type: HSettingType, value: u64) -> Self {
+ Self {
+ setting_type,
+ value,
+ }
+ }
+}
+
+#[derive(Clone, Debug, Default, PartialEq)]
+pub struct HSettings {
+ settings: Vec<HSetting>,
+}
+
+impl HSettings {
+ pub fn new(settings: &[HSetting]) -> Self {
+ Self {
+ settings: settings.to_vec(),
+ }
+ }
+
+ pub fn get(&self, setting: HSettingType) -> u64 {
+ match self.settings.iter().find(|s| s.setting_type == setting) {
+ Some(v) => v.value,
+ None => hsetting_default(setting),
+ }
+ }
+
+ pub fn encode_frame_contents(&self, enc: &mut Encoder) {
+ enc.encode_vvec_with(|enc_inner| {
+ for iter in &self.settings {
+ match iter.setting_type {
+ HSettingType::MaxHeaderListSize => {
+ enc_inner.encode_varint(SETTINGS_MAX_HEADER_LIST_SIZE as u64);
+ enc_inner.encode_varint(iter.value);
+ }
+ HSettingType::MaxTableCapacity => {
+ enc_inner.encode_varint(SETTINGS_QPACK_MAX_TABLE_CAPACITY as u64);
+ enc_inner.encode_varint(iter.value);
+ }
+ HSettingType::BlockedStreams => {
+ enc_inner.encode_varint(SETTINGS_QPACK_BLOCKED_STREAMS as u64);
+ enc_inner.encode_varint(iter.value);
+ }
+ }
+ }
+ });
+ }
+
+ pub fn decode_frame_contents(&mut self, dec: &mut Decoder) -> Res<()> {
+ while dec.remaining() > 0 {
+ let t = dec.decode_varint();
+ let v = dec.decode_varint();
+
+ if let Some(settings_type) = t {
+ if H3_RESERVED_SETTINGS.contains(&settings_type) {
+ return Err(Error::HttpSettings);
+ }
+ }
+ match (t, v) {
+ (Some(SETTINGS_MAX_HEADER_LIST_SIZE), Some(value)) => self
+ .settings
+ .push(HSetting::new(HSettingType::MaxHeaderListSize, value)),
+ (Some(SETTINGS_QPACK_MAX_TABLE_CAPACITY), Some(value)) => self
+ .settings
+ .push(HSetting::new(HSettingType::MaxTableCapacity, value)),
+ (Some(SETTINGS_QPACK_BLOCKED_STREAMS), Some(value)) => self
+ .settings
+ .push(HSetting::new(HSettingType::BlockedStreams, value)),
+ // other supported settings here
+ (Some(_), Some(_)) => {} // ignore unknown setting, it is fine.
+ _ => return Err(Error::NotEnoughData),
+ };
+ }
+ Ok(())
+ }
+}
+
+impl Deref for HSettings {
+ type Target = [HSetting];
+ fn deref(&self) -> &Self::Target {
+ &self.settings
+ }
+}
+
+#[derive(Debug)]
+pub struct HttpZeroRttChecker {
+ settings: QpackSettings,
+}
+
+impl HttpZeroRttChecker {
+ /// Right now we only have QPACK settings, so that is all this takes.
+ pub fn new(settings: QpackSettings) -> Self {
+ Self { settings }
+ }
+
+ /// Save the settings that matter for 0-RTT.
+ pub fn save(settings: QpackSettings) -> Vec<u8> {
+ let mut enc = Encoder::new();
+ enc.encode_varint(SETTINGS_ZERO_RTT_VERSION)
+ .encode_varint(SETTINGS_QPACK_MAX_TABLE_CAPACITY)
+ .encode_varint(settings.max_table_size_decoder)
+ .encode_varint(SETTINGS_QPACK_BLOCKED_STREAMS)
+ .encode_varint(settings.max_blocked_streams);
+ enc.into()
+ }
+}
+
+impl ZeroRttChecker for HttpZeroRttChecker {
+ fn check(&self, token: &[u8]) -> ZeroRttCheckResult {
+ let mut dec = Decoder::from(token);
+
+ // Read and check the version.
+ if let Some(version) = dec.decode_varint() {
+ if version != SETTINGS_ZERO_RTT_VERSION {
+ return ZeroRttCheckResult::Reject;
+ }
+ } else {
+ return ZeroRttCheckResult::Fail;
+ }
+
+ // Now treat the rest as a settings frame.
+ let mut settings = HSettings::new(&[]);
+ if settings.decode_frame_contents(&mut dec).is_err() {
+ return ZeroRttCheckResult::Fail;
+ }
+ if settings.iter().all(|setting| match setting.setting_type {
+ HSettingType::BlockedStreams => {
+ u64::from(self.settings.max_blocked_streams) >= setting.value
+ }
+ HSettingType::MaxTableCapacity => self.settings.max_table_size_decoder >= setting.value,
+ HSettingType::MaxHeaderListSize => true,
+ }) {
+ ZeroRttCheckResult::Accept
+ } else {
+ ZeroRttCheckResult::Reject
+ }
+ }
+}