1use std::io::{Cursor, Read, Write};
3
4use crate::{
5 Version,
6 byte_order::{Endianness, ExtendedByteOrder, WriteExt},
7 link_type::LinkType,
8 pcap::PcapParseError,
9};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
13pub enum MagicNumber {
14 #[default]
16 Microsecond,
17 Nanosecond,
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
23pub struct MagicNumberAndEndianness {
24 pub magic_number: MagicNumber,
26 pub endianness: Endianness,
28}
29
30impl TryFrom<[u8; 4]> for MagicNumberAndEndianness {
31 type Error = PcapParseError;
32
33 fn try_from(value: [u8; 4]) -> Result<Self, Self::Error> {
34 match value {
35 [0xa1, 0xb2, 0xc3, 0xd4] => Ok(Self {
36 magic_number: MagicNumber::Microsecond,
37 endianness: Endianness::BigEndian,
38 }),
39 [0xd4, 0xc3, 0xb2, 0xa1] => Ok(Self {
40 magic_number: MagicNumber::Microsecond,
41 endianness: Endianness::LittleEndian,
42 }),
43 [0xA1, 0xB2, 0x3C, 0x4D] => Ok(Self {
44 magic_number: MagicNumber::Nanosecond,
45 endianness: Endianness::BigEndian,
46 }),
47 [0x4d, 0x3c, 0xb2, 0xa1] => Ok(Self {
48 magic_number: MagicNumber::Nanosecond,
49 endianness: Endianness::LittleEndian,
50 }),
51 _ => Err(PcapParseError::InvalidMagicNumber(Some(value))),
52 }
53 }
54}
55impl From<MagicNumberAndEndianness> for [u8; 4] {
56 fn from(value: MagicNumberAndEndianness) -> Self {
57 match (value.magic_number, value.endianness) {
58 (MagicNumber::Microsecond, Endianness::LittleEndian) => [0xd4, 0xc3, 0xb2, 0xa1],
59 (MagicNumber::Microsecond, Endianness::BigEndian) => [0xa1, 0xb2, 0xc3, 0xd4],
60 (MagicNumber::Nanosecond, Endianness::LittleEndian) => [0xA1, 0xB2, 0x3C, 0x4D],
61 (MagicNumber::Nanosecond, Endianness::BigEndian) => [0x4d, 0x3c, 0xb2, 0xa1],
62 }
63 }
64}
65impl TryFrom<&[u8]> for MagicNumberAndEndianness {
66 type Error = PcapParseError;
67
68 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
69 if value.len() < 4 {
70 return Err(PcapParseError::InvalidMagicNumber(None));
71 }
72 let array: [u8; 4] = value[0..4].try_into()?;
73 Self::try_from(array)
74 }
75}
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub struct PcapFileHeader {
79 pub magic_number_and_endianness: MagicNumberAndEndianness,
81 pub version: Version,
84 pub timezone: u32,
87 pub sig_figs: u32,
90 pub snap_length: u32,
94 pub link_type: LinkType,
97}
98impl Default for PcapFileHeader {
99 fn default() -> Self {
100 Self {
101 magic_number_and_endianness: MagicNumberAndEndianness::default(),
102 version: Version::PCAP_VERSION_2_4,
103 timezone: Default::default(),
104 sig_figs: Default::default(),
105 snap_length: Default::default(),
106 link_type: Default::default(),
107 }
108 }
109}
110impl PcapFileHeader {
111 pub fn read<R: Read>(reader: &mut R) -> Result<Self, PcapParseError> {
113 let mut header = [0u8; 24];
114 reader.read_exact(&mut header)?;
115 Self::try_from(&header)
116 }
117 pub fn write<W: Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
119 let as_bytes: [u8; 24] = self.into();
120 writer.write_all(&as_bytes)?;
121 Ok(())
122 }
123}
124impl TryFrom<&[u8; 24]> for PcapFileHeader {
125 type Error = PcapParseError;
126
127 fn try_from(bytes: &[u8; 24]) -> Result<Self, Self::Error> {
128 let magic_number_and_endianness = MagicNumberAndEndianness::try_from(&bytes[0..4])?;
129
130 let version = Version::parse(&bytes[4..8], magic_number_and_endianness.endianness);
131 let timezone = magic_number_and_endianness
132 .endianness
133 .try_u32_from_bytes(&bytes[8..12])?;
134 let sig_figs = magic_number_and_endianness
135 .endianness
136 .try_u32_from_bytes(&bytes[12..16])?;
137 let snap_length = magic_number_and_endianness
138 .endianness
139 .try_u32_from_bytes(&bytes[16..20])?;
140 let link_type = LinkType::try_from(
141 magic_number_and_endianness
142 .endianness
143 .try_u32_from_bytes(&bytes[20..24])?,
144 )?;
145 Ok(Self {
146 magic_number_and_endianness,
147 version,
148 timezone,
149 sig_figs,
150 snap_length,
151 link_type,
152 })
153 }
154}
155impl<'a> From<&'a PcapFileHeader> for [u8; 24] {
156 fn from(value: &'a PcapFileHeader) -> Self {
157 let mut header = Cursor::new([0u8; 24]);
159 let magic_number: [u8; 4] = value.magic_number_and_endianness.into();
160 let _ = header.write_all(&magic_number);
161 let endianness = value.magic_number_and_endianness.endianness;
162 let _ = value.version.write(&mut header, endianness);
163 let _ = header.write_u32(value.timezone, endianness);
164 let _ = header.write_u32(value.sig_figs, endianness);
165 let _ = header.write_u32(value.snap_length, endianness);
166 let _ = header.write_u32((value.link_type as u16).into(), endianness);
167 header.into_inner()
168 }
169}
170
171impl From<PcapFileHeader> for [u8; 24] {
172 fn from(value: PcapFileHeader) -> Self {
173 (&value).into()
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180
181 #[test]
182 fn test_magic_number_and_endianness() {
183 let magic_bytes = [0xa1, 0xb2, 0xc3, 0xd4];
184 let magic = MagicNumberAndEndianness::try_from(magic_bytes).unwrap();
185 assert_eq!(magic.magic_number, MagicNumber::Microsecond);
186 assert_eq!(magic.endianness, Endianness::BigEndian);
187 }
188
189 #[test]
190 fn test_pcap_file_header_read() {
191 let file = std::fs::File::open("test_data/test.pcap").expect("Failed to open test.pcap");
192 let mut reader = std::io::BufReader::new(file);
193 let header = PcapFileHeader::read(&mut reader).expect("Failed to read PCAP header");
194 assert_eq!(
195 header.magic_number_and_endianness.magic_number,
196 MagicNumber::Microsecond
197 );
198 assert_eq!(
199 header.magic_number_and_endianness.endianness,
200 Endianness::LittleEndian
201 );
202 println!("{:?}", header);
203 }
204
205 #[test]
206 fn test_header_write() {
207 let header = PcapFileHeader {
208 magic_number_and_endianness: MagicNumberAndEndianness {
209 magic_number: MagicNumber::Microsecond,
210 endianness: Endianness::BigEndian,
211 },
212 version: Version { major: 2, minor: 6 },
213 timezone: 1,
214 sig_figs: 2,
215 snap_length: 100,
216 link_type: LinkType::Ethernet,
217 };
218
219 let as_bytes: [u8; 24] = header.into();
220
221 let from_bytes =
222 PcapFileHeader::try_from(&as_bytes).expect("Unable to parse written header");
223
224 assert_eq!(header, from_bytes)
225 }
226}