//! Packet maps. //! //! If configured to do so, a `PacketParser` will create a map that //! charts the byte-stream, describing where the information was //! extracted from. //! //! # Examples //! //! ``` //! # fn main() -> sequoia_openpgp::Result<()> { //! use sequoia_openpgp as openpgp; //! use openpgp::parse::{Parse, PacketParserBuilder}; //! //! let message_data = b"\xcb\x12t\x00\x00\x00\x00\x00Hello world."; //! let pp = PacketParserBuilder::from_bytes(message_data)? //! .map(true) // Enable mapping. //! .buffer_unread_content() // For the packet body. //! .build()? //! .expect("One packet, not EOF"); //! let map = pp.map().expect("Mapping is enabled"); //! //! assert_eq!(map.iter().nth(0).unwrap().name(), "CTB"); //! assert_eq!(map.iter().nth(0).unwrap().offset(), 0); //! assert_eq!(map.iter().nth(0).unwrap().as_bytes(), &[0xcb]); //! # Ok(()) } //! ``` use std::cmp; /// Map created during parsing. #[derive(Clone, Debug)] pub struct Map { length: usize, entries: Vec, header: Vec, data: Vec, } assert_send_and_sync!(Map); /// Represents an entry in the map. #[derive(Clone, Debug)] struct Entry { offset: usize, length: usize, field: &'static str, } impl Map { /// Creates a new map. pub(super) fn new(header: Vec) -> Self { Map { length: 0, entries: Vec::new(), header, data: Vec::new(), } } /// Adds a field to the map. pub(super) fn add(&mut self, field: &'static str, length: usize) { self.entries.push(Entry { offset: self.length, length, field }); self.length += length; } /// Finalizes the map providing the actual data. pub(super) fn finalize(&mut self, data: Vec) { self.data = data; } /// Creates an iterator over the map. /// /// Returns references to [`Field`]s. /// /// /// # Examples /// /// ``` /// # fn main() -> sequoia_openpgp::Result<()> { /// use sequoia_openpgp as openpgp; /// use openpgp::parse::{Parse, PacketParserBuilder}; /// /// let message_data = b"\xcb\x12t\x00\x00\x00\x00\x00Hello world."; /// let pp = PacketParserBuilder::from_bytes(message_data)? /// .map(true) // Enable mapping. /// .buffer_unread_content() // For the packet body. /// .build()? /// .expect("One packet, not EOF"); /// let map = pp.map().expect("Mapping is enabled"); /// /// assert_eq!(map.iter().count(), 6); /// # Ok(()) } /// ``` pub fn iter(&self) -> impl Iterator + Send + Sync { Iter::new(self) } } /// Represents an entry in the map. /// /// A field has a [`name`] returning a human-readable field name /// (e.g. "CTB", or "version"), an [`offset`] into the packet, and the /// read [`data`]. /// /// [`name`]: Field::name /// [`offset`]: Field::offset /// [`data`]: Field::as_bytes #[derive(Clone, Debug)] pub struct Field<'a> { /// Name of the field. name: &'static str, /// Offset of the field in the packet. offset: usize, /// Value of the field. data: &'a [u8], } assert_send_and_sync!(Field<'_>); impl<'a> Field<'a> { fn new(map: &'a Map, i: usize) -> Option> { // Synthetic packets have no CTB. #[allow(clippy::len_zero)] let has_ctb = map.header.len() > 0; // Old-style CTB with indeterminate length emits no length // field. let has_length = map.header.len() > 1; if i == 0 && has_ctb { Some(Field { offset: 0, name: "CTB", data: &map.header.as_slice()[..1], }) } else if i == 1 && has_length { Some(Field { offset: 1, name: "length", data: &map.header.as_slice()[1..] }) } else { let offset = if has_ctb { 1 } else { 0 } + if has_length { 1 } else { 0 }; map.entries.get(i - offset).map(|e| { let len = map.data.len(); let start = cmp::min(len, e.offset); let end = cmp::min(len, e.offset + e.length); Field { offset: map.header.len() + e.offset, name: e.field, data: &map.data[start..end], } }) } } /// Returns the name of the field. /// /// Note: The returned names are for display purposes only and may /// change in the future. /// /// # Examples /// /// ``` /// # fn main() -> sequoia_openpgp::Result<()> { /// use sequoia_openpgp as openpgp; /// use openpgp::parse::{Parse, PacketParserBuilder}; /// /// let message_data = b"\xcb\x12t\x00\x00\x00\x00\x00Hello world."; /// let pp = PacketParserBuilder::from_bytes(message_data)? /// .map(true) // Enable mapping. /// .buffer_unread_content() // For the packet body. /// .build()? /// .expect("One packet, not EOF"); /// let map = pp.map().expect("Mapping is enabled"); /// /// assert_eq!(map.iter().nth(0).unwrap().name(), "CTB"); /// assert_eq!(map.iter().nth(1).unwrap().name(), "length"); /// assert_eq!(map.iter().nth(2).unwrap().name(), "format"); /// assert_eq!(map.iter().nth(3).unwrap().name(), "filename_len"); /// assert_eq!(map.iter().nth(4).unwrap().name(), "date"); /// assert_eq!(map.iter().nth(5).unwrap().name(), "body"); /// assert!(map.iter().nth(6).is_none()); /// # Ok(()) } /// ``` pub fn name(&self) -> &'a str { self.name } /// Returns the offset of the field in the packet. /// /// # Examples /// /// ``` /// # fn main() -> sequoia_openpgp::Result<()> { /// use sequoia_openpgp as openpgp; /// use openpgp::parse::{Parse, PacketParserBuilder}; /// /// let message_data = b"\xcb\x12t\x00\x00\x00\x00\x00Hello world."; /// let pp = PacketParserBuilder::from_bytes(message_data)? /// .map(true) // Enable mapping. /// .buffer_unread_content() // For the packet body. /// .build()? /// .expect("One packet, not EOF"); /// let map = pp.map().expect("Mapping is enabled"); /// /// assert_eq!(map.iter().nth(0).unwrap().offset(), 0); /// assert_eq!(map.iter().nth(1).unwrap().offset(), 1); /// assert_eq!(map.iter().nth(2).unwrap().offset(), 2); /// assert_eq!(map.iter().nth(3).unwrap().offset(), 3); /// assert_eq!(map.iter().nth(4).unwrap().offset(), 4); /// assert_eq!(map.iter().nth(5).unwrap().offset(), 8); /// assert!(map.iter().nth(6).is_none()); /// # Ok(()) } /// ``` pub fn offset(&self) -> usize { self.offset } /// Returns the value of the field. /// /// # Examples /// /// ``` /// # fn main() -> sequoia_openpgp::Result<()> { /// use sequoia_openpgp as openpgp; /// use openpgp::parse::{Parse, PacketParserBuilder}; /// /// let message_data = b"\xcb\x12t\x00\x00\x00\x00\x00Hello world."; /// let pp = PacketParserBuilder::from_bytes(message_data)? /// .map(true) // Enable mapping. /// .buffer_unread_content() // For the packet body. /// .build()? /// .expect("One packet, not EOF"); /// let map = pp.map().expect("Mapping is enabled"); /// /// assert_eq!(map.iter().nth(0).unwrap().as_bytes(), &[0xcb]); /// assert_eq!(map.iter().nth(1).unwrap().as_bytes(), &[0x12]); /// assert_eq!(map.iter().nth(2).unwrap().as_bytes(), "t".as_bytes()); /// assert_eq!(map.iter().nth(3).unwrap().as_bytes(), &[0x00]); /// assert_eq!(map.iter().nth(4).unwrap().as_bytes(), /// &[0x00, 0x00, 0x00, 0x00]); /// assert_eq!(map.iter().nth(5).unwrap().as_bytes(), /// "Hello world.".as_bytes()); /// assert!(map.iter().nth(6).is_none()); /// # Ok(()) } /// ``` pub fn as_bytes(&self) -> &'a [u8] { self.data } } /// An iterator over the map. struct Iter<'a> { map: &'a Map, i: usize, } impl<'a> Iter<'a> { fn new(map: &'a Map) -> Iter<'a> { Iter { map, i: 0, } } } impl<'a> Iterator for Iter<'a> { type Item = Field<'a>; fn next(&mut self) -> Option { let field = Field::new(self.map, self.i); if field.is_some() { self.i += 1; } field } }