| use crate::{err::Layer, LenSource}; |
| |
| /// Error when different lengths are conflicting with each other (e.g. not |
| /// enough data in a slice to decode a header). |
| /// |
| /// This error is triggered whenever there is not enough data to decode |
| /// an element (e.g. if a slice is too small to decode an header) or |
| /// if a length that is inhered from an upper layer is too big for the |
| /// lower layer (e.g. length inherited from an IP header is too big to |
| /// be used as an ICMP packet length). |
| /// |
| /// When the error is caused by not enough data being available |
| /// `required_len > len` must be true. While when the length from |
| /// the upper layer is too big for the lower layer the inverse |
| /// (`required_len < len`) must be true. |
| /// |
| /// # Examples: |
| /// |
| /// An example for an error that could be returned when there is not enough |
| /// data available to decode an UDP header would be: |
| /// |
| /// ``` |
| /// use etherparse::*; |
| /// |
| /// err::LenError{ |
| /// // Expected to have at least the length of an UDP header present: |
| /// required_len: UdpHeader::LEN, |
| /// // Could not decode the UDP header: |
| /// layer: err::Layer::UdpHeader, |
| /// // There was only 1 byte left (not enough for an UDP header): |
| /// len: 1, |
| /// // The provided length was determined by the total length field in the |
| /// // IPv4 header: |
| /// len_source: LenSource::Ipv4HeaderTotalLen, |
| /// // Offset in bytes from the start of decoding (ethernet in this) case |
| /// // to the expected UDP header start: |
| /// layer_start_offset: Ethernet2Header::LEN + Ipv4Header::MIN_LEN |
| /// }; |
| /// ``` |
| #[derive(Clone, Debug, Eq, PartialEq, Hash)] |
| pub struct LenError { |
| /// Expected minimum or maximum length conflicting with the |
| /// `len` value. |
| pub required_len: usize, |
| |
| /// Length limiting or exceeding the required length. |
| pub len: usize, |
| |
| /// Source of the outer length (e.g. Slice or a length specified by |
| /// an upper level protocol). |
| pub len_source: LenSource, |
| |
| /// Layer in which the length error was encountered. |
| pub layer: Layer, |
| |
| /// Offset from the start of the parsed data to the layer where the |
| /// length error occurred. |
| pub layer_start_offset: usize, |
| } |
| |
| impl LenError { |
| /// Adds an offset value to the `layer_start_offset` field. |
| #[inline] |
| pub const fn add_offset(self, offset: usize) -> Self { |
| LenError { |
| required_len: self.required_len, |
| layer: self.layer, |
| len: self.len, |
| len_source: self.len_source, |
| layer_start_offset: self.layer_start_offset + offset, |
| } |
| } |
| } |
| |
| impl core::fmt::Display for LenError { |
| fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
| let len_source: &'static str = { |
| use LenSource::*; |
| match self.len_source { |
| Slice => "slice length", |
| Ipv4HeaderTotalLen => "length calculated from the IPv4 header 'total length' field", |
| Ipv6HeaderPayloadLen => { |
| "length calculated from the IPv6 header 'payload length' field" |
| } |
| UdpHeaderLen => "length calculated from the UDP header 'length' field", |
| TcpHeaderLen => "length calculated from the TCP header 'length' field", |
| } |
| }; |
| |
| if self.required_len > self.len { |
| if self.layer_start_offset > 0 { |
| write!( |
| f, |
| "{}: Not enough data to decode '{}'. {} byte(s) would be required, but only {} byte(s) are available based on the {} ('{}' starts at overall parsed byte {}).", |
| self.layer.error_title(), |
| self.layer, |
| self.required_len, |
| self.len, |
| len_source, |
| self.layer, |
| self.layer_start_offset |
| ) |
| } else { |
| write!( |
| f, |
| "{}: Not enough data to decode '{}'. {} byte(s) would be required, but only {} byte(s) are available based on the {}.", |
| self.layer.error_title(), |
| self.layer, |
| self.required_len, |
| self.len, |
| len_source |
| ) |
| } |
| } else if self.layer_start_offset > 0 { |
| write!( |
| f, |
| "{}: Length of {} byte(s) is too big for an '{}' (maximum is {} bytes). The {} was used to determine the length ('{}' starts at overall parsed byte {}).", |
| self.layer.error_title(), |
| self.len, |
| self.layer, |
| self.required_len, |
| len_source, |
| self.layer, |
| self.layer_start_offset |
| ) |
| } else { |
| write!( |
| f, |
| "{}: Length of {} byte(s) is too big for an '{}' (maximum is {} bytes). The {} was used to determine the length.", |
| self.layer.error_title(), |
| self.len, |
| self.layer, |
| self.required_len, |
| len_source |
| ) |
| } |
| } |
| } |
| |
| #[cfg(feature = "std")] |
| #[cfg_attr(docsrs, doc(cfg(feature = "std")))] |
| impl std::error::Error for LenError { |
| fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { |
| None |
| } |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| use alloc::format; |
| use std::{ |
| collections::hash_map::DefaultHasher, |
| error::Error, |
| hash::{Hash, Hasher}, |
| }; |
| |
| #[test] |
| fn add_offset() { |
| assert_eq!( |
| LenError { |
| required_len: 2, |
| layer: Layer::Icmpv4, |
| len: 1, |
| len_source: LenSource::Slice, |
| layer_start_offset: 20, |
| } |
| .add_offset(100), |
| LenError { |
| required_len: 2, |
| layer: Layer::Icmpv4, |
| len: 1, |
| len_source: LenSource::Slice, |
| layer_start_offset: 120, |
| } |
| ); |
| } |
| |
| #[test] |
| fn debug() { |
| assert_eq!( |
| format!( |
| "{:?}", |
| LenError { |
| required_len: 2, |
| layer: Layer::Ipv4Header, |
| len: 1, |
| len_source: LenSource::Slice, |
| layer_start_offset: 0 |
| } |
| ), |
| format!( |
| "LenError {{ required_len: {:?}, len: {:?}, len_source: {:?}, layer: {:?}, layer_start_offset: {:?} }}", |
| 2, 1, LenSource::Slice, Layer::Ipv4Header, 0 |
| ), |
| ); |
| } |
| |
| #[test] |
| fn clone_eq_hash() { |
| let err = LenError { |
| required_len: 2, |
| layer: Layer::Icmpv4, |
| len: 1, |
| len_source: LenSource::Slice, |
| layer_start_offset: 20, |
| }; |
| assert_eq!(err, err.clone()); |
| let hash_a = { |
| let mut hasher = DefaultHasher::new(); |
| err.hash(&mut hasher); |
| hasher.finish() |
| }; |
| let hash_b = { |
| let mut hasher = DefaultHasher::new(); |
| err.clone().hash(&mut hasher); |
| hasher.finish() |
| }; |
| assert_eq!(hash_a, hash_b); |
| } |
| |
| #[test] |
| fn fmt() { |
| // len sources based tests (not enough data) |
| { |
| use crate::LenSource::*; |
| let len_source_tests = [ |
| (Slice, "IPv4 Header Error: Not enough data to decode 'IPv4 header'. 2 byte(s) would be required, but only 1 byte(s) are available based on the slice length."), |
| (Ipv4HeaderTotalLen, "IPv4 Header Error: Not enough data to decode 'IPv4 header'. 2 byte(s) would be required, but only 1 byte(s) are available based on the length calculated from the IPv4 header 'total length' field."), |
| (Ipv6HeaderPayloadLen, "IPv4 Header Error: Not enough data to decode 'IPv4 header'. 2 byte(s) would be required, but only 1 byte(s) are available based on the length calculated from the IPv6 header 'payload length' field."), |
| (UdpHeaderLen, "IPv4 Header Error: Not enough data to decode 'IPv4 header'. 2 byte(s) would be required, but only 1 byte(s) are available based on the length calculated from the UDP header 'length' field."), |
| (TcpHeaderLen, "IPv4 Header Error: Not enough data to decode 'IPv4 header'. 2 byte(s) would be required, but only 1 byte(s) are available based on the length calculated from the TCP header 'length' field."), |
| ]; |
| |
| for test in len_source_tests { |
| assert_eq!( |
| test.1, |
| format!( |
| "{}", |
| LenError { |
| required_len: 2, |
| layer: Layer::Ipv4Header, |
| len: 1, |
| len_source: test.0, |
| layer_start_offset: 0 |
| } |
| ) |
| ); |
| } |
| } |
| |
| // start offset based test |
| assert_eq!( |
| "IPv4 Header Error: Not enough data to decode 'IPv4 header'. 2 byte(s) would be required, but only 1 byte(s) are available based on the slice length ('IPv4 header' starts at overall parsed byte 4).", |
| format!( |
| "{}", |
| LenError{ |
| required_len: 2, |
| len: 1, |
| len_source: LenSource::Slice, |
| layer: Layer::Ipv4Header, |
| layer_start_offset: 4 |
| } |
| ) |
| ); |
| |
| // len sources based tests (length too big) |
| { |
| use crate::LenSource::*; |
| let len_source_tests = [ |
| (Slice, "IPv4 Header Error: Length of 2 byte(s) is too big for an 'IPv4 header' (maximum is 1 bytes). The slice length was used to determine the length."), |
| (Ipv4HeaderTotalLen, "IPv4 Header Error: Length of 2 byte(s) is too big for an 'IPv4 header' (maximum is 1 bytes). The length calculated from the IPv4 header 'total length' field was used to determine the length."), |
| (Ipv6HeaderPayloadLen, "IPv4 Header Error: Length of 2 byte(s) is too big for an 'IPv4 header' (maximum is 1 bytes). The length calculated from the IPv6 header 'payload length' field was used to determine the length."), |
| (UdpHeaderLen, "IPv4 Header Error: Length of 2 byte(s) is too big for an 'IPv4 header' (maximum is 1 bytes). The length calculated from the UDP header 'length' field was used to determine the length."), |
| (TcpHeaderLen, "IPv4 Header Error: Length of 2 byte(s) is too big for an 'IPv4 header' (maximum is 1 bytes). The length calculated from the TCP header 'length' field was used to determine the length."), |
| ]; |
| |
| for test in len_source_tests { |
| assert_eq!( |
| test.1, |
| format!( |
| "{}", |
| LenError { |
| required_len: 1, |
| layer: Layer::Ipv4Header, |
| len: 2, |
| len_source: test.0, |
| layer_start_offset: 0 |
| } |
| ) |
| ); |
| } |
| } |
| |
| // start offset based test |
| assert_eq!( |
| "IPv4 Header Error: Length of 2 byte(s) is too big for an 'IPv4 header' (maximum is 1 bytes). The slice length was used to determine the length ('IPv4 header' starts at overall parsed byte 4).", |
| format!( |
| "{}", |
| LenError{ |
| required_len: 1, |
| len: 2, |
| len_source: LenSource::Slice, |
| layer: Layer::Ipv4Header, |
| layer_start_offset: 4 |
| } |
| ) |
| ); |
| } |
| |
| #[cfg(feature = "std")] |
| #[test] |
| fn source() { |
| assert!(LenError { |
| required_len: 0, |
| len: 0, |
| len_source: LenSource::Slice, |
| layer: Layer::Ipv4Header, |
| layer_start_offset: 0 |
| } |
| .source() |
| .is_none()); |
| } |
| } |