1use crate::{
5 byte_order::{ByteOrder, ReadExt, WriteExt},
6 pcap_ng::pad_length_to_32_bytes,
7};
8use std::io::{Read, Write};
9use thiserror::Error;
10
11macro_rules! define_options_enum {
12 (
13 $(#[$docs:meta])*
14 enum $name:ident {
15 $(
16 $(#[$variant_docs:meta])*
17 $variant:ident = $value:literal,
18 )*
19 }
20 ) => {
21 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
22 $(#[$docs])*
23 pub enum $name {
24 $(
25 $(#[$variant_docs])*
26 $variant = $value,
27 )*
28 }
29
30 impl TryFrom<u16> for $name {
31 type Error = ();
32
33 fn try_from(value: u16) -> Result<Self, Self::Error> {
34 match value {
35 $(
36 $value => Ok(Self::$variant),
37 )*
38 _ => return Err(()),
39 }
40 }
41 }
42
43 };
44}
45pub(crate) use define_options_enum;
46define_options_enum! {
47 enum StandardOptions{
51 EndOfOpt = 0,
55 Comment = 1,
59 CustomUTF8Copied = 2988,
63 CustomBinaryCopied = 2989,
66 CustomUTF8NotCopied = 19372,
70 CustomBinaryNotCopied = 19373,
73 }
74}
75impl StandardOptions {
76 pub fn is_custom(&self) -> bool {
78 matches!(
79 self,
80 Self::CustomBinaryCopied
81 | Self::CustomBinaryNotCopied
82 | Self::CustomUTF8Copied
83 | Self::CustomUTF8NotCopied
84 )
85 }
86}
87#[derive(Debug, Clone, PartialEq, Eq)]
88pub struct BlockOption {
89 pub code: u16,
91 pub length: u16,
93 pub pen: Option<u32>,
97 pub value: Vec<u8>,
99}
100#[derive(Debug, Clone, PartialEq, Eq, Error)]
101pub enum InvalidOption {
102 #[error("Custom option requires a Private Enterprise Number (PEN)")]
103 CustomRequiresPen,
104 #[error(
105 "Option code {0} is not a custom option, but a Private Enterprise Number (PEN) was provided"
106 )]
107 UnexpectedPen(u16),
108}
109impl BlockOption {
110 pub fn new(
114 option_code: u16,
115 pen: Option<u32>,
116 option_value: impl Into<Vec<u8>>,
117 ) -> Result<Self, InvalidOption> {
118 if let Ok(option_code) = StandardOptions::try_from(option_code) {
119 if option_code.is_custom() && pen.is_none() {
120 return Err(InvalidOption::CustomRequiresPen);
121 } else if !option_code.is_custom() && pen.is_some() {
122 return Err(InvalidOption::UnexpectedPen(option_code as u16));
123 }
124 } else if pen.is_some() {
125 return Err(InvalidOption::UnexpectedPen(option_code));
126 }
127 let option_value = option_value.into();
128 let option_length = option_value.len() as u16;
129 let result = Self {
130 code: option_code,
131 length: option_length,
132 pen,
133 value: option_value,
134 };
135 Ok(result)
136 }
137 pub fn padding_length(&self) -> usize {
138 pad_length_to_32_bytes(self.length as usize) - self.length as usize
139 }
140}
141#[derive(Debug, Error)]
142pub enum OptionParseError {
143 #[error(transparent)]
144 IO(#[from] std::io::Error),
145 #[error(transparent)]
146 UnexpectedSize(#[from] crate::byte_order::UnexpectedSize),
147}
148#[derive(Debug, Clone, PartialEq, Eq, Default)]
152pub struct BlockOptions(pub Vec<BlockOption>);
153impl BlockOptions {
154 fn read_option_header<R: Read, B: ByteOrder>(
155 reader: &mut R,
156 byte_order: B,
157 ) -> Result<Option<(u16, u16, Option<u32>)>, OptionParseError> {
158 let option_code = reader.read_u16(byte_order)?;
159 let option_length = reader.read_u16(byte_order)?;
160
161 if option_code == 0 && option_length == 0 {
162 return Ok(None); }
164
165 let pen = match StandardOptions::try_from(option_code) {
166 Ok(option) if option.is_custom() => Some(reader.read_u32(byte_order)?),
167 _ => None, };
169 Ok(Some((option_code, option_length, pen)))
170 }
171
172 pub fn read_in<R: Read, B: ByteOrder>(
173 &mut self,
174 reader: &mut R,
175 byte_order: B,
176 ) -> Result<(), OptionParseError> {
177 loop {
178 let Some((option_code, option_length, pen)) =
179 Self::read_option_header(reader, byte_order)?
180 else {
181 break; };
183
184 let padded_length = pad_length_to_32_bytes(option_length as usize);
185 let mut option_value = vec![0u8; padded_length];
186 reader.read_exact(&mut option_value)?;
187 option_value.truncate(option_length as usize);
188
189 self.0.push(BlockOption {
190 code: option_code,
191 length: option_length,
192 pen,
193 value: option_value,
194 });
195 }
196 Ok(())
197 }
198 pub fn read<R: Read, B: ByteOrder>(
199 reader: &mut R,
200 byte_order: B,
201 ) -> Result<Self, OptionParseError> {
202 let mut options = Self::default();
203 options.read_in(reader, byte_order)?;
204 Ok(options)
205 }
206
207 pub fn read_option<R: Read, B: ByteOrder>(
208 reader: &mut R,
209 byte_order: B,
210 ) -> Result<Option<Self>, OptionParseError> {
211 let mut options = Self::default();
212 options.read_in(reader, byte_order)?;
213 if options.0.is_empty() {
214 return Ok(None);
215 }
216 Ok(Some(options))
217 }
218
219 pub fn write<W: Write>(
220 &self,
221 writer: &mut W,
222 byte_order: impl ByteOrder,
223 ) -> Result<(), std::io::Error> {
224 for option in &self.0 {
225 writer.write_u16(option.code, byte_order)?;
226 writer.write_u16(option.length, byte_order)?;
227
228 if let Some(pen) = option.pen {
229 writer.write_u32(pen, byte_order)?;
230 }
231 writer.write_all(&option.value)?;
232 let padding = option.padding_length();
234 if padding > 0 {
235 writer.write_all(&vec![0; padding])?;
236 }
237 }
238 writer.write_u16(0, byte_order)?; writer.write_u16(0, byte_order)?; Ok(())
241 }
242}
243
244#[cfg(test)]
245mod tests {
246 use super::*;
247 use crate::byte_order::LittleEndian;
248
249 #[test]
250 fn test_block_options_read_write() {
251 let option_one = BlockOption::new(1, None, b"Test comment").unwrap();
252 assert_eq!(option_one.code, 1);
253 assert_eq!(option_one.length, 12);
254 assert_eq!(option_one.value, b"Test comment");
255 assert!(option_one.pen.is_none());
256 assert_eq!(option_one.padding_length(), 0);
257
258 let option_two = BlockOption::new(2, None, b"Custom data").unwrap();
259 assert_eq!(option_two.code, 2);
260 assert_eq!(option_two.length, 11);
261 assert_eq!(option_two.value, b"Custom data");
262 assert_eq!(option_two.pen, None);
263 assert_eq!(option_two.padding_length(), 1);
264
265 let options = BlockOptions(vec![option_one, option_two]);
266
267 let mut buffer = Vec::new();
268 options.write(&mut buffer, LittleEndian).unwrap();
269 let expected_result = [
270 1, 0, 12, 0, 84, 101, 115, 116, 32, 99, 111, 109, 109, 101, 110, 116, 2, 0, 11, 0, 67,
271 117, 115, 116, 111, 109, 32, 100, 97, 116, 97, 0, 0, 0, 0, 0,
272 ];
273 assert_eq!(buffer, expected_result);
274
275 let read_options = BlockOptions::read(&mut buffer.as_slice(), LittleEndian).unwrap();
276 assert_eq!(options, read_options);
277 }
278}