1use crate::format::pcap::PCAPNG_BYTE_ORDER_MAGIC;
7use crate::Error;
8
9pub mod block_type {
11 pub const SHB: u32 = 0x0a0d0d0a;
13 pub const IDB: u32 = 0x00000001;
15 pub const PB: u32 = 0x00000002;
17 pub const SPB: u32 = 0x00000003;
19 pub const NRB: u32 = 0x00000004;
21 pub const ISB: u32 = 0x00000005;
23 pub const EPB: u32 = 0x00000006;
25 pub const CUSTOM: u32 = 0x00040000;
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum BlockType {
32 SectionHeader,
33 InterfaceDescription,
34 Packet,
35 SimplePacket,
36 NameResolution,
37 InterfaceStatistics,
38 EnhancedPacket,
39 Unknown(u32),
40}
41
42impl From<u32> for BlockType {
43 fn from(value: u32) -> Self {
44 match value {
45 block_type::SHB => BlockType::SectionHeader,
46 block_type::IDB => BlockType::InterfaceDescription,
47 block_type::PB => BlockType::Packet,
48 block_type::SPB => BlockType::SimplePacket,
49 block_type::NRB => BlockType::NameResolution,
50 block_type::ISB => BlockType::InterfaceStatistics,
51 block_type::EPB => BlockType::EnhancedPacket,
52 other => BlockType::Unknown(other),
53 }
54 }
55}
56
57fn read_u16(data: &[u8], offset: usize) -> Result<u16, Error> {
59 if data.len() < offset + 2 {
60 return Err(Error::truncated(offset + 2, data.len()));
61 }
62 Ok(u16::from_le_bytes([data[offset], data[offset + 1]]))
63}
64
65fn read_u32(data: &[u8], offset: usize) -> Result<u32, Error> {
67 if data.len() < offset + 4 {
68 return Err(Error::truncated(offset + 4, data.len()));
69 }
70 Ok(u32::from_le_bytes([
71 data[offset],
72 data[offset + 1],
73 data[offset + 2],
74 data[offset + 3],
75 ]))
76}
77
78fn read_i64(data: &[u8], offset: usize) -> Result<i64, Error> {
80 if data.len() < offset + 8 {
81 return Err(Error::truncated(offset + 8, data.len()));
82 }
83 let bytes: [u8; 8] = [
84 data[offset],
85 data[offset + 1],
86 data[offset + 2],
87 data[offset + 3],
88 data[offset + 4],
89 data[offset + 5],
90 data[offset + 6],
91 data[offset + 7],
92 ];
93 Ok(i64::from_le_bytes(bytes))
94}
95
96fn read_u64(data: &[u8], offset: usize) -> Result<u64, Error> {
98 if data.len() < offset + 8 {
99 return Err(Error::truncated(offset + 8, data.len()));
100 }
101 let bytes: [u8; 8] = [
102 data[offset],
103 data[offset + 1],
104 data[offset + 2],
105 data[offset + 3],
106 data[offset + 4],
107 data[offset + 5],
108 data[offset + 6],
109 data[offset + 7],
110 ];
111 Ok(u64::from_le_bytes(bytes))
112}
113
114#[derive(Debug, Clone)]
116pub struct SectionHeaderBlock {
117 pub byte_order_magic: u32,
119 pub version_major: u16,
121 pub version_minor: u16,
123 pub section_length: i64,
125}
126
127#[derive(Debug, Clone)]
129pub struct InterfaceDescriptionBlock {
130 pub interface_id: u16,
132 pub link_type: u16,
134 pub snap_len: u32,
136}
137
138#[derive(Debug, Clone)]
140pub struct EnhancedPacketBlock {
141 pub interface_id: u32,
143 pub timestamp_high: u32,
145 pub timestamp_low: u32,
147 pub captured_length: u32,
149 pub original_length: u32,
151 pub data: Vec<u8>,
153}
154
155#[derive(Debug, Clone)]
157pub struct InterfaceStatisticsBlock {
158 pub interface_id: u32,
160 pub timestamp_high: u32,
162 pub timestamp_low: u32,
164 pub packets_received: u64,
166 pub packets_dropped: u64,
168 pub packets_discarded: u64,
170}
171
172#[derive(Debug, Clone, PartialEq)]
174pub enum NameRecord {
175 IPv4([u8; 4], String),
177 IPv6([u8; 16], String),
179 End,
181}
182
183#[derive(Debug, Clone)]
185pub struct NameResolutionBlock {
186 pub records: Vec<NameRecord>,
188}
189
190#[derive(Debug, Clone)]
192pub enum Block {
193 SectionHeader(SectionHeaderBlock),
194 InterfaceDescription(InterfaceDescriptionBlock),
195 EnhancedPacket(EnhancedPacketBlock),
196 NameResolution(NameResolutionBlock),
197 InterfaceStatistics(InterfaceStatisticsBlock),
198 Unknown,
199}
200
201pub fn parse_block(input: &[u8], offset: usize) -> Result<(Block, usize), Error> {
203 if input.len() < 12 {
204 return Err(Error::truncated(12, input.len()));
205 }
206
207 let block_type = read_u32(input, 0)?;
209 let block_len = read_u32(input, 4)? as usize;
211
212 if block_len < 12 || block_len > input.len() {
213 return Err(Error::parse(
214 offset,
215 format!("Invalid block length: {}", block_len),
216 ));
217 }
218
219 let block_data = &input[8..block_len - 4];
220 let block = match BlockType::from(block_type) {
221 BlockType::SectionHeader => {
222 if block_data.len() < 16 {
223 return Err(Error::truncated(16, block_data.len()));
224 }
225 let byte_order_magic = read_u32(block_data, 0)?;
226 if byte_order_magic != PCAPNG_BYTE_ORDER_MAGIC {
227 return Err(Error::parse(offset, "Invalid byte-order magic".to_string()));
228 }
229 Block::SectionHeader(SectionHeaderBlock {
230 byte_order_magic,
231 version_major: read_u16(block_data, 4)?,
232 version_minor: read_u16(block_data, 6)?,
233 section_length: read_i64(block_data, 8)?,
234 })
235 }
236 BlockType::InterfaceDescription => {
237 if block_data.len() < 8 {
238 return Err(Error::truncated(8, block_data.len()));
239 }
240 Block::InterfaceDescription(InterfaceDescriptionBlock {
241 interface_id: 0, link_type: read_u16(block_data, 0)?,
243 snap_len: read_u32(block_data, 4)?,
244 })
245 }
246 BlockType::EnhancedPacket => {
247 if block_data.len() < 20 {
248 return Err(Error::truncated(20, block_data.len()));
249 }
250 let captured_len = read_u32(block_data, 12)? as usize;
251 let data_len = captured_len + (4 - captured_len % 4) % 4; if block_data.len() < 20 + data_len {
254 return Err(Error::truncated(20 + data_len, block_data.len()));
255 }
256
257 let data = block_data[20..20 + captured_len].to_vec();
259
260 Block::EnhancedPacket(EnhancedPacketBlock {
261 interface_id: read_u32(block_data, 0)?,
262 timestamp_high: read_u32(block_data, 4)?,
263 timestamp_low: read_u32(block_data, 8)?,
264 captured_length: captured_len as u32,
265 original_length: read_u32(block_data, 16)?,
266 data,
267 })
268 }
269 BlockType::NameResolution => {
270 let mut records = Vec::new();
272 let mut pos = 0;
273
274 while pos + 4 <= block_data.len() {
275 let record_type = read_u16(block_data, pos)?;
276 pos += 2;
277
278 if record_type == 0 {
279 break;
281 }
282
283 let record_len = read_u16(block_data, pos)? as usize;
284 pos += 2;
285
286 if pos + record_len > block_data.len() {
287 break;
288 }
289
290 match record_type {
291 1 => {
292 if record_len >= 4 {
294 let addr = [
295 block_data[pos],
296 block_data[pos + 1],
297 block_data[pos + 2],
298 block_data[pos + 3],
299 ];
300 let name_start = pos + 4;
301 let name_end = name_start + record_len - 4;
302 if name_end <= block_data.len() {
303 let name =
304 String::from_utf8_lossy(&block_data[name_start..name_end])
305 .trim_end_matches('\0')
306 .to_string();
307 if !name.is_empty() {
308 records.push(NameRecord::IPv4(addr, name));
309 }
310 }
311 }
312 }
313 2 => {
314 if record_len >= 16 {
316 let mut addr = [0u8; 16];
317 addr.copy_from_slice(&block_data[pos..pos + 16]);
318 let name_start = pos + 16;
319 let name_end = name_start + record_len - 16;
320 if name_end <= block_data.len() {
321 let name =
322 String::from_utf8_lossy(&block_data[name_start..name_end])
323 .trim_end_matches('\0')
324 .to_string();
325 if !name.is_empty() {
326 records.push(NameRecord::IPv6(addr, name));
327 }
328 }
329 }
330 }
331 _ => {}
332 }
333
334 let consumed = 4 + record_len;
336 pos += (consumed + 3) & !3;
337 }
338
339 Block::NameResolution(NameResolutionBlock { records })
340 }
341 BlockType::InterfaceStatistics => {
342 if block_data.len() < 24 {
344 return Err(Error::truncated(24, block_data.len()));
345 }
346
347 let interface_id = read_u32(block_data, 0)?;
348 let ts_high = read_u32(block_data, 4)?;
349 let ts_low = read_u32(block_data, 8)?;
350
351 let mut packets_received = 0u64;
353 let mut packets_dropped = 0u64;
354 let mut packets_discarded = 0u64;
355
356 if block_data.len() >= 32 {
357 packets_received = read_u64(block_data, 24)?;
358 packets_dropped = read_u64(block_data, 32)?;
359 }
360 if block_data.len() >= 40 {
361 packets_discarded = read_u64(block_data, 40)?;
362 }
363
364 Block::InterfaceStatistics(InterfaceStatisticsBlock {
365 interface_id,
366 timestamp_high: ts_high,
367 timestamp_low: ts_low,
368 packets_received,
369 packets_dropped,
370 packets_discarded,
371 })
372 }
373 BlockType::Unknown(_) | BlockType::Packet | BlockType::SimplePacket => Block::Unknown,
374 };
375
376 Ok((block, offset + block_len))
377}
378
379#[cfg(test)]
380mod tests {
381 use super::*;
382
383 #[test]
385 fn test_parse_shb() {
386 let data = vec![
388 0x0a, 0x0d, 0x0d, 0x0a, 0x1c, 0x00, 0x00, 0x00, 0x4d, 0x3c, 0x2b, 0x1a, 0x02, 0x00, 0x04, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1c, 0x00, 0x00, 0x00,
396 ];
397
398 let (block, _) = parse_block(&data, 0).unwrap();
399 match block {
400 Block::SectionHeader(shb) => {
401 assert_eq!(shb.byte_order_magic, PCAPNG_BYTE_ORDER_MAGIC);
402 assert_eq!(shb.version_major, 2);
403 assert_eq!(shb.version_minor, 4);
404 assert_eq!(shb.section_length, -1);
405 }
406 _ => panic!("Expected SectionHeader block"),
407 }
408 }
409
410 #[test]
412 fn test_parse_idb() {
413 let data = vec![
416 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00,
420 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
425 ];
426
427 assert_eq!(data.len(), 24, "IDB should be exactly 24 bytes");
428
429 let (block, _) = parse_block(&data, 0).unwrap();
430 match block {
431 Block::InterfaceDescription(idb) => {
432 assert_eq!(idb.link_type, 1);
433 assert_eq!(idb.snap_len, 65535);
434 }
435 _ => panic!("Expected InterfaceDescription block"),
436 }
437 }
438
439 #[test]
441 fn test_parse_epb() {
442 let packet_data = vec![0xde, 0xad, 0xbe, 0xef];
444 let block_len = 32 + packet_data.len(); let mut data = vec![
447 0x06, 0x00, 0x00, 0x00,
449 ];
451 data.extend_from_slice(&(block_len as u32).to_le_bytes());
452
453 data.extend_from_slice(&0u32.to_le_bytes());
455 data.extend_from_slice(&1000u32.to_le_bytes());
457 data.extend_from_slice(&500u32.to_le_bytes());
459 data.extend_from_slice(&(packet_data.len() as u32).to_le_bytes());
461 data.extend_from_slice(&(packet_data.len() as u32).to_le_bytes());
463 data.extend_from_slice(&packet_data);
465 #[allow(clippy::manual_is_multiple_of)]
467 while data.len() % 4 != 0 {
468 data.push(0);
469 }
470 data.extend_from_slice(&(block_len as u32).to_le_bytes());
472
473 let (block, _) = parse_block(&data, 0).unwrap();
474 match block {
475 Block::EnhancedPacket(epb) => {
476 assert_eq!(epb.interface_id, 0);
477 assert_eq!(epb.timestamp_high, 1000);
478 assert_eq!(epb.timestamp_low, 500);
479 assert_eq!(epb.captured_length, 4);
480 assert_eq!(epb.original_length, 4);
481 assert_eq!(&epb.data, &packet_data);
482 }
483 _ => panic!("Expected EnhancedPacket block"),
484 }
485 }
486
487 #[test]
489 fn test_parse_truncated() {
490 let data = vec![0u8; 8]; let result = parse_block(&data, 0);
493 assert!(result.is_err());
494 }
495
496 #[test]
498 fn test_parse_invalid_block_length() {
499 let data = vec![
500 0x0a, 0x0d, 0x0d, 0x0a, 0x00, 0x00, 0x00, 0x00, ];
503
504 let result = parse_block(&data, 0);
505 assert!(result.is_err());
506 }
507
508 #[test]
510 fn test_parse_invalid_byte_order() {
511 let data = vec![
512 0x0a, 0x0d, 0x0d, 0x0a, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, ];
519
520 let result = parse_block(&data, 0);
521 assert!(result.is_err());
522 }
523}