pcap_parser/pcapng/
option.rs

1use std::borrow::Cow;
2use std::convert::TryFrom;
3
4use nom::combinator::{complete, map_parser};
5use nom::multi::many0;
6use nom::IResult;
7use nom::{bytes::streaming::take, error::ParseError};
8use rusticata_macros::{align32, newtype_enum};
9
10use crate::endianness::{PcapBE, PcapEndianness, PcapLE};
11
12#[derive(Clone, Copy, Eq, PartialEq)]
13pub struct OptionCode(pub u16);
14
15newtype_enum! {
16impl debug OptionCode {
17    EndOfOpt = 0,
18    Comment = 1,
19    ShbHardware = 2,
20    ShbOs = 3,
21    ShbUserAppl = 4,
22    IfTsresol = 9,
23    IfTsoffset = 14,
24    Custom2988 = 2988,
25    Custom2989 = 2989,
26    Custom19372 = 19372,
27    Custom19373 = 19373,
28}
29}
30
31#[derive(Debug)]
32pub struct PcapNGOption<'a> {
33    pub code: OptionCode,
34    pub len: u16,
35    pub value: Cow<'a, [u8]>,
36}
37
38impl<'a> PcapNGOption<'a> {
39    /// Return a reference to the option value, as raw bytes (not related to the `len` field)
40    #[inline]
41    pub fn value(&self) -> &[u8] {
42        self.value.as_ref()
43    }
44
45    /// Return a reference to the option value, using the `len` field to limit it, or None if length is invalid
46    pub fn as_bytes(&self) -> Option<&[u8]> {
47        let len = usize::from(self.len);
48        if len <= self.value.len() {
49            Some(&self.value[..len])
50        } else {
51            None
52        }
53    }
54
55    /// Return the option value interpreted as i32, or None
56    ///
57    /// Option data length and declared must be exactly 4 bytes
58    pub fn as_i32_le(&self) -> Option<i32> {
59        if self.len == 8 && self.value.len() == 8 {
60            <[u8; 4]>::try_from(self.value())
61                .ok()
62                .map(i32::from_le_bytes)
63        } else {
64            None
65        }
66    }
67
68    /// Return the option value interpreted as u32, or None
69    ///
70    /// Option data length and declared must be exactly 4 bytes
71    pub fn as_u32_le(&self) -> Option<u32> {
72        if self.len == 8 && self.value.len() == 8 {
73            <[u8; 4]>::try_from(self.value())
74                .ok()
75                .map(u32::from_le_bytes)
76        } else {
77            None
78        }
79    }
80
81    /// Return the option value interpreted as i64, or None
82    ///
83    /// Option data length and declared must be exactly 8 bytes
84    pub fn as_i64_le(&self) -> Option<i64> {
85        if self.len == 8 && self.value.len() == 8 {
86            <[u8; 8]>::try_from(self.value())
87                .ok()
88                .map(i64::from_le_bytes)
89        } else {
90            None
91        }
92    }
93
94    /// Return the option value interpreted as u64, or None
95    ///
96    /// Option data length and declared must be exactly 8 bytes
97    pub fn as_u64_le(&self) -> Option<u64> {
98        if self.len == 8 && self.value.len() == 8 {
99            <[u8; 8]>::try_from(self.value())
100                .ok()
101                .map(u64::from_le_bytes)
102        } else {
103            None
104        }
105    }
106}
107
108/// Parse a pcap-ng Option (little-endian)
109#[inline]
110pub fn parse_option_le<'i, E: ParseError<&'i [u8]>>(
111    i: &'i [u8],
112) -> IResult<&'i [u8], PcapNGOption, E> {
113    parse_option::<PcapLE, E>(i)
114}
115
116/// Parse a pcap-ng Option (big-endian)
117#[inline]
118pub fn parse_option_be<'i, E: ParseError<&'i [u8]>>(
119    i: &'i [u8],
120) -> IResult<&'i [u8], PcapNGOption, E> {
121    parse_option::<PcapBE, E>(i)
122}
123
124pub(crate) fn parse_option<'i, En: PcapEndianness, E: ParseError<&'i [u8]>>(
125    i: &'i [u8],
126) -> IResult<&'i [u8], PcapNGOption, E> {
127    let (i, code) = En::parse_u16(i)?;
128    let (i, len) = En::parse_u16(i)?;
129    let (i, value) = take(align32!(len as u32))(i)?;
130    let option = PcapNGOption {
131        code: OptionCode(code),
132        len,
133        value: Cow::Borrowed(value),
134    };
135    Ok((i, option))
136}
137
138pub(crate) fn opt_parse_options<'i, En: PcapEndianness, E: ParseError<&'i [u8]>>(
139    i: &'i [u8],
140    len: usize,
141    opt_offset: usize,
142) -> IResult<&'i [u8], Vec<PcapNGOption>, E> {
143    if len > opt_offset {
144        map_parser(
145            take(len - opt_offset),
146            many0(complete(parse_option::<En, E>)),
147        )(i)
148    } else {
149        Ok((i, Vec::new()))
150    }
151}