| use crate::{err::ipv4_exts::ExtsWalkError, *}; |
| |
| /// IPv4 extension headers present after the ip header. |
| /// |
| /// Currently supported: |
| /// * Authentication Header |
| /// |
| /// Currently not supported: |
| /// - Encapsulating Security Payload Header (ESP) |
| #[derive(Clone, Debug, Eq, PartialEq, Default)] |
| pub struct Ipv4Extensions { |
| pub auth: Option<IpAuthHeader>, |
| } |
| |
| impl Ipv4Extensions { |
| /// Minimum length required for extension header in bytes/octets. |
| /// Which is zero as no extension headers are required. |
| pub const MIN_LEN: usize = 0; |
| |
| /// Maximum summed up length of all extension headers in bytes/octets. |
| pub const MAX_LEN: usize = IpAuthHeader::MAX_LEN; |
| |
| /// Read all known ipv4 extensions and return an `Ipv4Extensions` with the |
| /// identified slices, the final ip number and a slice pointing to the non parsed data. |
| pub fn from_slice( |
| start_ip_number: IpNumber, |
| slice: &[u8], |
| ) -> Result<(Ipv4Extensions, IpNumber, &[u8]), err::ip_auth::HeaderSliceError> { |
| Ipv4ExtensionsSlice::from_slice(start_ip_number, slice).map(|v| (v.0.to_header(), v.1, v.2)) |
| } |
| |
| /// Collects all known ipv4 extension headers in a slice until an error |
| /// is encountered or a "non IP extension header" is found and |
| /// returns the successfully parsed parts (+ the unparsed slice |
| /// it's [`IpNumber`] and the error if one occurred). |
| /// |
| /// The returned values are |
| /// |
| /// * [`Ipv4Extensions`] containing the successfully parsed IPv6 extension headers |
| /// * [`IpNumber`] of unparsed data |
| /// * Slice with unparsed data |
| /// * Optional with error if there was an error wich stoped the parsing. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// use etherparse::{Ipv4Extensions, IpAuthHeader, ip_number::{UDP, AUTHENTICATION_HEADER}}; |
| /// |
| /// let auth_header = IpAuthHeader::new(UDP, 0, 0, &[]).unwrap(); |
| /// let data = auth_header.to_bytes(); |
| /// |
| /// let (ipv4_exts, next_ip_num, next_data, err) = |
| /// Ipv4Extensions::from_slice_lax(AUTHENTICATION_HEADER, &data); |
| /// |
| /// // authentication header is separated and no error occurred |
| /// assert!(ipv4_exts.auth.is_some()); |
| /// assert_eq!(next_ip_num, UDP); |
| /// assert_eq!(next_data, &[]); |
| /// assert!(err.is_none()); |
| /// ``` |
| /// |
| /// It is also ok to pass in a "non ip extension": |
| /// |
| /// ``` |
| /// use etherparse::{Ipv4Extensions, ip_number::UDP}; |
| /// |
| /// let data = [0,1,2,3]; |
| /// // passing a non "ip extension header" ip number |
| /// let (ipv4_exts, next_ip_num, next_data, err) = |
| /// Ipv4Extensions::from_slice_lax(UDP, &data); |
| /// |
| /// // the original data gets returned as UDP is not a |
| /// // an IP extension header |
| /// assert!(ipv4_exts.is_empty()); |
| /// assert_eq!(next_ip_num, UDP); |
| /// assert_eq!(next_data, &data); |
| /// // no errors gets triggered as the data is valid |
| /// assert!(err.is_none()); |
| /// ``` |
| /// |
| /// In case an error occurred the original data gets |
| /// returned together with the error: |
| /// |
| /// ``` |
| /// use etherparse::{ |
| /// Ipv4Extensions, |
| /// IpAuthHeader, |
| /// ip_number::AUTHENTICATION_HEADER, |
| /// LenSource, |
| /// err::{ip_auth::HeaderSliceError::Len, LenError, Layer} |
| /// }; |
| /// |
| /// // providing not enough data |
| /// let (ipv4_exts, next_ip_num, next_data, err) = |
| /// Ipv4Extensions::from_slice_lax(AUTHENTICATION_HEADER, &[]); |
| /// |
| /// // original data will be returned with no data parsed |
| /// assert!(ipv4_exts.is_empty()); |
| /// assert_eq!(next_ip_num, AUTHENTICATION_HEADER); |
| /// assert_eq!(next_data, &[]); |
| /// // the error that stopped the parsing will also be returned |
| /// assert_eq!(err, Some(Len(LenError{ |
| /// required_len: IpAuthHeader::MIN_LEN, |
| /// len: 0, |
| /// len_source: LenSource::Slice, |
| /// layer: Layer::IpAuthHeader, |
| /// layer_start_offset: 0, |
| /// }))); |
| /// ``` |
| pub fn from_slice_lax( |
| start_ip_number: IpNumber, |
| start_slice: &[u8], |
| ) -> ( |
| Ipv4Extensions, |
| IpNumber, |
| &[u8], |
| Option<err::ip_auth::HeaderSliceError>, |
| ) { |
| let (slice, next_ip_number, next_data, error) = |
| Ipv4ExtensionsSlice::from_slice_lax(start_ip_number, start_slice); |
| (slice.to_header(), next_ip_number, next_data, error) |
| } |
| |
| /// Reads the known ipv4 extension headers from the reader and returns the |
| /// headers together with the internet protocol number identifying the protocol |
| /// that will be next. |
| #[cfg(feature = "std")] |
| #[cfg_attr(docsrs, doc(cfg(feature = "std")))] |
| pub fn read<T: std::io::Read + Sized>( |
| reader: &mut T, |
| start_ip_number: IpNumber, |
| ) -> Result<(Ipv4Extensions, IpNumber), err::ip_auth::HeaderReadError> { |
| use ip_number::*; |
| if AUTH == start_ip_number { |
| let header = IpAuthHeader::read(reader)?; |
| let next_ip_number = header.next_header; |
| Ok((Ipv4Extensions { auth: Some(header) }, next_ip_number)) |
| } else { |
| Ok((Default::default(), start_ip_number)) |
| } |
| } |
| |
| /// Reads the known ipv4 extension headers from a length limited reader and returns the |
| /// headers together with the internet protocol number identifying the protocol |
| /// that will be next. |
| #[cfg(feature = "std")] |
| #[cfg_attr(docsrs, doc(cfg(feature = "std")))] |
| pub fn read_limited<T: std::io::Read + Sized>( |
| reader: &mut crate::io::LimitedReader<T>, |
| start_ip_number: IpNumber, |
| ) -> Result<(Ipv4Extensions, IpNumber), err::ip_auth::HeaderLimitedReadError> { |
| use ip_number::*; |
| if AUTH == start_ip_number { |
| let header = IpAuthHeader::read_limited(reader)?; |
| let next_ip_number = header.next_header; |
| Ok((Ipv4Extensions { auth: Some(header) }, next_ip_number)) |
| } else { |
| Ok((Default::default(), start_ip_number)) |
| } |
| } |
| |
| /// Write the extensions to the writer. |
| #[cfg(feature = "std")] |
| #[cfg_attr(docsrs, doc(cfg(feature = "std")))] |
| pub fn write<T: std::io::Write + Sized>( |
| &self, |
| writer: &mut T, |
| start_ip_number: IpNumber, |
| ) -> Result<(), err::ipv4_exts::HeaderWriteError> { |
| use err::ipv4_exts::{ExtsWalkError::*, HeaderWriteError::*}; |
| use ip_number::*; |
| match self.auth { |
| Some(ref header) => { |
| if AUTH == start_ip_number { |
| header.write(writer).map_err(Io) |
| } else { |
| Err(Content(ExtNotReferenced { |
| missing_ext: IpNumber::AUTHENTICATION_HEADER, |
| })) |
| } |
| } |
| None => Ok(()), |
| } |
| } |
| |
| ///Length of the all present headers in bytes. |
| pub fn header_len(&self) -> usize { |
| if let Some(ref header) = self.auth { |
| header.header_len() |
| } else { |
| 0 |
| } |
| } |
| |
| /// Sets all the next_header fields of the headers based on the adviced default order |
| /// with the given protocol number as last "next header" value. The return value is the protocol |
| /// number of the first existing extension header that should be entered in the ipv4 header as |
| /// protocol_number. |
| /// |
| /// If no extension headers are present the value of the argument is returned. |
| pub fn set_next_headers(&mut self, last_protocol_number: IpNumber) -> IpNumber { |
| use ip_number::*; |
| |
| let mut next = last_protocol_number; |
| |
| if let Some(ref mut header) = self.auth { |
| header.next_header = next; |
| next = AUTH; |
| } |
| |
| next |
| } |
| |
| /// Return next header based on the extension headers and |
| /// the first ip protocol number. |
| /// |
| /// In case a header is never referenced a |
| /// [`err::ipv4_exts::ExtsWalkError::ExtNotReferenced`] is returned. |
| pub fn next_header(&self, first_next_header: IpNumber) -> Result<IpNumber, ExtsWalkError> { |
| use ip_number::*; |
| if let Some(ref auth) = self.auth { |
| if first_next_header == AUTH { |
| Ok(auth.next_header) |
| } else { |
| Err(ExtsWalkError::ExtNotReferenced { |
| missing_ext: IpNumber::AUTHENTICATION_HEADER, |
| }) |
| } |
| } else { |
| Ok(first_next_header) |
| } |
| } |
| |
| /// Returns true if no IPv4 extension header is present (all fields `None`). |
| #[inline] |
| pub fn is_empty(&self) -> bool { |
| self.auth.is_none() |
| } |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| use crate::ip_number::*; |
| use crate::test_gens::*; |
| use alloc::vec::Vec; |
| use proptest::prelude::*; |
| use std::io::Cursor; |
| |
| #[test] |
| fn from_slice() { |
| let auth_header = IpAuthHeader::new(UDP, 0, 0, &[]).unwrap(); |
| |
| let buffer = { |
| let mut buffer = Vec::with_capacity(auth_header.header_len()); |
| auth_header.write(&mut buffer).unwrap(); |
| buffer.push(1); |
| buffer.push(2); |
| buffer |
| }; |
| |
| // no auth header |
| { |
| let (header, next, rest) = Ipv4Extensions::from_slice(TCP, &buffer).unwrap(); |
| assert!(header.auth.is_none()); |
| assert_eq!(TCP, next); |
| assert_eq!(rest, &buffer); |
| } |
| |
| // with auth header |
| { |
| let (actual, next, rest) = Ipv4Extensions::from_slice(AUTH, &buffer).unwrap(); |
| assert_eq!(actual.auth.unwrap(), auth_header); |
| assert_eq!(UDP, next); |
| assert_eq!(rest, &buffer[auth_header.header_len()..]); |
| } |
| |
| // too small |
| { |
| use err::ip_auth::HeaderSliceError::Len; |
| const AUTH_HEADER_LEN: usize = 12; |
| assert_eq!( |
| Ipv4Extensions::from_slice(AUTH, &buffer[..auth_header.header_len() - 1]) |
| .unwrap_err(), |
| Len(err::LenError { |
| required_len: AUTH_HEADER_LEN, |
| len: auth_header.header_len() - 1, |
| len_source: LenSource::Slice, |
| layer: err::Layer::IpAuthHeader, |
| layer_start_offset: 0, |
| }) |
| ); |
| } |
| } |
| |
| proptest! { |
| #[test] |
| fn from_slice_lax(auth in ip_auth_any()) { |
| use crate::ip_number::{UDP, AUTHENTICATION_HEADER}; |
| use crate::err::{*, ip_auth::HeaderSliceError::Len}; |
| |
| // normal read |
| { |
| let data = auth.to_bytes(); |
| |
| let (ipv4_exts, next_ip_num, next_data, err) = |
| Ipv4Extensions::from_slice_lax(AUTHENTICATION_HEADER, &data); |
| |
| // authentication header is separated and no error occurred |
| assert_eq!(ipv4_exts.auth, Some(auth.clone())); |
| assert_eq!(next_ip_num, auth.next_header); |
| assert_eq!(next_data, &[]); |
| assert!(err.is_none()); |
| } |
| // normal read with no extension header |
| { |
| let data = [0,1,2,3]; |
| // passing a non "ip extension header" ip number |
| let (ipv4_exts, next_ip_num, next_data, err) = |
| Ipv4Extensions::from_slice_lax(UDP, &data); |
| |
| // the original data gets returned as UDP is not a |
| // an IP extension header |
| assert!(ipv4_exts.is_empty()); |
| assert_eq!(next_ip_num, UDP); |
| assert_eq!(next_data, &data); |
| // no errors gets triggered as the data is valid |
| assert!(err.is_none()); |
| } |
| // len error during parsing |
| { |
| // providing not enough data |
| let (ipv4_exts, next_ip_num, next_data, err) = |
| Ipv4Extensions::from_slice_lax(AUTHENTICATION_HEADER, &[]); |
| |
| // original data will be returned with no data parsed |
| assert!(ipv4_exts.is_empty()); |
| assert_eq!(next_ip_num, AUTHENTICATION_HEADER); |
| assert_eq!(next_data, &[]); |
| // the error that stopped the parsing will also be returned |
| assert_eq!(err, Some(Len(LenError{ |
| required_len: IpAuthHeader::MIN_LEN, |
| len: 0, |
| len_source: LenSource::Slice, |
| layer: Layer::IpAuthHeader, |
| layer_start_offset: 0, |
| }))); |
| } |
| } |
| } |
| |
| proptest! { |
| #[test] |
| fn read(auth in ip_auth_any()) { |
| // None |
| { |
| let mut cursor = Cursor::new(&[]); |
| let (actual, next) = Ipv4Extensions::read(&mut cursor, UDP).unwrap(); |
| assert_eq!(next, UDP); |
| assert_eq!( |
| actual, |
| Ipv4Extensions{ |
| auth: None, |
| } |
| ); |
| } |
| |
| // Some sucessfull |
| { |
| let buffer = { |
| let mut buffer = Vec::with_capacity(auth.header_len()); |
| auth.write(&mut buffer).unwrap(); |
| buffer.push(1); |
| buffer |
| }; |
| let mut cursor = Cursor::new(&buffer); |
| let (actual, next) = Ipv4Extensions::read(&mut cursor, AUTH).unwrap(); |
| assert_eq!(auth.header_len(), cursor.position() as usize); |
| assert_eq!(next, auth.next_header); |
| assert_eq!( |
| actual, |
| Ipv4Extensions{ |
| auth: Some(auth.clone()), |
| } |
| ); |
| } |
| |
| // Some error |
| { |
| let mut cursor = Cursor::new(&[]); |
| assert!(Ipv4Extensions::read(&mut cursor, AUTH).is_err()); |
| } |
| } |
| } |
| |
| #[test] |
| fn write() { |
| // None |
| { |
| let mut buffer = Vec::new(); |
| Ipv4Extensions { auth: None } |
| .write(&mut buffer, UDP) |
| .unwrap(); |
| assert_eq!(0, buffer.len()); |
| } |
| |
| // Some |
| let auth_header = IpAuthHeader::new(UDP, 0, 0, &[]).unwrap(); |
| { |
| let mut buffer = Vec::with_capacity(auth_header.header_len()); |
| Ipv4Extensions { |
| auth: Some(auth_header.clone()), |
| } |
| .write(&mut buffer, AUTH) |
| .unwrap(); |
| let (read_header, _) = IpAuthHeader::from_slice(&buffer).unwrap(); |
| assert_eq!(auth_header, read_header); |
| } |
| |
| // Some bad start number |
| { |
| use crate::err::ipv4_exts::ExtsWalkError::ExtNotReferenced; |
| |
| let mut buffer = Vec::new(); |
| let err = Ipv4Extensions { |
| auth: Some(auth_header.clone()), |
| } |
| .write(&mut buffer, UDP) |
| .unwrap_err(); |
| assert_eq!( |
| err.content().unwrap(), |
| &ExtNotReferenced { |
| missing_ext: IpNumber::AUTHENTICATION_HEADER, |
| } |
| ); |
| } |
| |
| // Some: Write error |
| { |
| let mut buffer = Vec::with_capacity(auth_header.header_len() - 1); |
| buffer.resize(auth_header.header_len() - 1, 0); |
| let mut cursor = Cursor::new(&mut buffer[..]); |
| let err = Ipv4Extensions { |
| auth: Some(auth_header.clone()), |
| } |
| .write(&mut cursor, AUTH) |
| .unwrap_err(); |
| assert!(err.io().is_some()); |
| } |
| } |
| |
| #[test] |
| fn header_len() { |
| // None |
| assert_eq!(0, Ipv4Extensions { auth: None }.header_len()); |
| |
| // Some |
| { |
| let auth = IpAuthHeader::new(UDP, 0, 0, &[]).unwrap(); |
| assert_eq!( |
| auth.header_len(), |
| Ipv4Extensions { auth: Some(auth) }.header_len() |
| ); |
| } |
| // Some with paylaod |
| { |
| let auth = IpAuthHeader::new(UDP, 0, 0, &[1, 2, 3, 4]).unwrap(); |
| assert_eq!( |
| auth.header_len(), |
| Ipv4Extensions { auth: Some(auth) }.header_len() |
| ); |
| } |
| } |
| |
| #[test] |
| fn set_next_headers() { |
| // None |
| { |
| let mut exts = Ipv4Extensions { auth: None }; |
| assert_eq!(UDP, exts.set_next_headers(UDP)); |
| } |
| |
| // Some |
| { |
| let mut exts = Ipv4Extensions { |
| auth: Some(IpAuthHeader::new(TCP, 0, 0, &[]).unwrap()), |
| }; |
| assert_eq!(TCP, exts.auth.as_ref().unwrap().next_header); |
| // change from TCP to UDP |
| let re = exts.set_next_headers(UDP); |
| assert_eq!(AUTH, re); |
| assert_eq!(UDP, exts.auth.as_ref().unwrap().next_header); |
| } |
| } |
| |
| #[test] |
| fn next_header() { |
| // None |
| { |
| let exts = Ipv4Extensions { auth: None }; |
| assert_eq!(UDP, exts.next_header(UDP).unwrap()); |
| } |
| // Some |
| { |
| let exts = Ipv4Extensions { |
| auth: Some(IpAuthHeader::new(TCP, 0, 0, &[]).unwrap()), |
| }; |
| |
| // auth referenced |
| assert_eq!(TCP, exts.next_header(AUTH).unwrap()); |
| |
| // auth not referenced (error) |
| use crate::err::ipv4_exts::ExtsWalkError::ExtNotReferenced; |
| assert_eq!( |
| ExtNotReferenced { |
| missing_ext: IpNumber::AUTHENTICATION_HEADER |
| }, |
| exts.next_header(TCP).unwrap_err() |
| ); |
| } |
| } |
| |
| #[test] |
| fn is_empty() { |
| // empty |
| assert!(Ipv4Extensions { auth: None }.is_empty()); |
| |
| // auth |
| assert_eq!( |
| false, |
| Ipv4Extensions { |
| auth: Some(IpAuthHeader::new(ip_number::UDP, 0, 0, &[]).unwrap()), |
| } |
| .is_empty() |
| ); |
| } |
| |
| proptest! { |
| #[test] |
| fn debug(auth in ip_auth_any()) { |
| use alloc::format; |
| |
| // None |
| assert_eq!( |
| &format!("Ipv4Extensions {{ auth: {:?} }}", Option::<IpAuthHeader>::None), |
| &format!( |
| "{:?}", |
| Ipv4Extensions { |
| auth: None, |
| } |
| ) |
| ); |
| |
| // Some |
| assert_eq!( |
| &format!("Ipv4Extensions {{ auth: {:?} }}", Some(auth.clone())), |
| &format!( |
| "{:?}", |
| Ipv4Extensions { |
| auth: Some(auth.clone()), |
| } |
| ) |
| ); |
| } |
| } |
| |
| proptest! { |
| #[test] |
| fn clone_eq(auth in ip_auth_any()) { |
| // None |
| { |
| let header = Ipv4Extensions{ |
| auth: None, |
| }; |
| assert_eq!( |
| header.clone(), |
| Ipv4Extensions{ |
| auth: None, |
| } |
| ); |
| } |
| |
| // Some |
| { |
| let header = Ipv4Extensions{ |
| auth: Some(auth.clone()), |
| }; |
| assert_eq!( |
| header.clone(), |
| Ipv4Extensions{ |
| auth: Some(auth.clone()), |
| } |
| ); |
| } |
| } |
| } |
| } |