blob: 5969777e97b95b37785ea9ae6a12eb343b0d9993 [file] [log] [blame]
// 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
]
);
}
}
}