diff --git a/assets/modified-format.pcap b/assets/modified-format.pcap new file mode 100644 index 0000000..dbdaef1 Binary files /dev/null and b/assets/modified-format.pcap differ diff --git a/src/capture_pcap.rs b/src/capture_pcap.rs index 3b169eb..b64bf9f 100644 --- a/src/capture_pcap.rs +++ b/src/capture_pcap.rs @@ -3,7 +3,8 @@ use crate::capture::Capture; use crate::error::PcapError; use crate::linktype::Linktype; use crate::pcap::{ - parse_pcap_frame, parse_pcap_frame_be, parse_pcap_header, LegacyPcapBlock, PcapHeader, + parse_pcap_frame, parse_pcap_frame_be, parse_pcap_frame_modified, parse_pcap_header, + LegacyPcapBlock, PcapHeader, }; use crate::traits::PcapReaderIterator; use circular::Buffer; @@ -108,10 +109,14 @@ where Err(nom::Err::Incomplete(Needed::Size(n))) => Err(PcapError::Incomplete(n.into())), Err(nom::Err::Incomplete(Needed::Unknown)) => Err(PcapError::Incomplete(0)), }?; - let parse = if header.is_bigendian() { - parse_pcap_frame_be + let parse = if !header.is_modified_format() { + if header.is_bigendian() { + parse_pcap_frame_be + } else { + parse_pcap_frame + } } else { - parse_pcap_frame + parse_pcap_frame_modified }; // do not consume Ok(LegacyPcapReader { diff --git a/src/pcap.rs b/src/pcap.rs index 0010aad..58c6ae6 100644 --- a/src/pcap.rs +++ b/src/pcap.rs @@ -64,6 +64,10 @@ impl PcapHeader { (self.magic_number & 0xFFFF) == 0xb2a1 // works for both nanosecond and microsecond resolution timestamps } + pub fn is_modified_format(&self) -> bool { + self.magic_number == 0xa1b2_cd34 + } + pub fn is_nanosecond_precision(&self) -> bool { self.magic_number == 0xa1b2_3c4d || self.magic_number == 0x4d3c_b2a1 } @@ -131,13 +135,36 @@ pub fn parse_pcap_frame_be(i: &[u8]) -> IResult<&[u8], LegacyPcapBlock, PcapErro Ok((i, block)) } +/// Read a PCAP record header and data ("modified" pcap format) +/// +/// Each PCAP record starts with a small header, and is followed by packet data. +/// The packet data format depends on the LinkType. +pub fn parse_pcap_frame_modified(i: &[u8]) -> IResult<&[u8], LegacyPcapBlock, PcapError<&[u8]>> { + if i.len() < 24 { + return Err(nom::Err::Incomplete(nom::Needed::new(24 - i.len()))); + } + let ts_sec = u32::from_le_bytes(*array_ref4(i, 0)); + let ts_usec = u32::from_le_bytes(*array_ref4(i, 4)); + let caplen = u32::from_le_bytes(*array_ref4(i, 8)); + let origlen = u32::from_le_bytes(*array_ref4(i, 12)); + let (i, data) = take(caplen as usize)(&i[24..])?; + let block = LegacyPcapBlock { + ts_sec, + ts_usec, + caplen, + origlen, + data, + }; + Ok((i, block)) +} + /// Read the PCAP global header /// /// The global header contains the PCAP description and options pub fn parse_pcap_header(i: &[u8]) -> IResult<&[u8], PcapHeader, PcapError<&[u8]>> { let (i, magic_number) = le_u32(i)?; match magic_number { - 0xa1b2_c3d4 | 0xa1b2_3c4d => { + 0xa1b2_c3d4 | 0xa1b2_3c4d | 0xa1b2_cd34 => { let (i, version_major) = le_u16(i)?; let (i, version_minor) = le_u16(i)?; let (i, thiszone) = le_i32(i)?; diff --git a/tests/pcap.rs b/tests/pcap.rs index 18227dd..9b77f4e 100644 --- a/tests/pcap.rs +++ b/tests/pcap.rs @@ -76,3 +76,33 @@ fn test_truncated_pcap() { } } } + +#[test] +fn test_modified_format() { + let path = "assets/modified-format.pcap"; + let file = File::open(path).unwrap(); + let buffered = BufReader::new(file); + let mut num_blocks = 0; + let mut reader = LegacyPcapReader::new(65536, buffered).expect("LegacyPcapReader"); + loop { + match reader.next() { + Ok((offset, block)) => { + num_blocks += 1; + match block { + PcapBlockOwned::LegacyHeader(_) => (), + PcapBlockOwned::Legacy(b) => { + assert_eq!(b.caplen, 98); + } + PcapBlockOwned::NG(_) => panic!("unexpected NG data"), + } + reader.consume(offset); + } + Err(PcapError::Eof) => break, + Err(PcapError::Incomplete(_)) => { + reader.refill().unwrap(); + } + Err(e) => panic!("error while reading: {:?}", e), + } + } + assert_eq!(num_blocks, 2); +}