pcap_frame_parser/
pcap.rs1use nom::{
10 bytes::complete::take,
11 number::complete::{be_u32, le_u16, le_u32},
12 IResult,
13};
14
15const MAGIC_LE: u32 = 0xa1b2c3d4; const MAGIC_BE: u32 = 0xd4c3b2a1; #[derive(Debug, Clone)]
21pub struct PcapHeader {
22 pub magic: u32,
23 pub version_major: u16,
24 pub version_minor: u16,
25 pub snaplen: u32,
26 pub network: u32,
29 pub big_endian: bool,
30}
31
32#[derive(Debug, Clone)]
34pub struct RawPacket<'a> {
35 pub ts_sec: u32,
36 pub ts_usec: u32,
37 pub data: &'a [u8],
38}
39
40pub fn parse_pcap_header(input: &[u8]) -> IResult<&[u8], PcapHeader> {
42 let (input, magic) = le_u32(input)?;
43
44 let big_endian = match magic {
45 MAGIC_LE => false,
46 MAGIC_BE => true,
47 _ => {
48 return Err(nom::Err::Error(nom::error::Error::new(
49 input,
50 nom::error::ErrorKind::Tag,
51 )))
52 }
53 };
54
55 if big_endian {
56 let (input, version_major) = nom::number::complete::be_u16(input)?;
57 let (input, version_minor) = nom::number::complete::be_u16(input)?;
58 let (input, _thiszone) = be_u32(input)?;
59 let (input, _sigfigs) = be_u32(input)?;
60 let (input, snaplen) = be_u32(input)?;
61 let (input, network) = be_u32(input)?;
62 Ok((
63 input,
64 PcapHeader {
65 magic,
66 version_major,
67 version_minor,
68 snaplen,
69 network,
70 big_endian,
71 },
72 ))
73 } else {
74 let (input, version_major) = le_u16(input)?;
75 let (input, version_minor) = le_u16(input)?;
76 let (input, _thiszone) = le_u32(input)?;
77 let (input, _sigfigs) = le_u32(input)?;
78 let (input, snaplen) = le_u32(input)?;
79 let (input, network) = le_u32(input)?;
80 Ok((
81 input,
82 PcapHeader {
83 magic,
84 version_major,
85 version_minor,
86 snaplen,
87 network,
88 big_endian,
89 },
90 ))
91 }
92}
93
94pub fn parse_packet(input: &[u8], big_endian: bool) -> IResult<&[u8], RawPacket<'_>> {
96 let (input, ts_sec) = if big_endian {
97 be_u32(input)?
98 } else {
99 le_u32(input)?
100 };
101 let (input, ts_usec) = if big_endian {
102 be_u32(input)?
103 } else {
104 le_u32(input)?
105 };
106 let (input, incl_len) = if big_endian {
107 be_u32(input)?
108 } else {
109 le_u32(input)?
110 };
111 let (input, _orig_len) = if big_endian {
112 be_u32(input)?
113 } else {
114 le_u32(input)?
115 };
116 let (input, data) = take(incl_len)(input)?;
117
118 Ok((
119 input,
120 RawPacket {
121 ts_sec,
122 ts_usec,
123 data,
124 },
125 ))
126}
127
128pub fn iter_packets(mut input: &[u8]) -> Result<(PcapHeader, Vec<RawPacket<'_>>), String> {
137 let (rest, header) =
138 parse_pcap_header(input).map_err(|e| format!("failed to parse PCAP header: {e:?}"))?;
139
140 if header.network != 1 {
141 return Err(format!(
142 "unsupported link type: {} (only Ethernet/1 is supported)",
143 header.network
144 ));
145 }
146
147 input = rest;
148 let mut packets = Vec::new();
149
150 while !input.is_empty() {
151 match parse_packet(input, header.big_endian) {
152 Ok((rest, pkt)) => {
153 packets.push(pkt);
154 input = rest;
155 }
156 Err(_) => break,
157 }
158 }
159
160 Ok((header, packets))
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 fn le_u32_bytes(v: u32) -> [u8; 4] {
168 v.to_le_bytes()
169 }
170 fn le_u16_bytes(v: u16) -> [u8; 2] {
171 v.to_le_bytes()
172 }
173
174 fn build_pcap_le(frames: &[&[u8]]) -> Vec<u8> {
176 let mut buf = Vec::new();
177 buf.extend_from_slice(&le_u32_bytes(MAGIC_LE));
178 buf.extend_from_slice(&le_u16_bytes(2)); buf.extend_from_slice(&le_u16_bytes(4)); buf.extend_from_slice(&le_u32_bytes(0)); buf.extend_from_slice(&le_u32_bytes(0)); buf.extend_from_slice(&le_u32_bytes(65535)); buf.extend_from_slice(&le_u32_bytes(1)); for (i, frame) in frames.iter().enumerate() {
186 buf.extend_from_slice(&le_u32_bytes(1_700_000_000 + i as u32)); buf.extend_from_slice(&le_u32_bytes(0)); buf.extend_from_slice(&le_u32_bytes(frame.len() as u32)); buf.extend_from_slice(&le_u32_bytes(frame.len() as u32)); buf.extend_from_slice(frame);
191 }
192 buf
193 }
194
195 #[test]
196 fn parses_header_little_endian() {
197 let file = build_pcap_le(&[]);
198 let (_, header) = parse_pcap_header(&file).unwrap();
199 assert!(!header.big_endian);
200 assert_eq!(header.network, 1);
201 assert_eq!(header.snaplen, 65535);
202 }
203
204 #[test]
205 fn rejects_bad_magic() {
206 let mut file = build_pcap_le(&[]);
207 file[0] = 0xff;
208 assert!(parse_pcap_header(&file).is_err());
209 }
210
211 #[test]
212 fn iterates_multiple_packets() {
213 let frame_a = [0xAAu8; 20];
214 let frame_b = [0xBBu8; 34];
215 let file = build_pcap_le(&[&frame_a, &frame_b]);
216
217 let (header, packets) = iter_packets(&file).unwrap();
218 assert_eq!(header.network, 1);
219 assert_eq!(packets.len(), 2);
220 assert_eq!(packets[0].data, &frame_a[..]);
221 assert_eq!(packets[1].data, &frame_b[..]);
222 }
223
224 #[test]
225 fn rejects_non_ethernet_linktype() {
226 let mut file = build_pcap_le(&[]);
227 file[20..24].copy_from_slice(&le_u32_bytes(101));
230 assert!(iter_packets(&file).is_err());
231 }
232
233 #[test]
234 fn truncated_trailing_record_is_dropped_not_fatal() {
235 let frame_a = [0xCCu8; 16];
236 let mut file = build_pcap_le(&[&frame_a]);
237 file.extend_from_slice(&[0u8; 5]);
239
240 let (_, packets) = iter_packets(&file).unwrap();
241 assert_eq!(packets.len(), 1);
242 }
243}