1use std::borrow::Cow;
2use std::convert::TryFrom;
3use std::fmt;
4
5use nom::combinator::{complete, map_parser};
6use nom::multi::many0;
7use nom::{bytes::streaming::take, error::ParseError};
8use nom::{IResult, Parser as _};
9use rusticata_macros::{align32, newtype_enum};
10
11use crate::endianness::{PcapBE, PcapEndianness, PcapLE};
12
13#[derive(Clone, Copy, Eq, PartialEq)]
14pub struct OptionCode(pub u16);
15
16newtype_enum! {
17impl debug OptionCode {
18 EndOfOpt = 0,
19 Comment = 1,
20 ShbHardware = 2,
21 IfName = 2,
22 IsbStartTime = 2,
23 ShbOs = 3,
24 IfDescription = 3,
25 IsbEndTime = 3,
26 ShbUserAppl = 4,
27 IfIpv4Addr = 4,
28 IsbIfRecv = 4,
29 IsbIfDrop = 5,
30 IfMacAddr = 6,
31 IsbFilterAccept = 6,
32 IfEuiAddr = 7,
33 IsbOsDrop = 7,
34 IfSpeed = 8,
35 IsbUsrDeliv = 8,
36 IfTsresol = 9,
37 IfFilter = 11,
38 IfOs = 12,
39 IfTsoffset = 14,
40 Custom2988 = 2988,
41 Custom2989 = 2989,
42 Custom19372 = 19372,
43 Custom19373 = 19373,
44}
45}
46
47#[derive(Debug, PartialEq)]
49pub enum PcapNGOptionError {
50 InvalidLength,
51 Utf8Error,
52}
53
54impl fmt::Display for PcapNGOptionError {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 match self {
57 PcapNGOptionError::InvalidLength => write!(f, "Invalid length"),
58 PcapNGOptionError::Utf8Error => write!(f, "Invalid UTF-8 string"),
59 }
60 }
61}
62
63impl std::error::Error for PcapNGOptionError {}
64
65#[derive(Debug)]
67pub struct PcapNGOption<'a> {
68 pub code: OptionCode,
73 pub len: u16,
77 pub value: Cow<'a, [u8]>,
81}
82
83impl PcapNGOption<'_> {
84 #[inline]
86 pub fn value(&self) -> &[u8] {
87 self.value.as_ref()
88 }
89
90 pub fn as_bytes(&self) -> Result<&[u8], PcapNGOptionError> {
92 let len = usize::from(self.len);
93 if len <= self.value.len() {
94 Ok(&self.value[..len])
95 } else {
96 Err(PcapNGOptionError::InvalidLength)
97 }
98 }
99
100 pub fn as_str(&self) -> Result<&str, PcapNGOptionError> {
104 self.as_bytes()
105 .and_then(|b| std::str::from_utf8(b).or(Err(PcapNGOptionError::Utf8Error)))
106 }
107
108 pub fn as_i32_le(&self) -> Result<i32, PcapNGOptionError> {
112 if self.len != 4 {
113 return Err(PcapNGOptionError::InvalidLength);
114 }
115 <[u8; 4]>::try_from(self.value())
116 .map(i32::from_le_bytes)
117 .or(Err(PcapNGOptionError::InvalidLength))
118 }
119
120 pub fn as_u32_le(&self) -> Result<u32, PcapNGOptionError> {
124 if self.len != 4 {
125 return Err(PcapNGOptionError::InvalidLength);
126 }
127 <[u8; 4]>::try_from(self.value())
128 .map(u32::from_le_bytes)
129 .or(Err(PcapNGOptionError::InvalidLength))
130 }
131
132 pub fn as_i64_le(&self) -> Result<i64, PcapNGOptionError> {
136 if self.len != 8 {
137 return Err(PcapNGOptionError::InvalidLength);
138 }
139 <[u8; 8]>::try_from(self.value())
140 .map(i64::from_le_bytes)
141 .or(Err(PcapNGOptionError::InvalidLength))
142 }
143
144 pub fn as_u64_le(&self) -> Result<u64, PcapNGOptionError> {
148 if self.len != 8 {
149 return Err(PcapNGOptionError::InvalidLength);
150 }
151 <[u8; 8]>::try_from(self.value())
152 .map(u64::from_le_bytes)
153 .or(Err(PcapNGOptionError::InvalidLength))
154 }
155}
156
157#[inline]
159pub fn parse_option_le<'i, E: ParseError<&'i [u8]>>(
160 i: &'i [u8],
161) -> IResult<&'i [u8], PcapNGOption<'i>, E> {
162 parse_option::<PcapLE, E>(i)
163}
164
165#[inline]
167pub fn parse_option_be<'i, E: ParseError<&'i [u8]>>(
168 i: &'i [u8],
169) -> IResult<&'i [u8], PcapNGOption<'i>, E> {
170 parse_option::<PcapBE, E>(i)
171}
172
173pub(crate) fn parse_option<'i, En: PcapEndianness, E: ParseError<&'i [u8]>>(
174 i: &'i [u8],
175) -> IResult<&'i [u8], PcapNGOption<'i>, E> {
176 let (i, code) = En::parse_u16(i)?;
177 let (i, len) = En::parse_u16(i)?;
178 let (i, value) = take(align32!(len as u32))(i)?;
179 let option = PcapNGOption {
180 code: OptionCode(code),
181 len,
182 value: Cow::Borrowed(value),
183 };
184 Ok((i, option))
185}
186
187pub(crate) fn opt_parse_options<'i, En: PcapEndianness, E: ParseError<&'i [u8]>>(
188 i: &'i [u8],
189 len: usize,
190 opt_offset: usize,
191) -> IResult<&'i [u8], Vec<PcapNGOption<'i>>, E> {
192 if len > opt_offset {
193 map_parser(
194 take(len - opt_offset),
195 many0(complete(parse_option::<En, E>)),
196 )
197 .parse(i)
198 } else {
199 Ok((i, Vec::new()))
200 }
201}
202
203#[inline]
204pub(crate) fn options_find_map<'a, F, O>(
205 options: &'a [PcapNGOption],
206 code: OptionCode,
207 f: F,
208) -> Option<Result<O, PcapNGOptionError>>
209where
210 F: Fn(&'a PcapNGOption) -> Result<O, PcapNGOptionError>,
211{
212 options
213 .iter()
214 .find_map(|opt| if opt.code == code { Some(f(opt)) } else { None })
215}
216
217pub(crate) fn options_get_as_bytes<'a>(
218 options: &'a [PcapNGOption],
219 code: OptionCode,
220) -> Option<Result<&'a [u8], PcapNGOptionError>> {
221 options_find_map(options, code, |opt| opt.as_bytes())
222}
223
224pub(crate) fn options_get_as_str<'a>(
225 options: &'a [PcapNGOption],
226 code: OptionCode,
227) -> Option<Result<&'a str, PcapNGOptionError>> {
228 options_find_map(options, code, |opt| opt.as_str())
229}
230
231pub(crate) fn options_get_as_u8(
232 options: &[PcapNGOption],
233 code: OptionCode,
234) -> Option<Result<u8, PcapNGOptionError>> {
235 options_find_map(options, code, |opt| {
236 let value = opt.value();
237 if opt.len == 1 && !value.is_empty() {
238 Ok(value[0])
239 } else {
240 Err(PcapNGOptionError::InvalidLength)
241 }
242 })
243}
244
245pub(crate) fn options_get_as_i64_le(
246 options: &[PcapNGOption],
247 code: OptionCode,
248) -> Option<Result<i64, PcapNGOptionError>> {
249 options_find_map(options, code, |opt| opt.as_i64_le())
250}
251
252pub(crate) fn options_get_as_u64_le(
253 options: &[PcapNGOption],
254 code: OptionCode,
255) -> Option<Result<u64, PcapNGOptionError>> {
256 options_find_map(options, code, |opt| opt.as_u64_le())
257}
258
259pub(crate) fn options_get_as_ts(
260 options: &[PcapNGOption],
261 code: OptionCode,
262) -> Option<Result<(u32, u32), PcapNGOptionError>> {
263 options_find_map(options, code, |opt| {
264 let value = opt.value();
265 if opt.len == 8 && value.len() == 8 {
266 let bytes_ts_high =
267 <[u8; 4]>::try_from(&value[..4]).or(Err(PcapNGOptionError::InvalidLength))?;
268 let bytes_ts_low =
269 <[u8; 4]>::try_from(&value[4..8]).or(Err(PcapNGOptionError::InvalidLength))?;
270 let ts_high = u32::from_le_bytes(bytes_ts_high);
271 let ts_low = u32::from_le_bytes(bytes_ts_low);
272 Ok((ts_high, ts_low))
273 } else {
274 Err(PcapNGOptionError::InvalidLength)
275 }
276 })
277}