| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| //! Utilities for parsing and serializing sequential records. |
| //! |
| //! This module provides utilities for parsing and serializing repeated, |
| //! sequential records. Examples of packet formats which include repeated, |
| //! sequential records include IPv4, IPv6, TCP, NDP, and IGMP. |
| //! |
| //! The utilities in this module are very flexible and generic. The user must |
| //! supply a number of details about the format in order for parsing and |
| //! serializing to work. See the TODO trait for more details. |
| //! |
| //! Some packet formats use a [type-length-value]-like encoding for options. |
| //! Examples include IPv4, TCP, and NDP options. Special support for these |
| //! formats is provided by the [`options`] submodule. |
| //! |
| //! [`options`]: crate::records::options |
| //! [type-length-value]: https://en.wikipedia.org/wiki/Type-length-value |
| |
| use core::borrow::Borrow; |
| use core::marker::PhantomData; |
| use core::ops::Deref; |
| |
| use zerocopy::ByteSlice; |
| |
| use crate::serialize::InnerPacketBuilder; |
| use crate::util::{FromRaw, MaybeParsed}; |
| use crate::{BufferView, BufferViewMut}; |
| |
| /// A type that encapsuates the result of a record parsing operation. |
| pub type RecordParseResult<T, E> = std::result::Result<ParsedRecord<T>, E>; |
| |
| /// A type that encapsulates the successful result of a parsing operation. |
| pub enum ParsedRecord<T> { |
| /// A record was successfully consumed and parsed. |
| Parsed(T), |
| |
| /// A record was consumed but not parsed for non-fatal reasons. |
| /// |
| /// The caller should attempt to parse the next record to get a successfully |
| /// parsed record. |
| /// |
| /// An example of a record that is skippable is a record used for padding. |
| Skipped, |
| |
| /// All possible records have been already been consumed; there is nothing |
| /// left to parse. |
| /// |
| /// The behavior is unspecified if callers attempt to parse another record. |
| Done, |
| } |
| |
| impl<T> ParsedRecord<T> { |
| /// Does this result indicate that a record was consumed? |
| pub fn consumed(&self) -> bool { |
| match self { |
| ParsedRecord::Parsed(_) | ParsedRecord::Skipped => true, |
| ParsedRecord::Done => false, |
| } |
| } |
| } |
| |
| /// A parsed sequence of records. |
| /// |
| /// `Records` represents a pre-parsed sequence of records whose structure is |
| /// enforced by the impl in `R`. |
| #[derive(Debug, PartialEq)] |
| pub struct Records<B, R: RecordsImplLayout> { |
| bytes: B, |
| record_count: usize, |
| context: R::Context, |
| } |
| |
| /// An unchecked sequence of records. |
| /// |
| /// `RecordsRaw` represents a not-yet-parsed and not-yet-validated sequence of |
| /// records, whose structure is enforced by the impl in `R`. |
| /// |
| /// [`Records`] provides an implementation of [`FromRaw`] that can be used to |
| /// validate a `RecordsRaw`. |
| #[derive(Debug)] |
| pub struct RecordsRaw<B, R: RecordsImplLayout> { |
| bytes: B, |
| context: R::Context, |
| } |
| |
| impl<B, R> RecordsRaw<B, R> |
| where |
| R: RecordsImplLayout<Context = ()>, |
| { |
| /// Creates a new `RecordsRaw` with the data in `bytes`. |
| pub fn new(bytes: B) -> Self { |
| Self { bytes, context: () } |
| } |
| } |
| |
| impl<B, R> RecordsRaw<B, R> |
| where |
| R: for<'a> RecordsRawImpl<'a>, |
| B: ByteSlice, |
| { |
| /// Raw parse a sequence of records with a context. |
| /// |
| /// See [`RecordsRaw::parse_raw_with_mut_context`] for details on `bytes`, |
| /// `context`, and return value. `parse_raw_with_context` just calls |
| /// `parse_raw_with_mut_context` with a mutable reference to the `context` |
| /// which is passed by value to this function. |
| pub fn parse_raw_with_context<BV: BufferView<B>>( |
| bytes: &mut BV, |
| mut context: R::Context, |
| ) -> MaybeParsed<Self, (B, R::Error)> { |
| Self::parse_raw_with_mut_context(bytes, &mut context) |
| } |
| |
| /// Raw parse a sequence of records with a mutable context. |
| /// |
| /// `parse_raw_with_mut_context` shallowly parses `bytes` as a sequence of |
| /// records. `context` may be used by implementers to maintain state. |
| /// |
| /// `parse_raw_with_mut_context` performs a single pass over all of the |
| /// records to be able to find the end of the records list and update |
| /// `bytes` accordingly. Upon return, `bytes` is moved to the first byte |
| /// after the records list (if the return is a [`MaybeParsed::Complete`], |
| /// otherwise `bytes` will be at the point where raw parsing error was |
| /// found. |
| pub fn parse_raw_with_mut_context<BV: BufferView<B>>( |
| bytes: &mut BV, |
| context: &mut R::Context, |
| ) -> MaybeParsed<Self, (B, R::Error)> { |
| let c = context.clone(); |
| let mut b = LongLivedBuff::new(bytes.as_ref()); |
| let r = loop { |
| match R::parse_raw_with_context(&mut b, context) { |
| Ok(true) => {} // continue consuming from data |
| Ok(false) => { |
| break None; |
| } |
| Err(e) => { |
| break Some(e); |
| } |
| } |
| }; |
| |
| // When we get here, we know that whatever is left in `b` is not needed |
| // so we only take the amount of bytes we actually need from `bytes`, |
| // leaving the rest alone for the caller to continue parsing with. |
| let bytes_len = bytes.len(); |
| let b_len = b.len(); |
| let taken = bytes.take_front(bytes_len - b_len).unwrap(); |
| |
| match r { |
| Some(error) => MaybeParsed::Incomplete((taken, error)), |
| None => MaybeParsed::Complete(RecordsRaw { bytes: taken, context: c }), |
| } |
| } |
| } |
| |
| impl<B, R> RecordsRaw<B, R> |
| where |
| R: for<'a> RecordsRawImpl<'a> + RecordsImplLayout<Context = ()>, |
| B: ByteSlice, |
| { |
| /// Raw parses a sequence of records. |
| /// |
| /// Equivalent to calling [`RecordsRaw::parse_raw_with_context`] with |
| /// `context = ()`. |
| pub fn parse_raw<BV: BufferView<B>>(bytes: &mut BV) -> MaybeParsed<Self, (B, R::Error)> { |
| Self::parse_raw_with_context(bytes, ()) |
| } |
| } |
| |
| impl<B, R> Deref for RecordsRaw<B, R> |
| where |
| B: ByteSlice, |
| R: RecordsImplLayout, |
| { |
| type Target = [u8]; |
| |
| fn deref(&self) -> &[u8] { |
| self.bytes.deref() |
| } |
| } |
| |
| impl<B: Deref<Target = [u8]>, R: RecordsImplLayout> RecordsRaw<B, R> { |
| /// Gets the underlying bytes. |
| /// |
| /// `bytes` returns a reference to the byte slice backing this `RecordsRaw`. |
| pub fn bytes(&self) -> &[u8] { |
| &self.bytes |
| } |
| } |
| |
| /// An iterator over the records contained inside a [`Records`] instance. |
| #[derive(Copy, Clone, Debug)] |
| pub struct RecordsIter<'a, R: RecordsImpl<'a>> { |
| bytes: &'a [u8], |
| records_left: usize, |
| context: R::Context, |
| } |
| |
| /// The context kept while performing records parsing. |
| /// |
| /// Types which implement `RecordsContext` can be used as the long-lived context |
| /// which is kept during records parsing. This context allows parsers to keep |
| /// running computations over the span of multiple records. |
| pub trait RecordsContext: Sized + Clone { |
| /// Clone a context for iterator purposes. |
| /// |
| /// `clone_for_iter` is useful for cloning a context to be used by |
| /// `RecordsIter`. Since `Records::parse_with_context` will do a full pass |
| /// over all the records to check for errors, a `RecordsIter` should never |
| /// error. Therefore, instead of doing checks when iterating (if a context |
| /// was used for checks), a clone of a context can be made specifically for |
| /// iterator purposes that does not do checks (which may be expensive). |
| /// |
| /// The default implementation of this method is equivalent to |
| /// `self.clone()`. |
| fn clone_for_iter(&self) -> Self { |
| self.clone() |
| } |
| } |
| |
| // Implement the `RecordsContext` trait for `usize` which will be used by record |
| // limiting contexts (see [`LimitedRecordsImpl`]) and for `()` which is to |
| // represent an empty/no context-type. |
| impl RecordsContext for usize {} |
| impl RecordsContext for () {} |
| |
| /// Basic associated types used by a [`RecordsImpl`]. |
| /// |
| /// This trait is kept separate from `RecordsImpl` so that the associated types |
| /// do not depend on the lifetime parameter to `RecordsImpl`. |
| pub trait RecordsImplLayout { |
| // TODO(joshlf): Once associated type defaults are stable, give the |
| // `Context` type a default of `()`. |
| |
| /// A context type that can be used to maintain state while parsing multiple |
| /// records. |
| type Context: RecordsContext; |
| |
| /// The type of errors that may be returned by a call to |
| /// [`RecordsImpl::parse_with_context`]. |
| type Error; |
| } |
| |
| /// An implementation of a records parser. |
| /// |
| /// `RecordsImpl` provides functions to parse sequential records. It is required |
| /// in order to construct a [`Records`] or [`RecordsIter`]. |
| pub trait RecordsImpl<'a>: RecordsImplLayout { |
| /// The type of a single record; the output from the [`parse_with_context`] |
| /// function. |
| /// |
| /// For long or variable-length data, implementers are advised to make |
| /// `Record` a reference into the bytes passed to `parse_with_context`. Such |
| /// a reference will need to carry the lifetime `'a`, which is the same |
| /// lifetime that is passed to `parse_with_context`, and is also the |
| /// lifetime parameter to this trait. |
| /// |
| /// [`parse_with_context`]: crate::records::RecordsImpl::parse_with_context |
| type Record; |
| |
| /// Parses a record with some context. |
| /// |
| /// `parse_with_context` takes a variable-length `data` and a `context` to |
| /// maintain state. |
| /// |
| /// `data` may be empty. It is up to the implementer to handle an exhausted |
| /// `data`. |
| /// |
| /// When returning `Ok(ParsedRecord::Skipped)`, it's the implementer's |
| /// responsibility to consume the bytes of the record from `data`. If this |
| /// doesn't happen, then `parse_with_context` will be called repeatedly on |
| /// the same `data`, and the program will be stuck in an infinite loop. If |
| /// the implementation is unable to determine how many bytes to consume from |
| /// `data` in order to skip the record, `parse_with_context` must return |
| /// `Err`. |
| /// |
| /// `parse_with_context` must be deterministic, or else |
| /// [`Records::parse_with_context`] cannot guarantee that future iterations |
| /// will not produce errors (and panic). |
| fn parse_with_context<BV: BufferView<&'a [u8]>>( |
| data: &mut BV, |
| context: &mut Self::Context, |
| ) -> RecordParseResult<Self::Record, Self::Error>; |
| } |
| |
| /// An implementation of a raw records parser. |
| /// |
| /// `RecordsRawImpl` provides functions to raw-parse sequential records. It is |
| /// required to construct a partially-parsed [`RecordsRaw`]. |
| /// |
| /// `RecordsRawImpl` is meant to perform little or no validation on each record |
| /// it consumes. It is primarily used to be able to walk record sequences with |
| /// unknown lengths. |
| pub trait RecordsRawImpl<'a>: RecordsImplLayout { |
| /// Raw-parses a single record with some context. |
| /// |
| /// `parse_raw_with_context` takes a variable length `data` and a `context` |
| /// to maintain state, and returns `Ok(true)` if a record is successfully |
| /// consumed, `Ok(false)` if it is unable to parse more records, and |
| /// `Err(err)` if the `data` is malformed in any way. |
| /// |
| /// `data` may be empty. It is up to the implementer to handle an exhausted |
| /// `data`. |
| /// |
| /// It's the implementer's responsibility to consume exactly one record from |
| /// `data` when returning `Ok(_)`. |
| fn parse_raw_with_context<BV: BufferView<&'a [u8]>>( |
| data: &mut BV, |
| context: &mut Self::Context, |
| ) -> Result<bool, Self::Error>; |
| } |
| |
| /// A limited, parsed sequence of records. |
| /// |
| /// `LimitedRecords` represents a parsed sequence of records that can be limited |
| /// to a certain number of records. Unlike [`Records`], which accepts a |
| /// [`RecordsImpl`], `LimitedRecords` accepts a type that implements |
| /// [`LimitedRecordsImpl`]. |
| pub type LimitedRecords<B, R> = Records<B, LimitedRecordsImplBridge<R>>; |
| |
| /// Create a bridge to `RecordsImplLayout` and `RecordsImpl` from an `R` that |
| /// implements `LimitedRecordsImplLayout`. |
| /// |
| /// The obvious solution to this problem would be to define `LimitedRecords` as |
| /// follows, along with the following blanket impls: |
| /// |
| /// ```rust,ignore |
| /// pub type LimitedRecords<B, R> = Records<B, R>; |
| /// |
| /// impl<R: LimitedRecordsImplLayout> RecordsImplLayout for R { ... } |
| /// |
| /// impl<'a, R: LimitedRecordsImpl<'a>> RecordsImpl<'a> for R { ... } |
| /// ``` |
| /// |
| /// Unfortunately, we also provide a similar type alias in the `options` module, |
| /// defining options parsing in terms of records parsing. If we were to provide |
| /// both these blanket impls and the similar blanket impls in terms of |
| /// `OptionsImplLayout` and `OptionsImpl`, we would have conflicting blanket |
| /// impls. Instead, we wrap the `LimitedRecordsImpl` type in a |
| /// `LimitedRecordsImplBridge` in order to make it a distinct concrete type and |
| /// avoid the conflicting blanket impls problem. |
| /// |
| /// Note that we could theoretically provide the blanket impl here and only use |
| /// the newtype trick in the `options` module (or vice-versa), but that would |
| /// just result in more patterns to keep track of. |
| /// |
| /// `LimitedRecordsImplBridge` is `#[doc(hidden)]`; it is only `pub` because it |
| /// appears in the type alias `LimitedRecords`. |
| #[derive(Debug)] |
| #[doc(hidden)] |
| pub struct LimitedRecordsImplBridge<O>(PhantomData<O>); |
| |
| impl<R: LimitedRecordsImplLayout> RecordsImplLayout for LimitedRecordsImplBridge<R> { |
| /// All `LimitedRecords` get a context type of usize. |
| type Context = usize; |
| type Error = R::Error; |
| } |
| |
| impl<'a, R: LimitedRecordsImpl<'a>> RecordsImpl<'a> for LimitedRecordsImplBridge<R> { |
| type Record = R::Record; |
| |
| /// Parse some bytes with a given `context` as a limit. |
| /// |
| /// `parse_with_context` accepts a `bytes` buffer and limit `context` and |
| /// verifies that the limit has not been reached and that `bytes` is not |
| /// empty. See [`EXACT_LIMIT_ERROR`] for information about exact limiting. |
| /// If the limit has not been reached and `bytes` has not been exhausted, |
| /// `LimitedRecordsImpl::parse` will be called to do the actual parsing of a |
| /// record. |
| /// |
| /// [`EXACT_LIMIT_ERROR`]: LimitedRecordsImplLayout::EXACT_LIMIT_ERROR |
| fn parse_with_context<BV: BufferView<&'a [u8]>>( |
| bytes: &mut BV, |
| context: &mut Self::Context, |
| ) -> RecordParseResult<Self::Record, Self::Error> { |
| let limit_hit = *context == 0; |
| |
| if bytes.is_empty() || limit_hit { |
| return match R::EXACT_LIMIT_ERROR { |
| Some(_) if bytes.is_empty() ^ limit_hit => Err(R::EXACT_LIMIT_ERROR.unwrap()), |
| _ => Ok(ParsedRecord::Done), |
| }; |
| } |
| |
| *context = context.checked_sub(1).expect("Can't decrement counter below 0"); |
| |
| R::parse(bytes) |
| } |
| } |
| |
| /// A records implementation that limits the number of records read from a |
| /// buffer. |
| /// |
| /// Some protocol formats encode the number of records that appear in a given |
| /// sequence, for example in a header preceding the records (as in IGMP). |
| /// `LimitedRecordsImplLayout` is like [`RecordsImplLayout`], except that it |
| /// imposes a limit on the number of records that may be present. If more are |
| /// present than expected, a parsing error is returned. |
| /// |
| /// By default, the limit is used as an upper bound - fewer records than the |
| /// limit may be present. This behavior may be overridden by setting the |
| /// associated [`EXACT_LIMIT_ERROR`] constant to `Some(e)`. In that case, if the |
| /// buffer is exhausted before the limit is reached, the error `e` will be |
| /// returned. |
| /// |
| /// Note that an implementation, `R`, of `LimitedRecordsImpl` cannot be used as |
| /// the [`RecordsImpl`] type parameter to [`Records`] (ie, `Records<_, R>`). |
| /// Instead, use [`LimitedRecords<_, R>`]. |
| /// |
| /// [`EXACT_LIMIT_ERROR`]: crate::records::LimitedRecordsImplLayout::EXACT_LIMIT_ERROR |
| /// [`LimitedRecords<_, R>`]: crate::records::LimitedRecords |
| pub trait LimitedRecordsImplLayout { |
| /// The type of error returned from parsing. |
| type Error; |
| |
| /// If `Some(E)`, `parse_with_context` of `LimitedRecordsImplBridge` will emit the |
| /// provided constant as an error if the provided buffer is exhausted while `context` |
| /// is not 0, or if the `context` reaches 0 but the provided buffer is not empty. |
| const EXACT_LIMIT_ERROR: Option<Self::Error> = None; |
| } |
| |
| /// An implementation of a limited records parser. |
| /// |
| /// Like [`RecordsImpl`], `LimitedRecordsImpl` provides functions to parse |
| /// sequential records. Unlike `RecordsImpl`, `LimitedRecordsImpl` also imposes |
| /// a limit on the number of records that may be present. See |
| /// [`LimitedRecordsImplLayout`] for more details. |
| pub trait LimitedRecordsImpl<'a>: LimitedRecordsImplLayout { |
| /// The limited records analogue of [`RecordsImpl::Record`]; see that type's |
| /// documentation for more details. |
| type Record; |
| |
| /// Parses a record after limit check has completed. |
| /// |
| /// `parse` is like [`RecordsImpl::parse_with_context`], except that it is |
| /// only called after limit checks have been performed. When `parse` is |
| /// called, it is guaranteed that the limit has not yet been reached. |
| /// |
| /// Each call to `parse` is assumed to parse exactly one record. If `parse` |
| /// parses zero or more than one record, it may cause a parsing that should |
| /// fail to succeed, or a parsing that should succeed to fail. |
| /// |
| /// For information about return values, see |
| /// [`RecordsImpl::parse_with_context`]. |
| fn parse<BV: BufferView<&'a [u8]>>( |
| bytes: &mut BV, |
| ) -> RecordParseResult<Self::Record, Self::Error>; |
| } |
| |
| /// A builder capable of serializing a record. |
| /// |
| /// Given `R: RecordBuilder`, an iterator of `R` can be used with a |
| /// [`RecordSequenceBuilder`] to serialize a sequence of records. |
| pub trait RecordBuilder { |
| /// Provides the serialized length of a record. |
| /// |
| /// Returns the total length, in bytes, of the serialized encoding of |
| /// `self`. |
| fn serialized_len(&self) -> usize; |
| |
| /// Serializes `self` into a buffer. |
| /// |
| /// `data` will be exactly `self.serialized_len()` bytes long. |
| /// |
| /// # Panics |
| /// |
| /// If `data` is not exactly `self.serialized_len()` bytes long, |
| /// `serialize_into` may panic. |
| fn serialize_into(&self, data: &mut [u8]); |
| } |
| |
| /// A builder capable of serializing a record with an alignment requirement. |
| /// |
| /// Given `R: AlignedRecordBuilder`, an iterator of `R` can be used with an |
| /// [`AlignedRecordSequenceBuilder`] to serialize a sequence of aligned records. |
| pub trait AlignedRecordBuilder: RecordBuilder { |
| /// Returns the alignment requirement of `self`. |
| /// |
| /// The alignment requirement is returned as (x, y), which means that the |
| /// record must be aligned at `x * n + y` bytes from the beginning of the |
| /// records sequence for some non-negative `n`. |
| /// |
| /// `x` must be non-zero and `y` must be smaller than `x`. |
| fn alignment_requirement(&self) -> (usize, usize); |
| |
| /// Serializes the padding between subsequent aligned records. |
| /// |
| /// Some formats require that padding bytes have particular content. This |
| /// function serializes padding bytes as required by the format. |
| fn serialize_padding(buf: &mut [u8], length: usize); |
| } |
| |
| /// A builder capable of serializing a sequence of records. |
| /// |
| /// A `RecordSequenceBuilder` is instantiated with an [`Iterator`] that provides |
| /// [`RecordBuilder`]s to be serialized. The item produced by the iterator can |
| /// be any type which implements `Borrow<R>` for the `RecordBuilder` type `R`. |
| /// |
| /// `RecordSequenceBuilder` implements [`InnerPacketBuilder`]. |
| #[derive(Debug)] |
| pub struct RecordSequenceBuilder<R, I> { |
| records: I, |
| _marker: PhantomData<R>, |
| } |
| |
| impl<R, I> RecordSequenceBuilder<R, I> { |
| /// Creates a new `RecordSequenceBuilder` with the given `records`. |
| /// |
| /// `records` must produce the same sequence of values from every iterator, |
| /// even if cloned. Serialization is typically performed with two passes on |
| /// `records`: one to calculate the total length in bytes (`serialized_len`) |
| /// and another one to serialize to a buffer (`serialize_into`). Violating |
| /// this rule may result in panics or malformed packets. |
| pub fn new(records: I) -> Self { |
| Self { records, _marker: PhantomData } |
| } |
| } |
| |
| impl<R, I> RecordSequenceBuilder<R, I> |
| where |
| R: RecordBuilder, |
| I: Iterator + Clone, |
| I::Item: Borrow<R>, |
| { |
| /// Returns the total length, in bytes, of the serialized encoding of the |
| /// records contained within the `RecordSequenceBuilder`. |
| pub fn serialized_len(&self) -> usize { |
| self.records.clone().map(|r| r.borrow().serialized_len()).sum() |
| } |
| |
| /// `serialize_into` serializes all the records contained within the |
| /// `RecordSequenceBuilder` into the given buffer. |
| /// |
| /// # Panics |
| /// |
| /// `serialize_into` expects that `buffer` has enough bytes to serialize the |
| /// contained records (as obtained from `serialized_len`), otherwise it's |
| /// considered a violation of the API contract and the call may panic. |
| pub fn serialize_into(&self, buffer: &mut [u8]) { |
| let mut b = &mut &mut buffer[..]; |
| for r in self.records.clone() { |
| // SECURITY: Take a zeroed buffer from b to prevent leaking |
| // information from packets previously stored in this buffer. |
| r.borrow().serialize_into(b.take_front_zero(r.borrow().serialized_len()).unwrap()); |
| } |
| } |
| } |
| |
| impl<R, I> InnerPacketBuilder for RecordSequenceBuilder<R, I> |
| where |
| R: RecordBuilder, |
| I: Iterator + Clone, |
| I::Item: Borrow<R>, |
| { |
| fn bytes_len(&self) -> usize { |
| self.serialized_len() |
| } |
| |
| fn serialize(&self, buffer: &mut [u8]) { |
| self.serialize_into(buffer) |
| } |
| } |
| |
| /// A builder capable of serializing a sequence of aligned records. |
| /// |
| /// An `AlignedRecordSequenceBuilder` is instantiated with an [`Iterator`] that |
| /// provides [`AlignedRecordBuilder`]s to be serialized. The item produced by |
| /// the iterator can be any type which implements `Borrow<R>` for the |
| /// `AlignedRecordBuilder` type `R`. |
| /// |
| /// `AlignedRecordSequenceBuilder` implements [`InnerPacketBuilder`]. |
| #[derive(Debug)] |
| pub struct AlignedRecordSequenceBuilder<R, I> { |
| start_pos: usize, |
| records: I, |
| _marker: PhantomData<R>, |
| } |
| |
| impl<R, I> AlignedRecordSequenceBuilder<R, I> { |
| /// Creates a new `AlignedRecordSequenceBuilder` with given `records` and |
| /// `start_pos`. |
| /// |
| /// `records` must produce the same sequence of values from every iterator, |
| /// even if cloned. See `RecordSequenceBuilder` for more details. |
| /// |
| /// Alignment is calculated relative to the beginning of a virtual space of |
| /// bytes. If non-zero, `start_pos` instructs the serializer to consider the |
| /// buffer passed to [`serialize_into`] to start at the byte `start_pos` |
| /// within this virtual space, and to calculate alignment and padding |
| /// accordingly. For example, in the IPv6 Hop-by-Hop extension header, a |
| /// fixed header of two bytes precedes that extension header's options, but |
| /// alignment is calculated relative to the beginning of the extension |
| /// header, not relative to the beginning of the options. Thus, when |
| /// constructing an `AlignedRecordSequenceBuilder` to serialize those |
| /// options, `start_pos` would be 2. |
| /// |
| /// [`serialize_into`]: AlignedRecordSequenceBuilder::serialize_into |
| pub fn new(start_pos: usize, records: I) -> Self { |
| Self { start_pos, records, _marker: PhantomData } |
| } |
| } |
| |
| impl<R, I> AlignedRecordSequenceBuilder<R, I> |
| where |
| R: AlignedRecordBuilder, |
| I: Iterator + Clone, |
| I::Item: Borrow<R>, |
| { |
| /// Returns the total length, in bytes, of the serialized records contained |
| /// within the `AlignedRecordSequenceBuilder`. |
| /// |
| /// Note that this length includes all padding required to ensure that all |
| /// records satisfy their alignment requirements. |
| pub fn serialized_len(&self) -> usize { |
| let mut pos = self.start_pos; |
| self.records |
| .clone() |
| .map(|r| { |
| let (x, y) = r.borrow().alignment_requirement(); |
| let new_pos = align_up_to(pos, x, y) + r.borrow().serialized_len(); |
| let result = new_pos - pos; |
| pos = new_pos; |
| result |
| }) |
| .sum() |
| } |
| |
| /// `serialize_into` serializes all the records contained within the |
| /// `AlignedRecordSequenceBuilder` into the given buffer. |
| /// |
| /// # Panics |
| /// |
| /// `serialize_into` expects that `buffer` has enough bytes to serialize the |
| /// contained records (as obtained from `serialized_len`), otherwise it's |
| /// considered a violation of the API contract and the call may panic. |
| pub fn serialize_into(&self, buffer: &mut [u8]) { |
| let mut b = &mut &mut buffer[..]; |
| let mut pos = self.start_pos; |
| for r in self.records.clone() { |
| let (x, y) = r.borrow().alignment_requirement(); |
| let aligned = align_up_to(pos, x, y); |
| let pad_len = aligned - pos; |
| let pad = b.take_front_zero(pad_len).unwrap(); |
| R::serialize_padding(pad, pad_len); |
| pos = aligned; |
| // SECURITY: Take a zeroed buffer from b to prevent leaking |
| // information from packets previously stored in this buffer. |
| r.borrow().serialize_into(b.take_front_zero(r.borrow().serialized_len()).unwrap()); |
| pos += r.borrow().serialized_len(); |
| } |
| // we have to pad the containing header to 8-octet boundary. |
| let padding = b.take_rest_front_zero(); |
| R::serialize_padding(padding, padding.len()); |
| } |
| } |
| |
| /// Returns the aligned offset which is at `x * n + y`. |
| /// |
| /// # Panics |
| /// |
| /// Panics if `x == 0` or `y >= x`. |
| fn align_up_to(offset: usize, x: usize, y: usize) -> usize { |
| assert!(x != 0 && y < x); |
| // first add `x` to prevent overflow. |
| (offset + x - 1 - y) / x * x + y |
| } |
| |
| impl<B, R> Records<B, R> |
| where |
| B: ByteSlice, |
| R: for<'a> RecordsImpl<'a>, |
| { |
| /// Parses a sequence of records with a context. |
| /// |
| /// See `parse_with_mut_context` for details on `bytes`, `context`, and |
| /// return value. `parse_with_context` just calls `parse_with_mut_context` |
| /// with a mutable reference to the `context` which is passed by value to |
| /// this function. |
| pub fn parse_with_context( |
| bytes: B, |
| mut context: R::Context, |
| ) -> Result<Records<B, R>, R::Error> { |
| Self::parse_with_mut_context(bytes, &mut context) |
| } |
| |
| /// Parses a sequence of records with a mutable context. |
| /// |
| /// `parse_with_mut_context` parses `bytes` as a sequence of records. |
| /// `context` may be used by implementers to maintain state while parsing |
| /// multiple records. |
| /// |
| /// `parse_with_mut_context` performs a single pass over all of the records |
| /// to verify that they are well-formed. Once `parse_with_context` returns |
| /// successfully, the resulting `Records` can be used to construct |
| /// infallible iterators. |
| pub fn parse_with_mut_context( |
| bytes: B, |
| context: &mut R::Context, |
| ) -> Result<Records<B, R>, R::Error> { |
| // First, do a single pass over the bytes to detect any errors up front. |
| // Once this is done, since we have a reference to `bytes`, these bytes |
| // can't change out from under us, and so we can treat any iterator over |
| // these bytes as infallible. This makes a few assumptions, but none of |
| // them are that big of a deal. In all cases, breaking these assumptions |
| // would at worst result in a runtime panic. |
| // - B could return different bytes each time |
| // - R::parse could be non-deterministic |
| let c = context.clone(); |
| let mut b = LongLivedBuff::new(bytes.deref()); |
| let mut record_count = 0; |
| while next::<_, R>(&mut b, context)?.is_some() { |
| record_count += 1; |
| } |
| Ok(Records { bytes, record_count, context: c }) |
| } |
| } |
| |
| impl<B, R> Records<B, R> |
| where |
| B: ByteSlice, |
| R: for<'a> RecordsImpl<'a, Context = ()>, |
| { |
| /// Parses a sequence of records. |
| /// |
| /// Equivalent to calling [`parse_with_context`] with `context = ()`. |
| /// |
| /// [`parse_with_context`]: crate::records::Records::parse_with_context |
| pub fn parse(bytes: B) -> Result<Records<B, R>, R::Error> { |
| Self::parse_with_context(bytes, ()) |
| } |
| } |
| |
| impl<B, R> FromRaw<RecordsRaw<B, R>, ()> for Records<B, R> |
| where |
| for<'a> R: RecordsImpl<'a>, |
| B: ByteSlice, |
| { |
| type Error = R::Error; |
| |
| fn try_from_raw_with(raw: RecordsRaw<B, R>, _args: ()) -> Result<Self, R::Error> { |
| Records::<B, R>::parse_with_context(raw.bytes, raw.context) |
| } |
| } |
| |
| impl<B: Deref<Target = [u8]>, R> Records<B, R> |
| where |
| R: for<'a> RecordsImpl<'a>, |
| { |
| /// Gets the underlying bytes. |
| /// |
| /// `bytes` returns a reference to the byte slice backing this `Records`. |
| pub fn bytes(&self) -> &[u8] { |
| &self.bytes |
| } |
| } |
| |
| impl<'a, B, R> Records<B, R> |
| where |
| B: 'a + ByteSlice, |
| R: RecordsImpl<'a>, |
| { |
| /// Creates an iterator over options. |
| /// |
| /// `iter` constructs an iterator over the records. Since the records were |
| /// validated in `parse`, then so long as [`R::parse_with_context`] is |
| /// deterministic, the iterator is infallible. |
| /// |
| /// [`R::parse_with_context`]: crate::records::RecordsImpl::parse_with_context |
| pub fn iter(&'a self) -> RecordsIter<'a, R> { |
| RecordsIter { |
| bytes: &self.bytes, |
| records_left: self.record_count, |
| context: self.context.clone_for_iter(), |
| } |
| } |
| } |
| |
| impl<'a, R> RecordsIter<'a, R> |
| where |
| R: RecordsImpl<'a>, |
| { |
| /// Gets a reference to the context. |
| pub fn context(&self) -> &R::Context { |
| &self.context |
| } |
| } |
| |
| impl<'a, R> Iterator for RecordsIter<'a, R> |
| where |
| R: RecordsImpl<'a>, |
| { |
| type Item = R::Record; |
| |
| fn next(&mut self) -> Option<R::Record> { |
| let mut bytes = LongLivedBuff::new(self.bytes); |
| // use match rather than expect because expect requires that Err: Debug |
| #[allow(clippy::match_wild_err_arm)] |
| let result = match next::<_, R>(&mut bytes, &mut self.context) { |
| Ok(o) => o, |
| Err(_) => panic!("already-validated options should not fail to parse"), |
| }; |
| if result.is_some() { |
| self.records_left -= 1; |
| } |
| self.bytes = bytes.into_rest(); |
| result |
| } |
| |
| fn size_hint(&self) -> (usize, Option<usize>) { |
| (self.records_left, Some(self.records_left)) |
| } |
| } |
| |
| impl<'a, R> ExactSizeIterator for RecordsIter<'a, R> |
| where |
| R: RecordsImpl<'a>, |
| { |
| fn len(&self) -> usize { |
| self.records_left |
| } |
| } |
| |
| /// Gets the next entry for a set of sequential records in `bytes`. |
| /// |
| /// On return, `bytes` will be pointing to the start of where a next record |
| /// would be. |
| fn next<'a, BV, R>(bytes: &mut BV, context: &mut R::Context) -> Result<Option<R::Record>, R::Error> |
| where |
| R: RecordsImpl<'a>, |
| BV: BufferView<&'a [u8]>, |
| { |
| loop { |
| match R::parse_with_context(bytes, context)? { |
| ParsedRecord::Done => return Ok(None), |
| ParsedRecord::Skipped => {} |
| ParsedRecord::Parsed(o) => return Ok(Some(o)), |
| } |
| } |
| } |
| |
| /// A wrapper around the implementation of `BufferView` for slices. |
| /// |
| /// `LongLivedBuff` is a thin wrapper around `&[u8]` meant to provide an |
| /// implementation of `BufferView` that returns slices tied to the same lifetime |
| /// as the slice that `LongLivedBuff` was created with. This is in contrast to |
| /// the more widely used `&'b mut &'a [u8]` `BufferView` implementer that |
| /// returns slice references tied to lifetime `b`. |
| struct LongLivedBuff<'a>(&'a [u8]); |
| |
| impl<'a> LongLivedBuff<'a> { |
| /// Creates a new `LongLivedBuff` around a slice reference with lifetime |
| /// `a`. |
| /// |
| /// All slices returned by the `BufferView` impl of `LongLivedBuff` are |
| /// guaranteed to return slice references tied to the same lifetime `a`. |
| fn new(data: &'a [u8]) -> LongLivedBuff<'a> { |
| LongLivedBuff::<'a>(data) |
| } |
| } |
| |
| impl<'a> AsRef<[u8]> for LongLivedBuff<'a> { |
| fn as_ref(&self) -> &[u8] { |
| self.0 |
| } |
| } |
| |
| impl<'a> BufferView<&'a [u8]> for LongLivedBuff<'a> { |
| fn take_front(&mut self, n: usize) -> Option<&'a [u8]> { |
| if self.0.len() >= n { |
| let (prefix, rest) = core::mem::replace(&mut self.0, &[]).split_at(n); |
| self.0 = rest; |
| Some(prefix) |
| } else { |
| None |
| } |
| } |
| |
| fn take_back(&mut self, n: usize) -> Option<&'a [u8]> { |
| if self.0.len() >= n { |
| let (rest, suffix) = core::mem::replace(&mut self.0, &[]).split_at(n); |
| self.0 = rest; |
| Some(suffix) |
| } else { |
| None |
| } |
| } |
| |
| fn into_rest(self) -> &'a [u8] { |
| self.0 |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use zerocopy::{AsBytes, FromBytes, LayoutVerified, Unaligned}; |
| |
| use super::*; |
| |
| const DUMMY_BYTES: [u8; 16] = [ |
| 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, |
| 0x04, |
| ]; |
| |
| #[derive(Debug, AsBytes, FromBytes, Unaligned)] |
| #[repr(C)] |
| struct DummyRecord { |
| a: [u8; 2], |
| b: u8, |
| c: u8, |
| } |
| |
| fn parse_dummy_rec<'a, BV>( |
| data: &mut BV, |
| ) -> RecordParseResult<LayoutVerified<&'a [u8], DummyRecord>, ()> |
| where |
| BV: BufferView<&'a [u8]>, |
| { |
| if data.is_empty() { |
| return Ok(ParsedRecord::Done); |
| } |
| |
| match data.take_obj_front::<DummyRecord>() { |
| Some(res) => Ok(ParsedRecord::Parsed(res)), |
| None => Err(()), |
| } |
| } |
| |
| // |
| // Context-less records |
| // |
| |
| #[derive(Debug)] |
| struct ContextlessRecordImpl; |
| |
| impl RecordsImplLayout for ContextlessRecordImpl { |
| type Context = (); |
| type Error = (); |
| } |
| |
| impl<'a> RecordsImpl<'a> for ContextlessRecordImpl { |
| type Record = LayoutVerified<&'a [u8], DummyRecord>; |
| |
| fn parse_with_context<BV: BufferView<&'a [u8]>>( |
| data: &mut BV, |
| _context: &mut Self::Context, |
| ) -> RecordParseResult<Self::Record, Self::Error> { |
| parse_dummy_rec(data) |
| } |
| } |
| |
| // |
| // Limit context records |
| // |
| |
| #[derive(Debug)] |
| struct LimitContextRecordImpl; |
| |
| impl LimitedRecordsImplLayout for LimitContextRecordImpl { |
| type Error = (); |
| } |
| |
| impl<'a> LimitedRecordsImpl<'a> for LimitContextRecordImpl { |
| type Record = LayoutVerified<&'a [u8], DummyRecord>; |
| |
| fn parse<BV: BufferView<&'a [u8]>>( |
| data: &mut BV, |
| ) -> RecordParseResult<Self::Record, Self::Error> { |
| parse_dummy_rec(data) |
| } |
| } |
| |
| // |
| // Exact limit context records |
| // |
| |
| #[derive(Debug)] |
| struct ExactLimitContextRecordImpl; |
| |
| impl LimitedRecordsImplLayout for ExactLimitContextRecordImpl { |
| type Error = (); |
| |
| const EXACT_LIMIT_ERROR: Option<()> = Some(()); |
| } |
| |
| impl<'a> LimitedRecordsImpl<'a> for ExactLimitContextRecordImpl { |
| type Record = LayoutVerified<&'a [u8], DummyRecord>; |
| |
| fn parse<BV: BufferView<&'a [u8]>>( |
| data: &mut BV, |
| ) -> RecordParseResult<Self::Record, Self::Error> { |
| parse_dummy_rec(data) |
| } |
| } |
| |
| // |
| // Filter context records |
| // |
| |
| #[derive(Debug)] |
| struct FilterContextRecordImpl; |
| |
| #[derive(Clone)] |
| struct FilterContext { |
| pub disallowed: [bool; 256], |
| } |
| |
| impl RecordsContext for FilterContext {} |
| |
| impl RecordsImplLayout for FilterContextRecordImpl { |
| type Context = FilterContext; |
| type Error = (); |
| } |
| |
| impl core::fmt::Debug for FilterContext { |
| fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
| write!(f, "FilterContext{{disallowed:{:?}}}", &self.disallowed[..]) |
| } |
| } |
| |
| impl<'a> RecordsImpl<'a> for FilterContextRecordImpl { |
| type Record = LayoutVerified<&'a [u8], DummyRecord>; |
| |
| fn parse_with_context<BV: BufferView<&'a [u8]>>( |
| bytes: &mut BV, |
| context: &mut Self::Context, |
| ) -> RecordParseResult<Self::Record, Self::Error> { |
| if bytes.len() < core::mem::size_of::<DummyRecord>() { |
| Ok(ParsedRecord::Done) |
| } else if bytes.as_ref()[0..core::mem::size_of::<DummyRecord>()] |
| .iter() |
| .any(|x| context.disallowed[*x as usize]) |
| { |
| Err(()) |
| } else { |
| parse_dummy_rec(bytes) |
| } |
| } |
| } |
| |
| // |
| // Stateful context records |
| // |
| |
| #[derive(Debug)] |
| struct StatefulContextRecordImpl; |
| |
| #[derive(Clone, Debug)] |
| struct StatefulContext { |
| pub pre_parse_counter: usize, |
| pub parse_counter: usize, |
| pub post_parse_counter: usize, |
| pub iter: bool, |
| } |
| |
| impl RecordsImplLayout for StatefulContextRecordImpl { |
| type Context = StatefulContext; |
| type Error = (); |
| } |
| |
| impl StatefulContext { |
| pub fn new() -> StatefulContext { |
| StatefulContext { |
| pre_parse_counter: 0, |
| parse_counter: 0, |
| post_parse_counter: 0, |
| iter: false, |
| } |
| } |
| } |
| |
| impl RecordsContext for StatefulContext { |
| fn clone_for_iter(&self) -> Self { |
| let mut x = self.clone(); |
| x.iter = true; |
| x |
| } |
| } |
| |
| impl<'a> RecordsImpl<'a> for StatefulContextRecordImpl { |
| type Record = LayoutVerified<&'a [u8], DummyRecord>; |
| |
| fn parse_with_context<BV: BufferView<&'a [u8]>>( |
| data: &mut BV, |
| context: &mut Self::Context, |
| ) -> RecordParseResult<Self::Record, Self::Error> { |
| if !context.iter { |
| context.pre_parse_counter += 1; |
| } |
| |
| let ret = parse_dummy_rec_with_context(data, context); |
| |
| if let Ok(ParsedRecord::Parsed(_)) = ret { |
| if !context.iter { |
| context.post_parse_counter += 1; |
| } |
| } |
| |
| ret |
| } |
| } |
| |
| impl<'a> RecordsRawImpl<'a> for StatefulContextRecordImpl { |
| fn parse_raw_with_context<BV: BufferView<&'a [u8]>>( |
| data: &mut BV, |
| context: &mut Self::Context, |
| ) -> Result<bool, Self::Error> { |
| Self::parse_with_context(data, context).map(|r| r.consumed()) |
| } |
| } |
| |
| fn parse_dummy_rec_with_context<'a, BV>( |
| data: &mut BV, |
| context: &mut StatefulContext, |
| ) -> RecordParseResult<LayoutVerified<&'a [u8], DummyRecord>, ()> |
| where |
| BV: BufferView<&'a [u8]>, |
| { |
| if data.is_empty() { |
| return Ok(ParsedRecord::Done); |
| } |
| |
| if !context.iter { |
| context.parse_counter += 1; |
| } |
| |
| match data.take_obj_front::<DummyRecord>() { |
| Some(res) => Ok(ParsedRecord::Parsed(res)), |
| None => Err(()), |
| } |
| } |
| |
| fn check_parsed_record(rec: &DummyRecord) { |
| assert_eq!(rec.a[0], 0x01); |
| assert_eq!(rec.a[1], 0x02); |
| assert_eq!(rec.b, 0x03); |
| } |
| |
| fn validate_parsed_stateful_context_records<B: ByteSlice>( |
| records: Records<B, StatefulContextRecordImpl>, |
| context: StatefulContext, |
| ) { |
| // Should be 5 because on the last iteration, we should realize that we |
| // have no more bytes left and end before parsing (also explaining why |
| // `parse_counter` should only be 4. |
| assert_eq!(context.pre_parse_counter, 5); |
| assert_eq!(context.parse_counter, 4); |
| assert_eq!(context.post_parse_counter, 4); |
| |
| let mut iter = records.iter(); |
| let context = &iter.context; |
| assert_eq!(context.pre_parse_counter, 0); |
| assert_eq!(context.parse_counter, 0); |
| assert_eq!(context.post_parse_counter, 0); |
| assert_eq!(context.iter, true); |
| |
| // Manually iterate over `iter` so as to not move it. |
| let mut count = 0; |
| while let Some(_) = iter.next() { |
| count += 1; |
| } |
| assert_eq!(count, 4); |
| |
| // Check to see that when iterating, the context doesn't update counters |
| // as that is how we implemented our StatefulContextRecordImpl.. |
| let context = &iter.context; |
| assert_eq!(context.pre_parse_counter, 0); |
| assert_eq!(context.parse_counter, 0); |
| assert_eq!(context.post_parse_counter, 0); |
| assert_eq!(context.iter, true); |
| } |
| |
| // |
| // Utilities |
| // |
| |
| impl<B, R> Records<B, R> |
| where |
| B: ByteSlice, |
| R: for<'a> RecordsImpl<'a>, |
| { |
| /// Parse a sequence of records with a context, using a `BufferView`. |
| /// |
| /// See `parse_bv_with_mut_context` for details on `bytes`, `context`, |
| /// and return value. `parse_bv_with_context` just calls |
| /// `parse_bv_with_mut_context` with a mutable reference to the |
| /// `context` which is passed by value to this function. |
| pub fn parse_bv_with_context<BV: BufferView<B>>( |
| bytes: &mut BV, |
| mut context: R::Context, |
| ) -> Result<Records<B, R>, R::Error> { |
| Self::parse_bv_with_mut_context(bytes, &mut context) |
| } |
| |
| /// Parse a sequence of records with a mutable context, using a |
| /// `BufferView`. |
| /// |
| /// This function is exactly the same as `parse_with_mut_context` except |
| /// instead of operating on a `ByteSlice`, we operate on a |
| /// `BufferView<B>` where `B` is a `ByteSlice`. |
| /// `parse_bv_with_mut_context` enables parsing records without knowing |
| /// the size of all records beforehand (unlike `parse_with_mut_context` |
| /// where callers need to pass in a `ByteSlice` of some predetermined |
| /// sized). Since callers will provide a mutable reference to a |
| /// `BufferView`, `parse_bv_with_mut_context` will take only the amount |
| /// of bytes it needs to parse records, leaving the rest in the |
| /// `BufferView` object. That is, when `parse_bv_with_mut_context` |
| /// returns, the `BufferView` object provided will be x bytes smaller, |
| /// where x is the number of bytes required to parse the records. |
| pub fn parse_bv_with_mut_context<BV: BufferView<B>>( |
| bytes: &mut BV, |
| context: &mut R::Context, |
| ) -> Result<Records<B, R>, R::Error> { |
| let c = context.clone(); |
| let mut b = LongLivedBuff::new(bytes.as_ref()); |
| let mut record_count = 0; |
| while next::<_, R>(&mut b, context)?.is_some() { |
| record_count += 1; |
| } |
| |
| // When we get here, we know that whatever is left in `b` is not |
| // needed so we only take the amount of bytes we actually need from |
| // `bytes`, leaving the rest alone for the caller to continue |
| // parsing with. |
| let bytes_len = bytes.len(); |
| let b_len = b.len(); |
| Ok(Records { |
| bytes: bytes.take_front(bytes_len - b_len).unwrap(), |
| record_count, |
| context: c, |
| }) |
| } |
| } |
| |
| #[test] |
| fn all_records_parsing() { |
| let parsed = Records::<_, ContextlessRecordImpl>::parse(&DUMMY_BYTES[..]).unwrap(); |
| let mut iter = parsed.iter(); |
| // Test ExactSizeIterator implementation. |
| assert_eq!(iter.len(), 4); |
| let mut cnt = 4; |
| while let Some(_) = iter.next() { |
| cnt -= 1; |
| assert_eq!(iter.len(), cnt); |
| } |
| assert_eq!(iter.len(), 0); |
| for rec in parsed.iter() { |
| check_parsed_record(rec.deref()); |
| } |
| } |
| |
| #[test] |
| fn limit_records_parsing() { |
| // Test without mutable limit/context |
| let limit = 2; |
| let parsed = LimitedRecords::<_, LimitContextRecordImpl>::parse_with_context( |
| &DUMMY_BYTES[..], |
| limit, |
| ) |
| .unwrap(); |
| assert_eq!(parsed.iter().count(), limit); |
| for rec in parsed.iter() { |
| check_parsed_record(rec.deref()); |
| } |
| |
| // Test with mutable limit/context |
| let mut mut_limit = limit; |
| let parsed = LimitedRecords::<_, LimitContextRecordImpl>::parse_with_mut_context( |
| &DUMMY_BYTES[..], |
| &mut mut_limit, |
| ) |
| .unwrap(); |
| assert_eq!(mut_limit, 0); |
| assert_eq!(parsed.iter().count(), limit); |
| for rec in parsed.iter() { |
| check_parsed_record(rec.deref()); |
| } |
| } |
| |
| #[test] |
| fn limit_records_parsing_with_bv() { |
| // Test without mutable limit/context |
| let limit = 2; |
| let mut bv = &mut &DUMMY_BYTES[..]; |
| let parsed = |
| LimitedRecords::<_, LimitContextRecordImpl>::parse_bv_with_context(&mut bv, limit) |
| .unwrap(); |
| assert_eq!(bv.len(), DUMMY_BYTES.len() - std::mem::size_of::<DummyRecord>() * limit); |
| assert_eq!(parsed.iter().count(), limit); |
| for rec in parsed.iter() { |
| check_parsed_record(rec.deref()); |
| } |
| |
| // Test with mutable limit context |
| let mut mut_limit = limit; |
| let mut bv = &mut &DUMMY_BYTES[..]; |
| let parsed = LimitedRecords::<_, LimitContextRecordImpl>::parse_bv_with_mut_context( |
| &mut bv, |
| &mut mut_limit, |
| ) |
| .unwrap(); |
| assert_eq!(mut_limit, 0); |
| assert_eq!(bv.len(), DUMMY_BYTES.len() - std::mem::size_of::<DummyRecord>() * limit); |
| assert_eq!(parsed.iter().count(), limit); |
| for rec in parsed.iter() { |
| check_parsed_record(rec.deref()); |
| } |
| } |
| |
| #[test] |
| fn exact_limit_records_parsing() { |
| LimitedRecords::<_, ExactLimitContextRecordImpl>::parse_with_context(&DUMMY_BYTES[..], 2) |
| .expect_err("fails if all the buffer hasn't been parsed"); |
| LimitedRecords::<_, ExactLimitContextRecordImpl>::parse_with_context(&DUMMY_BYTES[..], 5) |
| .expect_err("fails if can't extract enough records"); |
| } |
| |
| #[test] |
| fn context_filtering_some_byte_records_parsing() { |
| // Do not disallow any bytes |
| let context = FilterContext { disallowed: [false; 256] }; |
| let parsed = |
| Records::<_, FilterContextRecordImpl>::parse_with_context(&DUMMY_BYTES[..], context) |
| .unwrap(); |
| assert_eq!(parsed.iter().count(), 4); |
| for rec in parsed.iter() { |
| check_parsed_record(rec.deref()); |
| } |
| |
| // Do not allow byte value 0x01 |
| let mut context = FilterContext { disallowed: [false; 256] }; |
| context.disallowed[1] = true; |
| Records::<_, FilterContextRecordImpl>::parse_with_context(&DUMMY_BYTES[..], context) |
| .expect_err("fails if the buffer has an element with value 0x01"); |
| } |
| |
| #[test] |
| fn context_filtering_some_byte_records_parsing_with_bv() { |
| // Do not disallow any bytes |
| let context = FilterContext { disallowed: [false; 256] }; |
| let mut bv = &mut &DUMMY_BYTES[..]; |
| let parsed = |
| Records::<_, FilterContextRecordImpl>::parse_bv_with_context(&mut bv, context).unwrap(); |
| assert_eq!(bv.len(), 0); |
| assert_eq!(parsed.iter().count(), 4); |
| for rec in parsed.iter() { |
| check_parsed_record(rec.deref()); |
| } |
| |
| // Do not allow byte value 0x01 |
| let mut bv = &mut &DUMMY_BYTES[..]; |
| let mut context = FilterContext { disallowed: [false; 256] }; |
| context.disallowed[1] = true; |
| Records::<_, FilterContextRecordImpl>::parse_bv_with_context(&mut bv, context) |
| .expect_err("fails if the buffer has an element with value 0x01"); |
| assert_eq!(bv.len(), DUMMY_BYTES.len()); |
| } |
| |
| #[test] |
| fn stateful_context_records_parsing() { |
| let mut context = StatefulContext::new(); |
| let parsed = Records::<_, StatefulContextRecordImpl>::parse_with_mut_context( |
| &DUMMY_BYTES[..], |
| &mut context, |
| ) |
| .unwrap(); |
| validate_parsed_stateful_context_records(parsed, context); |
| } |
| |
| #[test] |
| fn stateful_context_records_parsing_with_bv() { |
| let mut context = StatefulContext::new(); |
| let mut bv = &mut &DUMMY_BYTES[..]; |
| let parsed = Records::<_, StatefulContextRecordImpl>::parse_bv_with_mut_context( |
| &mut bv, |
| &mut context, |
| ) |
| .unwrap(); |
| assert_eq!(bv.len(), 0); |
| validate_parsed_stateful_context_records(parsed, context); |
| } |
| |
| #[test] |
| fn raw_parse_success() { |
| let mut context = StatefulContext::new(); |
| let mut bv = &mut &DUMMY_BYTES[..]; |
| let result = RecordsRaw::<_, StatefulContextRecordImpl>::parse_raw_with_mut_context( |
| &mut bv, |
| &mut context, |
| ) |
| .complete() |
| .unwrap(); |
| let RecordsRaw { bytes, context: _ } = &result; |
| assert_eq!(*bytes, &DUMMY_BYTES[..]); |
| let parsed = Records::try_from_raw(result).unwrap(); |
| validate_parsed_stateful_context_records(parsed, context); |
| } |
| |
| #[test] |
| fn raw_parse_failure() { |
| let mut context = StatefulContext::new(); |
| let mut bv = &mut &DUMMY_BYTES[0..15]; |
| let result = RecordsRaw::<_, StatefulContextRecordImpl>::parse_raw_with_mut_context( |
| &mut bv, |
| &mut context, |
| ) |
| .incomplete() |
| .unwrap(); |
| assert_eq!(result, (&DUMMY_BYTES[0..12], ())); |
| } |
| } |
| |
| /// Utilities for parsing the options formats in protocols like IPv4, TCP, and |
| /// NDP. |
| /// |
| /// This module provides parsing utilities for [type-length-value]-like records |
| /// encoding like those used by the options in an IPv4 or TCP header or an NDP |
| /// packet. These formats are not identical, but share enough in common that the |
| /// utilities provided here only need a small amount of customization by the |
| /// user to be fully functional. |
| /// |
| /// [type-length-value]: https://en.wikipedia.org/wiki/Type-length-value |
| pub mod options { |
| use core::convert::TryFrom; |
| use core::mem; |
| use core::num::{NonZeroUsize, TryFromIntError}; |
| |
| use nonzero_ext::nonzero; |
| use zerocopy::{byteorder::ByteOrder, AsBytes, FromBytes, Unaligned}; |
| |
| use super::*; |
| |
| /// A parsed sequence of options. |
| /// |
| /// `Options` represents a parsed sequence of options, for example from an |
| /// IPv4 or TCP header or an NDP packet. `Options` uses [`Records`] under |
| /// the hood. |
| /// |
| /// [`Records`]: crate::records::Records |
| pub type Options<B, O> = Records<B, OptionsImplBridge<O>>; |
| |
| /// A not-yet-parsed sequence of options. |
| /// |
| /// `OptionsRaw` represents a not-yet-parsed and not-yet-validated sequence |
| /// of options, for example from an IPv4 or TCP header or an NDP packet. |
| /// `OptionsRaw` uses [`RecordsRaw`] under the hood. |
| /// |
| /// [`RecordsRaw`]: crate::records::RecordsRaw |
| pub type OptionsRaw<B, O> = RecordsRaw<B, OptionsImplBridge<O>>; |
| |
| /// A builder capable of serializing a sequence of options. |
| /// |
| /// An `OptionSequenceBuilder` is instantiated with an [`Iterator`] that |
| /// provides [`OptionBuilder`]s to be serialized. The item produced by the |
| /// iterator can be any type which implements `Borrow<O>` for the |
| /// `OptionBuilder` type `O`. |
| /// |
| /// `OptionSequenceBuilder` implements [`InnerPacketBuilder`]. |
| pub type OptionSequenceBuilder<R, I> = RecordSequenceBuilder<R, I>; |
| |
| /// A builder capable of serializing a sequence of aligned options. |
| /// |
| /// An `AlignedOptionSequenceBuilder` is instantiated with an [`Iterator`] |
| /// that provides [`AlignedOptionBuilder`]s to be serialized. The item |
| /// produced by the iterator can be any type which implements `Borrow<O>` |
| /// for the `AlignedOptionBuilder` type `R`. |
| /// |
| /// `AlignedOptionSequenceBuilder` implements [`InnerPacketBuilder`]. |
| pub type AlignedOptionSequenceBuilder<R, I> = AlignedRecordSequenceBuilder<R, I>; |
| |
| /// Create a bridge to `RecordsImplLayout` and `RecordsImpl` from an `O` |
| /// that implements `OptionsImpl`. |
| /// |
| /// (Note that this doc comment is written in terms of the `Options` type |
| /// alias, but the same explanations apply to the `OptionsRaw` type alias as |
| /// well). |
| /// |
| /// The obvious solution to this problem would be to define `Options` as |
| /// follows, along with the following blanket impls: |
| /// |
| /// ```rust,ignore |
| /// pub type Options<B, O> = Records<B, O>; |
| /// |
| /// impl<O: OptionsImplLayout> RecordsImplLayout for O { ... } |
| /// |
| /// impl<'a, O: OptionsImpl<'a>> RecordsImpl<'a> for O { ... } |
| /// ``` |
| /// |
| /// Unfortunately, we also provide a similar type alias in the parent |
| /// `records` module, defining limited records parsing in terms of general |
| /// records parsing. If we were to provide both these blanket impls and the |
| /// similar blanket impls in terms of `LimitedRecordsImplLayout` and |
| /// `LimitedRecordsImpl`, we would have conflicting blanket impls. Instead, |
| /// we wrap the `OptionsImpl` type in an `OptionsImplBridge` in order to |
| /// make it a distinct concrete type and avoid the conflicting blanket impls |
| /// problem. |
| /// |
| /// Note that we could theoretically provide the blanket impl here and only |
| /// use the newtype trick in the `records` module (or vice-versa), but that |
| /// would just result in more patterns to keep track of. |
| /// |
| /// `OptionsImplBridge` is `#[doc(hidden)]`; it is only `pub` because it |
| /// appears in the type aliases `Options` and `OptionsRaw`. |
| #[derive(Copy, Clone, Debug)] |
| #[doc(hidden)] |
| pub struct OptionsImplBridge<O>(PhantomData<O>); |
| |
| impl<'a, O> RecordsImplLayout for OptionsImplBridge<O> |
| where |
| O: OptionsImpl<'a>, |
| { |
| type Context = (); |
| type Error = OptionParseErr<O::Error>; |
| } |
| |
| impl<'a, O> RecordsImpl<'a> for OptionsImplBridge<O> |
| where |
| O: OptionsImpl<'a>, |
| { |
| type Record = O::Option; |
| |
| fn parse_with_context<BV: BufferView<&'a [u8]>>( |
| data: &mut BV, |
| _context: &mut Self::Context, |
| ) -> RecordParseResult<Self::Record, Self::Error> { |
| next::<_, O>(data) |
| } |
| } |
| |
| impl<O: OptionBuilder> RecordBuilder for O { |
| fn serialized_len(&self) -> usize { |
| // TODO(https://fxbug.dev/77981): Remove this `.expect` |
| <O::Layout as OptionLayout>::LENGTH_ENCODING |
| .record_length::<<O::Layout as OptionLayout>::KindLenField>( |
| OptionBuilder::serialized_len(self), |
| ) |
| .expect("integer overflow while computing record length") |
| } |
| |
| fn serialize_into(&self, mut data: &mut [u8]) { |
| // NOTE(brunodalbo) we don't currently support serializing the two |
| // single-byte options used in TCP and IP: NOP and END_OF_OPTIONS. |
| // If it is necessary to support those as part of TLV options |
| // serialization, some changes will be required here. |
| |
| // So that `data` implements `BufferViewMut`. |
| let mut data = &mut data; |
| |
| // Data not having enough space is a contract violation, so we panic |
| // in that case. |
| *BufferView::<&mut [u8]>::take_obj_front::<<O::Layout as OptionLayout>::KindLenField>(&mut data) |
| .expect("buffer too short") = self.option_kind(); |
| let body_len = OptionBuilder::serialized_len(self); |
| // TODO(https://fxbug.dev/77981): Remove this `.expect` |
| let length = <O::Layout as OptionLayout>::LENGTH_ENCODING |
| .encode_length::<<O::Layout as OptionLayout>::KindLenField>(body_len) |
| .expect("integer overflow while encoding length"); |
| // Length overflowing `O::Layout::KindLenField` is a contract |
| // violation, so we panic in that case. |
| *BufferView::<&mut [u8]>::take_obj_front::<<O::Layout as OptionLayout>::KindLenField>(&mut data) |
| .expect("buffer too short") = length; |
| // SECURITY: Because padding may have occurred, we zero-fill data |
| // before passing it along in order to prevent leaking information |
| // from packets previously stored in the buffer. |
| let data = data.into_rest_zero(); |
| // Pass exactly `body_len` bytes even if there is padding. |
| OptionBuilder::serialize_into(self, &mut data[..body_len]); |
| } |
| } |
| |
| impl<O: AlignedOptionBuilder> AlignedRecordBuilder for O { |
| fn alignment_requirement(&self) -> (usize, usize) { |
| // Use the underlying option's alignment requirement as the |
| // alignment requirement for the record. |
| AlignedOptionBuilder::alignment_requirement(self) |
| } |
| |
| fn serialize_padding(buf: &mut [u8], length: usize) { |
| <O as AlignedOptionBuilder>::serialize_padding(buf, length); |
| } |
| } |
| |
| /// Errors returned from parsing options. |
| /// |
| /// `OptionParseErr` is either `Internal`, which indicates that this module |
| /// encountered a malformed sequence of options (likely with a length field |
| /// larger than the remaining bytes in the options buffer), or `External`, |
| /// which indicates that the [`OptionsImpl::parse`] callback returned an |
| /// error. |
| #[derive(Debug, Eq, PartialEq)] |
| pub enum OptionParseErr<E> { |
| Internal, |
| External(E), |
| } |
| |
| /// Whether the length field of an option encodes the length of the entire |
| /// option (including kind and length fields) or only of the value field. |
| /// |
| /// For the `TypeLengthValue` variant, an `option_len_multiplier` may also |
| /// be specified. Some formats (such as NDP) do not directly encode the |
| /// length in bytes of each option, but instead encode a number which must |
| /// be multiplied by `option_len_multiplier` in order to get the length in |
| /// bytes. |
| #[derive(Copy, Clone, Eq, PartialEq)] |
| pub enum LengthEncoding { |
| TypeLengthValue { option_len_multiplier: NonZeroUsize }, |
| ValueOnly, |
| } |
| |
| impl LengthEncoding { |
| /// Computes the length of an entire option record - including kind and |
| /// length fields - from the length of an option body. |
| /// |
| /// `record_length` takes into account the length of the kind and length |
| /// fields and also adds any padding required to reach a multiple of |
| /// `option_len_multiplier`, returning `None` if the value cannot be |
| /// stored in a `usize`. |
| fn record_length<F: KindLenField>(self, option_body_len: usize) -> Option<usize> { |
| let unpadded_len = option_body_len.checked_add(2 * mem::size_of::<F>())?; |
| match self { |
| LengthEncoding::TypeLengthValue { option_len_multiplier } => { |
| round_up(unpadded_len, option_len_multiplier) |
| } |
| LengthEncoding::ValueOnly => Some(unpadded_len), |
| } |
| } |
| |
| /// Encodes the length of an option's body. |
| /// |
| /// `option_body_len` is the length in bytes of the body option as |
| /// returned from [`OptionsSerializerImpl::option_length`]. This value |
| /// does not include the kind, length, or padding bytes. |
| /// |
| /// `encode_length` computes the value which should be stored in the |
| /// length field, returning `None` if the value cannot be stored in an |
| /// `F`. |
| fn encode_length<F: KindLenField>(self, option_body_len: usize) -> Option<F> { |
| let len = match self { |
| LengthEncoding::TypeLengthValue { option_len_multiplier } => { |
| let unpadded_len = (2 * mem::size_of::<F>()).checked_add(option_body_len)?; |
| let padded_len = round_up(unpadded_len, option_len_multiplier)?; |
| padded_len / option_len_multiplier.get() |
| } |
| LengthEncoding::ValueOnly => option_body_len, |
| }; |
| match F::try_from(len) { |
| Ok(len) => Some(len), |
| Err(TryFromIntError { .. }) => None, |
| } |
| } |
| |
| /// Decode the length of an option's body. |
| /// |
| /// `length_field` is the value of the length field. `decode_length` |
| /// computes the length of the option's body which this value encodes, |
| /// returning an error if `length_field` is invalid or if integer |
| /// overflow occurs. `length_field` is invalid if it encodes a total |
| /// length smaller than the header (specifically, if `self` is |
| /// LengthEncoding::TypeLengthValue { option_len_multiplier }` and |
| /// `length_field * option_len_multiplier < 2 * size_of::<F>()`). |
| fn decode_length<F: KindLenField>(self, length_field: F) -> Option<usize> { |
| let length_field = length_field.into(); |
| match self { |
| LengthEncoding::TypeLengthValue { option_len_multiplier } => length_field |
| .checked_mul(option_len_multiplier.get()) |
| .and_then(|product| product.checked_sub(2 * mem::size_of::<F>())), |
| LengthEncoding::ValueOnly => Some(length_field), |
| } |
| } |
| } |
| |
| /// Rounds up `x` to the next multiple of `mul` unless `x` is already a |
| /// multiple of `mul`. |
| fn round_up(x: usize, mul: NonZeroUsize) -> Option<usize> { |
| let mul = mul.get(); |
| // - Subtracting 1 can't underflow because we just added `mul`, which is |
| // at least 1, and the addition didn't overflow |
| // - Dividing by `mul` can't overflow (and can't divide by 0 because |
| // `mul` is nonzero) |
| // - Multiplying by `mul` can't overflow because division rounds down, |
| // so the result of the multiplication can't be any larger than the |
| // numerator in `(x_times_mul - 1) / mul`, which we already know |
| // didn't overflow |
| x.checked_add(mul).map(|x_times_mul| ((x_times_mul - 1) / mul) * mul) |
| } |
| |
| /// The type of the "kind" and "length" fields in an option. |
| /// |
| /// See the docs for [`OptionLayout::KindLenField`] for more information. |
| pub trait KindLenField: |
| FromBytes |
| + AsBytes |
| + Unaligned |
| + Into<usize> |
| + TryFrom<usize, Error = TryFromIntError> |
| + Eq |
| + Copy |
| + crate::sealed::Sealed |
| { |
| } |
| |
| impl crate::sealed::Sealed for u8 {} |
| impl KindLenField for u8 {} |
| impl<O: ByteOrder> crate::sealed::Sealed for zerocopy::U16<O> {} |
| impl<O: ByteOrder> KindLenField for zerocopy::U16<O> {} |
| |
| /// Information about an option's layout. |
| /// |
| /// It is recommended that this trait be implemented for an uninhabited type |
| /// since it never needs to be instantiated: |
| /// |
| /// ```rust |
| /// # use packet::records::options::{OptionLayout, LengthEncoding}; |
| /// /// A carrier for information about the layout of the IPv4 option |
| /// /// format. |
| /// /// |
| /// /// This type exists only at the type level, and does not need to be |
| /// /// constructed. |
| /// pub enum Ipv4OptionLayout {} |
| /// |
| /// impl OptionLayout for Ipv4OptionLayout { |
| /// type KindLenField = u8; |
| /// } |
| /// ``` |
| pub trait OptionLayout { |
| /// The type of the "kind" and "length" fields in an option. |
| /// |
| /// For most protocols, this is simply `u8`, as the "kind" and "length" |
| /// fields are each a single byte. For protocols which use two bytes for |
| /// these fields, this is [`zerocopy::U16`]. |
| // TODO(https://github.com/rust-lang/rust/issues/29661): Have |
| // `KindLenField` default to `u8`. |
| type KindLenField: KindLenField; |
| |
| /// The encoding of the length byte. |
| /// |
| /// Some formats (such as IPv4) use the length field to encode the |
| /// length of the entire option, including the kind and length bytes. |
| /// Other formats (such as IPv6) use the length field to encode the |
| /// length of only the value. This constant specifies which encoding is |
| /// used. |
| /// |
| /// Additionally, some formats (such as NDP) do not directly encode the |
| /// length in bytes of each option, but instead encode a number which |
| /// must be multiplied by a constant in order to get the length in |
| /// bytes. This is set using the [`TypeLengthValue`] variant's |
| /// `option_len_multiplier` field, and it defaults to 1. |
| /// |
| /// [`TypeLengthValue`]: LengthEncoding::TypeLengthValue |
| const LENGTH_ENCODING: LengthEncoding = |
| LengthEncoding::TypeLengthValue { option_len_multiplier: nonzero!(1usize) }; |
| } |
| |
| /// Information about an option's layout required in order to parse it. |
| pub trait OptionParseLayout: OptionLayout { |
| /// The type of errors that may be returned by a call to |
| /// [`OptionsImpl::parse`]. |
| type Error; |
| |
| /// The End of options type (if one exists). |
| const END_OF_OPTIONS: Option<Self::KindLenField>; |
| |
| /// The No-op type (if one exists). |
| const NOP: Option<Self::KindLenField>; |
| } |
| |
| /// An implementation of an options parser. |
| /// |
| /// `OptionsImpl` provides functions to parse fixed- and variable-length |
| /// options. It is required in order to construct an [`Options`]. |
| pub trait OptionsImpl<'a>: OptionParseLayout { |
| /// The type of an option; the output from the [`parse`] function. |
| /// |
| /// For long or variable-length data, implementers advised to make |
| /// `Option` a reference into the bytes passed to `parse`. Such a |
| /// reference will need to carry the lifetime `'a`, which is the same |
| /// lifetime that is passed to `parse`, and is also the lifetime |
| /// parameter to this trait. |
| /// |
| /// [`parse`]: crate::records::options::OptionsImpl::parse |
| type Option; |
| |
| /// Parses an option. |
| /// |
| /// `parse` takes a kind byte and variable-length data and returns |
| /// `Ok(Some(o))` if the option successfully parsed as `o`, `Ok(None)` |
| /// if the kind byte was unrecognized, and `Err(err)` if the kind byte |
| /// was recognized but `data` was malformed for that option kind. |
| /// |
| /// `parse` is allowed to not recognize certain option kinds, as the |
| /// length field can still be used to safely skip over them, but it must |
| /// recognize all single-byte options (if it didn't, a single-byte |
| /// option would be spuriously interpreted as a multi-byte option, and |
| /// the first byte of the next option byte would be spuriously |
| /// interpreted as the option's length byte). |
| /// |
| /// `parse` must be deterministic, or else [`Options::parse`] cannot |
| /// guarantee that future iterations will not produce errors (and |
| /// panic). |
| /// |
| /// [`Options::parse`]: crate::records::Records::parse |
| fn parse( |
| kind: Self::KindLenField, |
| data: &'a [u8], |
| ) -> Result<Option<Self::Option>, Self::Error>; |
| } |
| |
| /// A builder capable of serializing an option. |
| /// |
| /// Given `O: OptionBuilder`, an iterator of `O` can be used with a |
| /// [`OptionSequenceBuilder`] to serialize a sequence of options. |
| pub trait OptionBuilder { |
| /// Information about the option's layout. |
| type Layout: OptionLayout; |
| |
| /// Returns the serialized length, in bytes, of `self`. |
| /// |
| /// Implementers must return the length, in bytes, of the **data*** |
| /// portion of the option field (not counting the kind and length |
| /// bytes). The internal machinery of options serialization takes care |
| /// of aligning options to their [`option_len_multiplier`] boundaries, |
| /// adding padding bytes if necessary. |
| /// |
| /// [`option_len_multiplier`]: LengthEncoding::TypeLengthValue::option_len_multiplier |
| fn serialized_len(&self) -> usize; |
| |
| /// Returns the wire value for this option kind. |
| fn option_kind(&self) -> <Self::Layout as OptionLayout>::KindLenField; |
| |
| /// Serializes `self` into `data`. |
| /// |
| /// `data` will be exactly `self.serialized_len()` bytes long. |
| /// Implementers must write the **data** portion of `self` into `data` |
| /// (not the kind or length fields). |
| /// |
| /// # Panics |
| /// |
| /// If `data` is not exactly `self.serialized_len()` bytes long, |
| /// `serialize` may panic. |
| fn serialize_into(&self, data: &mut [u8]); |
| } |
| |
| /// A builder capable of serializing an option with an alignment |
| /// requirement. |
| /// |
| /// Given `O: AlignedOptionBuilder`, an iterator of `O` can be used with an |
| /// [`AlignedOptionSequenceBuilder`] to serialize a sequence of aligned |
| /// options. |
| pub trait AlignedOptionBuilder: OptionBuilder { |
| /// Returns the alignment requirement of `self`. |
| /// |
| /// `option.alignment_requirement()` returns `(x, y)`, which means that |
| /// the serialized encoding of `option` must be aligned at `x * n + y` |
| /// bytes from the beginning of the options sequence for some |
| /// non-negative `n`. For example, the IPv6 Router Alert Hop-by-Hop |
| /// option has alignment (2, 0), while the Jumbo Payload option has |
| /// alignment (4, 2). (1, 0) means there is no alignment requirement. |
| /// |
| /// `x` must be non-zero and `y` must be smaller than `x`. |
| fn alignment_requirement(&self) -> (usize, usize); |
| |
| /// Serialize the padding between subsequent aligned options. |
| /// |
| /// Some formats require that padding bytes have particular content. |
| /// This function serializes padding bytes as required by the format. |
| fn serialize_padding(buf: &mut [u8], length: usize); |
| } |
| |
| fn next<'a, BV, O>(bytes: &mut BV) -> RecordParseResult<O::Option, OptionParseErr<O::Error>> |
| where |
| BV: BufferView<&'a [u8]>, |
| O: OptionsImpl<'a>, |
| { |
| // For an explanation of this format, see the "Options" section of |
| // https://en.wikipedia.org/wiki/Transmission_Control_Protocol#TCP_segment_structure |
| loop { |
| let kind = match bytes.take_obj_front::<O::KindLenField>() { |
| None => return Ok(ParsedRecord::Done), |
| Some(k) => { |
| // Can't do pattern matching with associated constants, so |
| // do it the good-ol' way: |
| if Some(*k) == O::NOP { |
| continue; |
| } else if Some(*k) == O::END_OF_OPTIONS { |
| return Ok(ParsedRecord::Done); |
| } |
| k |
| } |
| }; |
| let body_len = match bytes.take_obj_front::<O::KindLenField>() { |
| None => return Err(OptionParseErr::Internal), |
| Some(len) => O::LENGTH_ENCODING |
| .decode_length::<O::KindLenField>(*len) |
| .ok_or(OptionParseErr::Internal)?, |
| }; |
| |
| let option_data = bytes.take_front(body_len).ok_or(OptionParseErr::Internal)?; |
| match O::parse(*kind, option_data) { |
| Ok(Some(o)) => return Ok(ParsedRecord::Parsed(o)), |
| Ok(None) => {} |
| Err(err) => return Err(OptionParseErr::External(err)), |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use core::convert::{Infallible as Never, TryInto}; |
| use core::fmt::Debug; |
| |
| use nonzero_ext::nonzero; |
| |
| use super::*; |
| use crate::Serializer; |
| |
| type U16 = zerocopy::U16<zerocopy::byteorder::NetworkEndian>; |
| |
| #[derive(Debug)] |
| struct DummyOptionsImpl; |
| |
| #[derive(Debug)] |
| struct DummyOption { |
| kind: u8, |
| data: Vec<u8>, |
| } |
| |
| impl OptionLayout for DummyOptionsImpl { |
| type KindLenField = u8; |
| } |
| |
| impl OptionParseLayout for DummyOptionsImpl { |
| type Error = Never; |
| const END_OF_OPTIONS: Option<u8> = Some(0); |
| const NOP: Option<u8> = Some(1); |
| } |
| |
| impl<'a> OptionsImpl<'a> for DummyOptionsImpl { |
| type Option = DummyOption; |
| |
| fn parse(kind: u8, data: &'a [u8]) -> Result<Option<Self::Option>, Never> { |
| let mut v = Vec::new(); |
| v.extend_from_slice(data); |
| Ok(Some(DummyOption { kind, data: v })) |
| } |
| } |
| |
| impl OptionBuilder for DummyOption { |
| type Layout = DummyOptionsImpl; |
| |
| fn serialized_len(&self) -> usize { |
| self.data.len() |
| } |
| |
| fn option_kind(&self) -> u8 { |
| self.kind |
| } |
| |
| fn serialize_into(&self, data: &mut [u8]) { |
| assert_eq!(data.len(), OptionBuilder::serialized_len(self)); |
| data.copy_from_slice(&self.data); |
| } |
| } |
| |
| impl AlignedOptionBuilder for DummyOption { |
| // For our `DummyOption`, we simply regard (length, kind) as their |
| // alignment requirement. |
| fn alignment_requirement(&self) -> (usize, usize) { |
| (self.data.len(), self.kind as usize) |
| } |
| |
| fn serialize_padding(buf: &mut [u8], length: usize) { |
| assert!(length <= buf.len()); |
| assert!(length <= (std::u8::MAX as usize) + 2); |
| |
| if length == 1 { |
| // Use Pad1 |
| buf[0] = 0 |
| } else if length > 1 { |
| // Use PadN |
| buf[0] = 1; |
| buf[1] = (length - 2) as u8; |
| for i in 2..length { |
| buf[i] = 0 |
| } |
| } |
| } |
| } |
| |
| #[derive(Debug)] |
| struct AlwaysErrOptionsImpl; |
| |
| impl OptionLayout for AlwaysErrOptionsImpl { |
| type KindLenField = u8; |
| } |
| |
| impl OptionParseLayout for AlwaysErrOptionsImpl { |
| type Error = (); |
| const END_OF_OPTIONS: Option<u8> = Some(0); |
| const NOP: Option<u8> = Some(1); |
| } |
| |
| impl<'a> OptionsImpl<'a> for AlwaysErrOptionsImpl { |
| type Option = (); |
| |
| fn parse(_kind: u8, _data: &'a [u8]) -> Result<Option<()>, ()> { |
| Err(()) |
| } |
| } |
| |
| #[derive(Debug)] |
| struct DummyNdpOptionsImpl; |
| |
| #[derive(Debug)] |
| struct NdpOption { |
| kind: u8, |
| data: Vec<u8>, |
| } |
| |
| impl OptionLayout for NdpOption { |
| type KindLenField = u8; |
| |
| const LENGTH_ENCODING: LengthEncoding = |
| LengthEncoding::TypeLengthValue { option_len_multiplier: nonzero!(8usize) }; |
| } |
| |
| impl OptionLayout for DummyNdpOptionsImpl { |
| type KindLenField = u8; |
| |
| const LENGTH_ENCODING: LengthEncoding = |
| LengthEncoding::TypeLengthValue { option_len_multiplier: nonzero!(8usize) }; |
| } |
| |
| impl OptionParseLayout for DummyNdpOptionsImpl { |
| type Error = Never; |
| |
| const END_OF_OPTIONS: Option<u8> = None; |
| |
| const NOP: Option<u8> = None; |
| } |
| |
| impl<'a> OptionsImpl<'a> for DummyNdpOptionsImpl { |
| type Option = NdpOption; |
| |
| fn parse(kind: u8, data: &'a [u8]) -> Result<Option<Self::Option>, Never> { |
| let mut v = Vec::with_capacity(data.len()); |
| v.extend_from_slice(data); |
| Ok(Some(NdpOption { kind, data: v })) |
| } |
| } |
| |
| impl OptionBuilder for NdpOption { |
| type Layout = DummyNdpOptionsImpl; |
| |
| fn serialized_len(&self) -> usize { |
| self.data.len() |
| } |
| |
| fn option_kind(&self) -> u8 { |
| self.kind |
| } |
| |
| fn serialize_into(&self, data: &mut [u8]) { |
| assert_eq!(data.len(), OptionBuilder::serialized_len(self)); |
| data.copy_from_slice(&self.data) |
| } |
| } |
| |
| #[derive(Debug)] |
| struct DummyMultiByteKindOptionsImpl; |
| |
| #[derive(Debug)] |
| struct MultiByteOption { |
| kind: U16, |
| data: Vec<u8>, |
| } |
| |
| impl OptionLayout for MultiByteOption { |
| type KindLenField = U16; |
| } |
| |
| impl OptionLayout for DummyMultiByteKindOptionsImpl { |
| type KindLenField = U16; |
| } |
| |
| impl OptionParseLayout for DummyMultiByteKindOptionsImpl { |
| type Error = Never; |
| |
| const END_OF_OPTIONS: Option<U16> = None; |
| |
| const NOP: Option<U16> = None; |
| } |
| |
| impl<'a> OptionsImpl<'a> for DummyMultiByteKindOptionsImpl { |
| type Option = MultiByteOption; |
| |
| fn parse(kind: U16, data: &'a [u8]) -> Result<Option<Self::Option>, Never> { |
| let mut v = Vec::with_capacity(data.len()); |
| v.extend_from_slice(data); |
| Ok(Some(MultiByteOption { kind, data: v })) |
| } |
| } |
| |
| impl OptionBuilder for MultiByteOption { |
| type Layout = DummyMultiByteKindOptionsImpl; |
| |
| fn serialized_len(&self) -> usize { |
| self.data.len() |
| } |
| |
| fn option_kind(&self) -> U16 { |
| self.kind |
| } |
| |
| fn serialize_into(&self, data: &mut [u8]) { |
| data.copy_from_slice(&self.data) |
| } |
| } |
| |
| #[test] |
| fn test_length_encoding() { |
| const TLV_1: LengthEncoding = |
| LengthEncoding::TypeLengthValue { option_len_multiplier: nonzero!(1usize) }; |
| const TLV_2: LengthEncoding = |
| LengthEncoding::TypeLengthValue { option_len_multiplier: nonzero!(2usize) }; |
| |
| // Test LengthEncoding::record_length |
| |
| // For `ValueOnly`, `record_length` should always add 2 or 4 for the kind |
| // and length bytes, but never add padding. |
| assert_eq!(LengthEncoding::ValueOnly.record_length::<u8>(0), Some(2)); |
| assert_eq!(LengthEncoding::ValueOnly.record_length::<u8>(1), Some(3)); |
| assert_eq!(LengthEncoding::ValueOnly.record_length::<u8>(2), Some(4)); |
| assert_eq!(LengthEncoding::ValueOnly.record_length::<u8>(3), Some(5)); |
| |
| assert_eq!(LengthEncoding::ValueOnly.record_length::<U16>(0), Some(4)); |
| assert_eq!(LengthEncoding::ValueOnly.record_length::<U16>(1), Some(5)); |
| assert_eq!(LengthEncoding::ValueOnly.record_length::<U16>(2), Some(6)); |
| assert_eq!(LengthEncoding::ValueOnly.record_length::<U16>(3), Some(7)); |
| |
| // For `TypeLengthValue` with `option_len_multiplier = 1`, |
| // `record_length` should always add 2 or 4 for the kind and length |
| // bytes, but never add padding. |
| assert_eq!(TLV_1.record_length::<u8>(0), Some(2)); |
| assert_eq!(TLV_1.record_length::<u8>(1), Some(3)); |
| assert_eq!(TLV_1.record_length::<u8>(2), Some(4)); |
| assert_eq!(TLV_1.record_length::<u8>(3), Some(5)); |
| |
| assert_eq!(TLV_1.record_length::<U16>(0), Some(4)); |
| assert_eq!(TLV_1.record_length::<U16>(1), Some(5)); |
| assert_eq!(TLV_1.record_length::<U16>(2), Some(6)); |
| assert_eq!(TLV_1.record_length::<U16>(3), Some(7)); |
| |
| // For `TypeLengthValue` with `option_len_multiplier = 2`, |
| // `record_length` should always add 2 or 4 for the kind and length |
| // bytes, and add padding if necessary to reach a multiple of 2. |
| assert_eq!(TLV_2.record_length::<u8>(0), Some(2)); // (0 + 2) |
| assert_eq!(TLV_2.record_length::<u8>(1), Some(4)); // (1 + 2 + 1) |
| assert_eq!(TLV_2.record_length::<u8>(2), Some(4)); // (2 + 2) |
| assert_eq!(TLV_2.record_length::<u8>(3), Some(6)); // (3 + 2 + 1) |
| |
| assert_eq!(TLV_2.record_length::<U16>(0), Some(4)); // (0 + 4) |
| assert_eq!(TLV_2.record_length::<U16>(1), Some(6)); // (1 + 4 + 1) |
| assert_eq!(TLV_2.record_length::<U16>(2), Some(6)); // (2 + 4) |
| assert_eq!(TLV_2.record_length::<U16>(3), Some(8)); // (3 + 4 + 1) |
| |
| // Test LengthEncoding::encode_length |
| |
| fn encode_length<K: KindLenField>( |
| length_encoding: LengthEncoding, |
| option_body_len: usize, |
| ) -> Option<usize> { |
| length_encoding.encode_length::<K>(option_body_len).map(Into::into) |
| } |
| |
| // For `ValueOnly`, `encode_length` should always return the |
| // argument unmodified. |
| assert_eq!(encode_length::<u8>(LengthEncoding::ValueOnly, 0), Some(0)); |
| assert_eq!(encode_length::<u8>(LengthEncoding::ValueOnly, 1), Some(1)); |
| assert_eq!(encode_length::<u8>(LengthEncoding::ValueOnly, 2), Some(2)); |
| assert_eq!(encode_length::<u8>(LengthEncoding::ValueOnly, 3), Some(3)); |
| |
| assert_eq!(encode_length::<U16>(LengthEncoding::ValueOnly, 0), Some(0)); |
| assert_eq!(encode_length::<U16>(LengthEncoding::ValueOnly, 1), Some(1)); |
| assert_eq!(encode_length::<U16>(LengthEncoding::ValueOnly, 2), Some(2)); |
| assert_eq!(encode_length::<U16>(LengthEncoding::ValueOnly, 3), Some(3)); |
| |
| // For `TypeLengthValue` with `option_len_multiplier = 1`, |
| // `encode_length` should always add 2 or 4 for the kind and length |
| // bytes. |
| assert_eq!(encode_length::<u8>(TLV_1, 0), Some(2)); |
| assert_eq!(encode_length::<u8>(TLV_1, 1), Some(3)); |
| assert_eq!(encode_length::<u8>(TLV_1, 2), Some(4)); |
| assert_eq!(encode_length::<u8>(TLV_1, 3), Some(5)); |
| |
| assert_eq!(encode_length::<U16>(TLV_1, 0), Some(4)); |
| assert_eq!(encode_length::<U16>(TLV_1, 1), Some(5)); |
| assert_eq!(encode_length::<U16>(TLV_1, 2), Some(6)); |
| assert_eq!(encode_length::<U16>(TLV_1, 3), Some(7)); |
| |
| // For `TypeLengthValue` with `option_len_multiplier = 2`, |
| // `encode_length` should always add 2 or 4 for the kind and length |
| // bytes, add padding if necessary to reach a multiple of 2, and |
| // then divide by 2. |
| assert_eq!(encode_length::<u8>(TLV_2, 0), Some(1)); // (0 + 2) / 2 |
| assert_eq!(encode_length::<u8>(TLV_2, 1), Some(2)); // (1 + 2 + 1) / 2 |
| assert_eq!(encode_length::<u8>(TLV_2, 2), Some(2)); // (2 + 2) / 2 |
| assert_eq!(encode_length::<u8>(TLV_2, 3), Some(3)); // (3 + 2 + 1) / 2 |
| |
| assert_eq!(encode_length::<U16>(TLV_2, 0), Some(2)); // (0 + 4) / 2 |
| assert_eq!(encode_length::<U16>(TLV_2, 1), Some(3)); // (1 + 4 + 1) / 2 |
| assert_eq!(encode_length::<U16>(TLV_2, 2), Some(3)); // (2 + 4) / 2 |
| assert_eq!(encode_length::<U16>(TLV_2, 3), Some(4)); // (3 + 4 + 1) / 2 |
| |
| // Test LengthEncoding::decode_length |
| |
| fn decode_length<K: KindLenField>( |
| length_encoding: LengthEncoding, |
| length_field: usize, |
| ) -> Option<usize> { |
| length_encoding.decode_length::<K>(length_field.try_into().unwrap()) |
| } |
| |
| // For `ValueOnly`, `decode_length` should always return the |
| // argument unmodified. |
| assert_eq!(decode_length::<u8>(LengthEncoding::ValueOnly, 0), Some(0)); |
| assert_eq!(decode_length::<u8>(LengthEncoding::ValueOnly, 1), Some(1)); |
| assert_eq!(decode_length::<u8>(LengthEncoding::ValueOnly, 2), Some(2)); |
| assert_eq!(decode_length::<u8>(LengthEncoding::ValueOnly, 3), Some(3)); |
| |
| assert_eq!(decode_length::<U16>(LengthEncoding::ValueOnly, 0), Some(0)); |
| assert_eq!(decode_length::<U16>(LengthEncoding::ValueOnly, 1), Some(1)); |
| assert_eq!(decode_length::<U16>(LengthEncoding::ValueOnly, 2), Some(2)); |
| assert_eq!(decode_length::<U16>(LengthEncoding::ValueOnly, 3), Some(3)); |
| |
| // For `TypeLengthValue` with `option_len_multiplier = 1`, |
| // `decode_length` should always subtract 2 or 4 for the kind and |
| // length bytes. |
| assert_eq!(decode_length::<u8>(TLV_1, 0), None); |
| assert_eq!(decode_length::<u8>(TLV_1, 1), None); |
| assert_eq!(decode_length::<u8>(TLV_1, 2), Some(0)); |
| assert_eq!(decode_length::<u8>(TLV_1, 3), Some(1)); |
| |
| assert_eq!(decode_length::<U16>(TLV_1, 0), None); |
| assert_eq!(decode_length::<U16>(TLV_1, 1), None); |
| assert_eq!(decode_length::<U16>(TLV_1, 2), None); |
| assert_eq!(decode_length::<U16>(TLV_1, 3), None); |
| assert_eq!(decode_length::<U16>(TLV_1, 4), Some(0)); |
| assert_eq!(decode_length::<U16>(TLV_1, 5), Some(1)); |
| |
| // For `TypeLengthValue` with `option_len_multiplier = 2`, |
| // `decode_length` should always multiply by 2 or 4 and then |
| // subtract 2 for the kind and length bytes. |
| assert_eq!(decode_length::<u8>(TLV_2, 0), None); |
| assert_eq!(decode_length::<u8>(TLV_2, 1), Some(0)); |
| assert_eq!(decode_length::<u8>(TLV_2, 2), Some(2)); |
| assert_eq!(decode_length::<u8>(TLV_2, 3), Some(4)); |
| |
| assert_eq!(decode_length::<U16>(TLV_2, 0), None); |
| assert_eq!(decode_length::<U16>(TLV_2, 1), None); |
| assert_eq!(decode_length::<U16>(TLV_2, 2), Some(0)); |
| assert_eq!(decode_length::<U16>(TLV_2, 3), Some(2)); |
| |
| // Test end-to-end by creating options implementation with different |
| // length encodings. |
| |
| /// Declare a new options impl type with a custom `LENGTH_ENCODING`. |
| macro_rules! declare_options_impl { |
| ($opt:ident, $impl:ident, $encoding:expr) => { |
| #[derive(Debug)] |
| enum $impl {} |
| |
| #[derive(Debug, PartialEq)] |
| struct $opt { |
| kind: u8, |
| data: Vec<u8>, |
| } |
| |
| impl<'a> From<&'a (u8, Vec<u8>)> for $opt { |
| fn from((kind, data): &'a (u8, Vec<u8>)) -> $opt { |
| $opt { kind: *kind, data: data.clone() } |
| } |
| } |
| |
| impl OptionLayout for $opt { |
| const LENGTH_ENCODING: LengthEncoding = $encoding; |
| type KindLenField = u8; |
| } |
| |
| impl OptionLayout for $impl { |
| const LENGTH_ENCODING: LengthEncoding = $encoding; |
| type KindLenField = u8; |
| } |
| |
| impl OptionParseLayout for $impl { |
| type Error = Never; |
| const END_OF_OPTIONS: Option<u8> = Some(0); |
| const NOP: Option<u8> = Some(1); |
| } |
| |
| impl<'a> OptionsImpl<'a> for $impl { |
| type Option = $opt; |
| |
| fn parse(kind: u8, data: &'a [u8]) -> Result<Option<Self::Option>, Never> { |
| let mut v = Vec::new(); |
| v.extend_from_slice(data); |
| Ok(Some($opt { kind, data: v })) |
| } |
| } |
| |
| impl OptionBuilder for $opt { |
| type Layout = $impl; |
| |
| fn serialized_len(&self) -> usize { |
| self.data.len() |
| } |
| |
| fn option_kind(&self) -> u8 { |
| self.kind |
| } |
| |
| fn serialize_into(&self, data: &mut [u8]) { |
| assert_eq!(data.len(), OptionBuilder::serialized_len(self)); |
| data.copy_from_slice(&self.data); |
| } |
| } |
| }; |
| } |
| |
| declare_options_impl!( |
| DummyImplValueOnly, |
| DummyImplValueOnlyImpl, |
| LengthEncoding::ValueOnly |
| ); |
| declare_options_impl!(DummyImplTlv1, DummyImplTlv1Impl, TLV_1); |
| declare_options_impl!(DummyImplTlv2, DummyImplTlv2Impl, TLV_2); |
| |
| /// Tests that a given option is parsed from different byte |
| /// sequences for different options layouts. |
| /// |
| /// Since some options cannot be parsed from any byte sequence using |
| /// the `DummyImplTlv2` layout (namely, those whose lengths are not |
| /// a multiple of 2), `tlv_2` may be `None`. |
| fn test_parse( |
| (expect_kind, expect_data): (u8, Vec<u8>), |
| value_only: &[u8], |
| tlv_1: &[u8], |
| tlv_2: Option<&[u8]>, |
| ) { |
| let options = Options::<_, DummyImplValueOnlyImpl>::parse(value_only) |
| .unwrap() |
| .iter() |
| .collect::<Vec<_>>(); |
| let data = expect_data.clone(); |
| assert_eq!(options, [DummyImplValueOnly { kind: expect_kind, data }]); |
| |
| let options = Options::<_, DummyImplTlv1Impl>::parse(tlv_1) |
| .unwrap() |
| .iter() |
| .collect::<Vec<_>>(); |
| let data = expect_data.clone(); |
| assert_eq!(options, [DummyImplTlv1 { kind: expect_kind, data }]); |
| |
| if let Some(tlv_2) = tlv_2 { |
| let options = Options::<_, DummyImplTlv2Impl>::parse(tlv_2) |
| .unwrap() |
| .iter() |
| .collect::<Vec<_>>(); |
| assert_eq!(options, [DummyImplTlv2 { kind: expect_kind, data: expect_data }]); |
| } |
| } |
| |
| // 0-byte body |
| test_parse((0xFF, vec![]), &[0xFF, 0], &[0xFF, 2], Some(&[0xFF, 1])); |
| // 1-byte body |
| test_parse((0xFF, vec![0]), &[0xFF, 1, 0], &[0xFF, 3, 0], None); |
| // 2-byte body |
| test_parse( |
| (0xFF, vec![0, 1]), |
| &[0xFF, 2, 0, 1], |
| &[0xFF, 4, 0, 1], |
| Some(&[0xFF, 2, 0, 1]), |
| ); |
| // 3-byte body |
| test_parse((0xFF, vec![0, 1, 2]), &[0xFF, 3, 0, 1, 2], &[0xFF, 5, 0, 1, 2], None); |
| // 4-byte body |
| test_parse( |
| (0xFF, vec![0, 1, 2, 3]), |
| &[0xFF, 4, 0, 1, 2, 3], |
| &[0xFF, 6, 0, 1, 2, 3], |
| Some(&[0xFF, 3, 0, 1, 2, 3]), |
| ); |
| |
| /// Tests that an option can be serialized and then parsed in each |
| /// option layout. |
| /// |
| /// In some cases (when the body length is not a multiple of 2), the |
| /// `DummyImplTlv2` layout will parse a different option than was |
| /// originally serialized. In this case, `expect_tlv_2` can be used |
| /// to provide a different value to expect as the result of parsing. |
| fn test_serialize_parse(opt: (u8, Vec<u8>), expect_tlv_2: Option<(u8, Vec<u8>)>) { |
| let opts = [opt.clone()]; |
| |
| fn test_serialize_parse_inner< |
| O: OptionBuilder + Debug + PartialEq + for<'a> From<&'a (u8, Vec<u8>)>, |
| I: for<'a> OptionsImpl<'a, Error = Never, Option = O> + std::fmt::Debug, |
| >( |
| opts: &[(u8, Vec<u8>)], |
| expect: &[(u8, Vec<u8>)], |
| ) { |
| let opts = opts.iter().map(Into::into).collect::<Vec<_>>(); |
| let expect = expect.iter().map(Into::into).collect::<Vec<_>>(); |
| |
| let ser = OptionSequenceBuilder::<O, _>::new(opts.iter()); |
| let serialized = |
| ser.into_serializer().serialize_vec_outer().unwrap().as_ref().to_vec(); |
| let options = Options::<_, I>::parse(serialized.as_slice()) |
| .unwrap() |
| .iter() |
| .collect::<Vec<_>>(); |
| assert_eq!(options, expect); |
| } |
| |
| test_serialize_parse_inner::<DummyImplValueOnly, DummyImplValueOnlyImpl>( |
| &opts, &opts, |
| ); |
| test_serialize_parse_inner::<DummyImplTlv1, DummyImplTlv1Impl>(&opts, &opts); |
| let expect = if let Some(expect) = expect_tlv_2 { expect } else { opt }; |
| test_serialize_parse_inner::<DummyImplTlv2, DummyImplTlv2Impl>(&opts, &[expect]); |
| } |
| |
| // 0-byte body |
| test_serialize_parse((0xFF, vec![]), None); |
| // 1-byte body |
| test_serialize_parse((0xFF, vec![0]), Some((0xFF, vec![0, 0]))); |
| // 2-byte body |
| test_serialize_parse((0xFF, vec![0, 1]), None); |
| // 3-byte body |
| test_serialize_parse((0xFF, vec![0, 1, 2]), Some((0xFF, vec![0, 1, 2, 0]))); |
| // 4-byte body |
| test_serialize_parse((0xFF, vec![0, 1, 2, 3]), None); |
| } |
| |
| #[test] |
| fn test_empty_options() { |
| // all END_OF_OPTIONS |
| let bytes = [0; 64]; |
| let options = Options::<_, DummyOptionsImpl>::parse(&bytes[..]).unwrap(); |
| assert_eq!(options.iter().count(), 0); |
| |
| // all NOP |
| let bytes = [1; 64]; |
| let options = Options::<_, DummyOptionsImpl>::parse(&bytes[..]).unwrap(); |
| assert_eq!(options.iter().count(), 0); |
| } |
| |
| #[test] |
| fn test_parse() { |
| // Construct byte sequences in the pattern [3, 2], [4, 3, 2], [5, 4, |
| // 3, 2], etc. The second byte is the length byte, so these are all |
| // valid options (with data [], [2], [3, 2], etc). |
| let mut bytes = Vec::new(); |
| for i in 4..16 { |
| // from the user's perspective, these NOPs should be transparent |
| bytes.push(1); |
| for j in (2..i).rev() { |
| bytes.push(j); |
| } |
| // from the user's perspective, these NOPs should be transparent |
| bytes.push(1); |
| } |
| |
| let options = Options::<_, DummyOptionsImpl>::parse(bytes.as_slice()).unwrap(); |
| for (idx, DummyOption { kind, data }) in options.iter().enumerate() { |
| assert_eq!(kind as usize, idx + 3); |
| assert_eq!(data.len(), idx); |
| let mut bytes = Vec::new(); |
| for i in (2..(idx + 2)).rev() { |
| bytes.push(i as u8); |
| } |
| assert_eq!(data, bytes); |
| } |
| |
| // Test that we get no parse errors so long as |
| // AlwaysErrOptionsImpl::parse is never called. |
| // |
| // `bytes` is a sequence of NOPs. |
| let bytes = [1; 64]; |
| let options = Options::<_, AlwaysErrOptionsImpl>::parse(&bytes[..]).unwrap(); |
| assert_eq!(options.iter().count(), 0); |
| } |
| |
| #[test] |
| fn test_parse_ndp_options() { |
| let mut bytes = Vec::new(); |
| for i in 0..16 { |
| bytes.push(i); |
| // NDP uses len*8 for the actual length. |
| bytes.push(i + 1); |
| // Write remaining 6 bytes. |
| for j in 2..((i + 1) * 8) { |
| bytes.push(j) |
| } |
| } |
| |
| let options = Options::<_, DummyNdpOptionsImpl>::parse(bytes.as_slice()).unwrap(); |
| for (idx, NdpOption { kind, data }) in options.iter().enumerate() { |
| assert_eq!(kind as usize, idx); |
| assert_eq!(data.len(), ((idx + 1) * 8) - 2); |
| let mut bytes = Vec::new(); |
| for i in 2..((idx + 1) * 8) { |
| bytes.push(i as u8); |
| } |
| assert_eq!(data, bytes); |
| } |
| } |
| |
| #[test] |
| fn test_parse_err() { |
| // the length byte is too short |
| let bytes = [2, 1]; |
| assert_eq!( |
| Options::<_, DummyOptionsImpl>::parse(&bytes[..]).unwrap_err(), |
| OptionParseErr::Internal |
| ); |
| |
| // the length byte is 0 (similar check to above, but worth |
| // explicitly testing since this was a bug in the Linux kernel: |
| // https://bugzilla.redhat.com/show_bug.cgi?id=1622404) |
| let bytes = [2, 0]; |
| assert_eq!( |
| Options::<_, DummyOptionsImpl>::parse(&bytes[..]).unwrap_err(), |
| OptionParseErr::Internal |
| ); |
| |
| // the length byte is too long |
| let bytes = [2, 3]; |
| assert_eq!( |
| Options::<_, DummyOptionsImpl>::parse(&bytes[..]).unwrap_err(), |
| OptionParseErr::Internal |
| ); |
| |
| // the buffer is fine, but the implementation returns a parse error |
| let bytes = [2, 2]; |
| assert_eq!( |
| Options::<_, AlwaysErrOptionsImpl>::parse(&bytes[..]).unwrap_err(), |
| OptionParseErr::External(()) |
| ); |
| } |
| |
| #[test] |
| fn test_missing_length_bytes() { |
| // Construct a sequence with a valid record followed by an |
| // incomplete one, where `kind` is specified but `len` is missing. |
| // So we can assert that we'll fail cleanly in that case. |
| // |
| // Added as part of Change-Id |
| // Ibd46ac7384c7c5e0d74cb344b48c88876c351b1a |
| // |
| // Before the small refactor in the Change-Id above, there was a |
| // check during parsing that guaranteed that the length of the |
| // remaining buffer was >= 1, but it should've been a check for |
| // >= 2, and the case below would have caused it to panic while |
| // trying to access the length byte, which was a DoS vulnerability. |
| assert_matches::assert_matches!( |
| Options::<_, DummyOptionsImpl>::parse(&[0x03, 0x03, 0x01, 0x03][..]), |
| Err(OptionParseErr::Internal) |
| ); |
| } |
| |
| #[test] |
| fn test_parse_and_serialize() { |
| // Construct byte sequences in the pattern [3, 2], [4, 3, 2], [5, 4, |
| // 3, 2], etc. The second byte is the length byte, so these are all |
| // valid options (with data [], [2], [3, 2], etc). |
| let mut bytes = Vec::new(); |
| for i in 4..16 { |
| // from the user's perspective, these NOPs should be transparent |
| for j in (2..i).rev() { |
| bytes.push(j); |
| } |
| } |
| |
| let options = Options::<_, DummyOptionsImpl>::parse(bytes.as_slice()).unwrap(); |
| |
| let collected = options.iter().collect::<Vec<_>>(); |
| // Pass `collected.iter()` instead of `options.iter()` since we need |
| // an iterator over references, and `options.iter()` produces an |
| // iterator over values. |
| let ser = OptionSequenceBuilder::<DummyOption, _>::new(collected.iter()); |
| |
| let serialized = ser.into_serializer().serialize_vec_outer().unwrap().as_ref().to_vec(); |
| |
| assert_eq!(serialized, bytes); |
| } |
| |
| #[test] |
| fn test_parse_and_serialize_ndp() { |
| let mut bytes = Vec::new(); |
| for i in 0..16 { |
| bytes.push(i); |
| // NDP uses len*8 for the actual length. |
| bytes.push(i + 1); |
| // Write remaining 6 bytes. |
| for j in 2..((i + 1) * 8) { |
| bytes.push(j) |
| } |
| } |
| let options = Options::<_, DummyNdpOptionsImpl>::parse(bytes.as_slice()).unwrap(); |
| let collected = options.iter().collect::<Vec<_>>(); |
| // Pass `collected.iter()` instead of `options.iter()` since we need |
| // an iterator over references, and `options.iter()` produces an |
| // iterator over values. |
| let ser = OptionSequenceBuilder::<NdpOption, _>::new(collected.iter()); |
| |
| let serialized = ser.into_serializer().serialize_vec_outer().unwrap().as_ref().to_vec(); |
| |
| assert_eq!(serialized, bytes); |
| } |
| |
| #[test] |
| fn test_parse_and_serialize_multi_byte_fields() { |
| let mut bytes = Vec::new(); |
| for i in 4..16 { |
| // Push kind U16<NetworkEndian>. |
| bytes.push(0); |
| bytes.push(i); |
| // Push length U16<NetworkEndian>. |
| bytes.push(0); |
| bytes.push(i); |
| // Write `i` - 4 bytes. |
| for j in 4..i { |
| bytes.push(j); |
| } |
| } |
| |
| let options = |
| Options::<_, DummyMultiByteKindOptionsImpl>::parse(bytes.as_slice()).unwrap(); |
| for (idx, MultiByteOption { kind, data }) in options.iter().enumerate() { |
| assert_eq!(usize::from(kind), idx + 4); |
| let idx: u8 = idx.try_into().unwrap(); |
| let bytes: Vec<_> = (4..(idx + 4)).collect(); |
| assert_eq!(data, bytes); |
| } |
| |
| let collected = options.iter().collect::<Vec<_>>(); |
| // Pass `collected.iter()` instead of `options.iter()` since we need |
| // an iterator over references, and `options.iter()` produces an |
| // iterator over values. |
| let ser = OptionSequenceBuilder::<MultiByteOption, _>::new(collected.iter()); |
| let mut output = vec![0u8; ser.serialized_len()]; |
| ser.serialize_into(output.as_mut_slice()); |
| assert_eq!(output, bytes); |
| } |
| |
| #[test] |
| fn test_align_up_to() { |
| // We are doing some sort of property testing here: |
| // We generate a random alignment requirement (x, y) and a random offset `pos`. |
| // The resulting `new_pos` must: |
| // - 1. be at least as large as the original `pos`. |
| // - 2. be in form of x * n + y for some integer n. |
| // - 3. for any number in between, they shouldn't be in form of x * n + y. |
| use rand::{thread_rng, Rng}; |
| let mut rng = thread_rng(); |
| for _ in 0..100_000 { |
| let x = rng.gen_range(1usize..256); |
| let y = rng.gen_range(0..x); |
| let pos = rng.gen_range(0usize..65536); |
| let new_pos = align_up_to(pos, x, y); |
| // 1) |
| assert!(new_pos >= pos); |
| // 2) |
| assert_eq!((new_pos - y) % x, 0); |
| // 3) Note: `p` is not guaranteed to be bigger than `y`, plus `x` to avoid overflow. |
| assert!((pos..new_pos).all(|p| (p + x - y) % x != 0)) |
| } |
| } |
| |
| #[test] |
| #[rustfmt::skip] |
| fn test_aligned_dummy_options_serializer() { |
| // testing for cases: 2n+{0,1}, 3n+{1,2}, 1n+0, 4n+2 |
| let dummy_options = [ |
| // alignment requirement: 2 * n + 1, |
| // |
| DummyOption { kind: 1, data: vec![42, 42] }, |
| DummyOption { kind: 0, data: vec![42, 42] }, |
| DummyOption { kind: 1, data: vec![1, 2, 3] }, |
| DummyOption { kind: 2, data: vec![3, 2, 1] }, |
| DummyOption { kind: 0, data: vec![42] }, |
| DummyOption { kind: 2, data: vec![9, 9, 9, 9] }, |
| ]; |
| let ser = AlignedRecordSequenceBuilder::<DummyOption, _>::new( |
| 0, |
| dummy_options.iter(), |
| ); |
| assert_eq!(ser.serialized_len(), 32); |
| let mut buf = [0u8; 32]; |
| ser.serialize_into(&mut buf[..]); |
| assert_eq!( |
| &buf[..], |
| &[ |
| 0, // Pad1 padding |
| 1, 4, 42, 42, // (1, [42, 42]) starting at 2 * 0 + 1 = 3 |
| 0, // Pad1 padding |
| 0, 4, 42, 42, // (0, [42, 42]) starting at 2 * 3 + 0 = 6 |
| 1, 5, 1, 2, 3, // (1, [1, 2, 3]) starting at 3 * 2 + 1 = 7 |
| 1, 0, // PadN padding |
| 2, 5, 3, 2, 1, // (2, [3, 2, 1]) starting at 3 * 4 + 2 = 14 |
| 0, 3, 42, // (0, [42]) starting at 1 * 19 + 0 = 19 |
| 0, // PAD1 padding |
| 2, 6, 9, 9, 9, 9 // (2, [9, 9, 9, 9]) starting at 4 * 6 + 2 = 26 |
| // total length: 32 |
| ] |
| ); |
| } |
| } |
| } |