From 98963e677d053d6bf336b8d5054c0bbb0f0d5757 Mon Sep 17 00:00:00 2001 From: Lucas Pardue Date: Wed, 29 May 2024 14:53:51 +0100 Subject: [PATCH] lib: Add TryFrom for WireErrorCode This is a fallible conversion, since a peer could send an error code value that is unknown to quiche itself. --- quiche/src/h3/mod.rs | 56 ++++++++++++++++++++++++++++++++++++++- quiche/src/lib.rs | 63 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 1 deletion(-) diff --git a/quiche/src/h3/mod.rs b/quiche/src/h3/mod.rs index 4da9d1f561..660f024704 100644 --- a/quiche/src/h3/mod.rs +++ b/quiche/src/h3/mod.rs @@ -285,7 +285,6 @@ use std::collections::HashSet; use std::collections::VecDeque; -#[cfg(feature = "sfv")] use std::convert::TryFrom; use std::fmt; use std::fmt::Write; @@ -476,6 +475,48 @@ pub enum WireErrorCode { VersionFallback = 0x110, } +/// Errors for conversions related to [WireErrorCode]. +#[derive(Debug, Eq, PartialEq)] +pub enum FromWireConversionError { + /// The value was larger than the maximum encodable length. + TooBig, + /// The value was unknown by quiche, possibly a private extension or grease. + Unknown, +} + +impl TryFrom for WireErrorCode { + type Error = FromWireConversionError; + + fn try_from(value: u64) -> std::result::Result { + if value >= 1 << 62 { + return Err(FromWireConversionError::TooBig); + } + + let res = match value { + 0x100 => WireErrorCode::NoError, + 0x101 => WireErrorCode::GeneralProtocolError, + 0x102 => WireErrorCode::InternalError, + 0x103 => WireErrorCode::StreamCreationError, + 0x104 => WireErrorCode::ClosedCriticalStream, + 0x105 => WireErrorCode::FrameUnexpected, + 0x106 => WireErrorCode::FrameError, + 0x107 => WireErrorCode::ExcessiveLoad, + 0x108 => WireErrorCode::IdError, + 0x109 => WireErrorCode::SettingsError, + 0x10a => WireErrorCode::MissingSettings, + 0x10b => WireErrorCode::RequestRejected, + 0x10c => WireErrorCode::RequestCancelled, + 0x10d => WireErrorCode::RequestIncomplete, + 0x10e => WireErrorCode::MessageError, + 0x10f => WireErrorCode::ConnectError, + 0x110 => WireErrorCode::VersionFallback, + _ => return Err(FromWireConversionError::Unknown), + }; + + Ok(res) + } +} + impl Error { fn to_wire(self) -> u64 { match self { @@ -6482,6 +6523,19 @@ mod tests { assert_eq!(s.poll_client(), Ok((stream, Event::Finished))); assert_eq!(s.poll_client(), Err(Error::Done)); } + + #[test] + fn wire_error_convert() { + assert_eq!(WireErrorCode::try_from(0x100), Ok(WireErrorCode::NoError)); + assert_eq!( + WireErrorCode::try_from(u64::MAX), + Err(FromWireConversionError::TooBig) + ); + assert_eq!( + WireErrorCode::try_from(0), + Err(FromWireConversionError::Unknown) + ); + } } #[cfg(feature = "ffi")] diff --git a/quiche/src/lib.rs b/quiche/src/lib.rs index 1187ecc01f..c0be403787 100644 --- a/quiche/src/lib.rs +++ b/quiche/src/lib.rs @@ -405,6 +405,7 @@ use qlog::events::RawInfo; use stream::StreamPriorityKey; use std::cmp; +use std::convert::TryFrom; use std::convert::TryInto; use std::time; @@ -643,6 +644,51 @@ pub enum WireErrorCode { NoViablePath = 0x10, } +/// Errors for conversions related to [WireErrorCode]. +#[derive(Debug, Eq, PartialEq)] +pub enum FromWireConversionError { + /// The value was larger than the maximum encodable length. + TooBig, + /// The value was in the crypto error range. + CryptoRange, + /// The value was unknown by quiche, possibly a private extension or grease. + Unknown, +} + +impl TryFrom for WireErrorCode { + type Error = FromWireConversionError; + + fn try_from(value: u64) -> std::result::Result { + if value >= 1 << 62 { + return Err(FromWireConversionError::TooBig); + } + + let res = match value { + 0x0 => WireErrorCode::NoError, + 0x1 => WireErrorCode::InternalError, + 0x2 => WireErrorCode::ConnectionRefused, + 0x3 => WireErrorCode::FlowControlError, + 0x4 => WireErrorCode::StreamLimitError, + 0x5 => WireErrorCode::StreamStateError, + 0x6 => WireErrorCode::FinalSizeError, + 0x7 => WireErrorCode::FrameEncodingError, + 0x8 => WireErrorCode::TransportParameterError, + 0x9 => WireErrorCode::ConnectionIdLimitError, + 0xa => WireErrorCode::ProtocolViolation, + 0xb => WireErrorCode::InvalidToken, + 0xc => WireErrorCode::ApplicationError, + 0xd => WireErrorCode::CryptoBufferExceeded, + 0xe => WireErrorCode::KeyUpdateError, + 0xf => WireErrorCode::AeadLimitReached, + 0x10 => WireErrorCode::NoViablePath, + 0x100..=0x1ff => return Err(FromWireConversionError::CryptoRange), + _ => return Err(FromWireConversionError::Unknown), + }; + + Ok(res) + } +} + impl Error { fn to_wire(self) -> u64 { match self { @@ -17334,6 +17380,23 @@ mod tests { // Continue searching for PMTU assert!(pmtu_param.get_probe_status()); } + + #[test] + fn wire_error_convert() { + assert_eq!(WireErrorCode::try_from(0), Ok(WireErrorCode::NoError)); + assert_eq!( + WireErrorCode::try_from(u64::MAX), + Err(FromWireConversionError::TooBig) + ); + assert_eq!( + WireErrorCode::try_from(0x100), + Err(FromWireConversionError::CryptoRange) + ); + assert_eq!( + WireErrorCode::try_from(0x200), + Err(FromWireConversionError::Unknown) + ); + } } pub use crate::packet::ConnectionId;