From e7ed2ef0b414fe2356986ed53958062d37151904 Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Tue, 23 Apr 2024 11:28:44 +0200 Subject: [PATCH] Refactor: split capture and reader --- src/capture.rs | 3 +- src/lib.rs | 9 +- src/pcap.rs | 4 + src/pcap/capture.rs | 126 ++++++++++++++++++++ src/{capture_pcap.rs => pcap/reader.rs} | 124 +------------------ src/pcapng.rs | 4 + src/pcapng/capture.rs | 126 ++++++++++++++++++++ src/{capture_pcapng.rs => pcapng/reader.rs} | 126 +------------------- 8 files changed, 266 insertions(+), 256 deletions(-) create mode 100644 src/pcap/capture.rs rename src/{capture_pcap.rs => pcap/reader.rs} (68%) create mode 100644 src/pcapng/capture.rs rename src/{capture_pcapng.rs => pcapng/reader.rs} (70%) diff --git a/src/capture.rs b/src/capture.rs index f4e4fe0..55ed6ed 100644 --- a/src/capture.rs +++ b/src/capture.rs @@ -1,11 +1,10 @@ use crate::blocks::PcapBlock; -use crate::capture_pcap::LegacyPcapReader; -use crate::capture_pcapng::PcapNGReader; use crate::error::PcapError; use crate::linktype::Linktype; use crate::pcap::parse_pcap_header; use crate::pcapng::parse_sectionheaderblock; use crate::traits::PcapReaderIterator; +use crate::{LegacyPcapReader, PcapNGReader}; use circular::Buffer; use nom::Needed; use std::io::Read; diff --git a/src/lib.rs b/src/lib.rs index 5f1a80a..09cc3fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -129,10 +129,12 @@ mod utils; pub use utils::{Data, MutableData}; mod blocks; +mod capture; mod endianness; mod error; mod linktype; pub use blocks::*; +pub use capture::*; pub use error::*; pub use linktype::*; @@ -143,13 +145,6 @@ pub use pcapng::*; pub mod traits; -mod capture; -mod capture_pcap; -mod capture_pcapng; -pub use capture::*; -pub use capture_pcap::*; -pub use capture_pcapng::*; - #[cfg(feature = "serialize")] #[cfg_attr(docsrs, doc(cfg(feature = "serialize")))] mod serialize; diff --git a/src/pcap.rs b/src/pcap.rs index c2d3ccc..9bb22e1 100644 --- a/src/pcap.rs +++ b/src/pcap.rs @@ -15,11 +15,15 @@ //! loop over [`parse_pcap_frame`](fn.parse_pcap_frame.html) to get the data. //! This can be used in a streaming parser. +mod capture; mod frame; mod header; +mod reader; +pub use capture::*; pub use frame::*; pub use header::*; +pub use reader::*; #[cfg(test)] pub mod tests { diff --git a/src/pcap/capture.rs b/src/pcap/capture.rs new file mode 100644 index 0000000..3b84f1c --- /dev/null +++ b/src/pcap/capture.rs @@ -0,0 +1,126 @@ +use crate::blocks::{PcapBlock, PcapBlockOwned}; +use crate::capture::Capture; +use crate::error::PcapError; +use crate::linktype::Linktype; +use crate::pcap::{parse_pcap_frame, parse_pcap_header, LegacyPcapBlock, PcapHeader}; +use nom::combinator::complete; +use nom::multi::many0; +use nom::{IResult, Needed}; +use std::fmt; + +/// Parsing iterator over legacy pcap data (requires data to be loaded into memory) +/// +/// ```rust +/// use pcap_parser::*; +/// use std::fs::File; +/// use std::io::Read; +/// +/// # let path = "assets/ntp.pcap"; +/// let mut file = File::open(path).unwrap(); +/// let mut buffer = Vec::new(); +/// file.read_to_end(&mut buffer).unwrap(); +/// let mut num_blocks = 0; +/// match LegacyPcapSlice::from_slice(&buffer) { +/// Ok(iter) => { +/// println!("Format: PCAP"); +/// for _block in iter { +/// num_blocks += 1; +/// } +/// return; +/// }, +/// _ => () +/// } +/// ``` +pub struct LegacyPcapSlice<'a> { + pub header: PcapHeader, + // remaining (unparsed) data + rem: &'a [u8], +} + +impl<'a> LegacyPcapSlice<'a> { + pub fn from_slice(i: &[u8]) -> Result>> { + let (rem, header) = parse_pcap_header(i)?; + Ok(LegacyPcapSlice { header, rem }) + } +} + +/// Iterator for LegacyPcapSlice. Returns a result so parsing errors are not +/// silently ignored +impl<'a> Iterator for LegacyPcapSlice<'a> { + type Item = Result, nom::Err>>; + + fn next(&mut self) -> Option { + if self.rem.is_empty() { + return None; + } + let r = parse_pcap_frame(self.rem).map(|(rem, b)| { + self.rem = rem; + PcapBlockOwned::from(b) + }); + Some(r) + } +} + +/// Generic interface for PCAP file access +pub struct PcapCapture<'a> { + pub header: PcapHeader, + + pub blocks: Vec>, +} + +impl<'a> PcapCapture<'a> { + pub fn from_file(i: &[u8]) -> Result> { + match parse_pcap(i) { + Ok((_, pcap)) => Ok(pcap), + Err(nom::Err::Error(e)) | Err(nom::Err::Failure(e)) => Err(e), + Err(nom::Err::Incomplete(Needed::Size(n))) => Err(PcapError::Incomplete(n.into())), + Err(nom::Err::Incomplete(Needed::Unknown)) => Err(PcapError::Incomplete(0)), + } + } +} + +impl<'a> fmt::Debug for PcapCapture<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + writeln!(f, "PcapCapture:") + } +} + +/// Iterator over `PcapCapture` +pub struct LegacyPcapIterator<'a> { + cap: &'a PcapCapture<'a>, + idx: usize, +} + +impl<'a> Iterator for LegacyPcapIterator<'a> { + type Item = PcapBlock<'a>; + + fn next(&mut self) -> Option> { + self.cap.blocks.get(self.idx).map(|b| { + self.idx += 1; + PcapBlock::from(b) + }) + } +} + +impl<'a> Capture for PcapCapture<'a> { + fn get_datalink(&self) -> Linktype { + self.header.network + } + + fn get_snaplen(&self) -> u32 { + self.header.snaplen + } + + fn iter<'b>(&'b self) -> Box + 'b> { + Box::new(LegacyPcapIterator { cap: self, idx: 0 }) + } +} + +/// Parse the entire file +/// +/// Note: this requires the file to be fully loaded to memory. +pub fn parse_pcap(i: &[u8]) -> IResult<&[u8], PcapCapture, PcapError<&[u8]>> { + let (i, header) = parse_pcap_header(i)?; + let (i, blocks) = many0(complete(parse_pcap_frame))(i)?; + Ok((i, PcapCapture { header, blocks })) +} diff --git a/src/capture_pcap.rs b/src/pcap/reader.rs similarity index 68% rename from src/capture_pcap.rs rename to src/pcap/reader.rs index 1618c93..4c1b47d 100644 --- a/src/capture_pcap.rs +++ b/src/pcap/reader.rs @@ -1,17 +1,12 @@ -use crate::blocks::{PcapBlock, PcapBlockOwned}; -use crate::capture::Capture; +use crate::blocks::PcapBlockOwned; use crate::error::PcapError; -use crate::linktype::Linktype; use crate::pcap::{ parse_pcap_frame, parse_pcap_frame_be, parse_pcap_frame_modified, parse_pcap_header, LegacyPcapBlock, PcapHeader, }; use crate::traits::PcapReaderIterator; use circular::Buffer; -use nom::combinator::complete; -use nom::multi::many0; use nom::{IResult, Needed, Offset}; -use std::fmt; use std::io::Read; /// Parsing iterator over legacy pcap data (streaming version) @@ -216,120 +211,3 @@ where self.reader_exhausted } } - -/// Parsing iterator over legacy pcap data (requires data to be loaded into memory) -/// -/// ```rust -/// use pcap_parser::*; -/// use std::fs::File; -/// use std::io::Read; -/// -/// # let path = "assets/ntp.pcap"; -/// let mut file = File::open(path).unwrap(); -/// let mut buffer = Vec::new(); -/// file.read_to_end(&mut buffer).unwrap(); -/// let mut num_blocks = 0; -/// match LegacyPcapSlice::from_slice(&buffer) { -/// Ok(iter) => { -/// println!("Format: PCAP"); -/// for _block in iter { -/// num_blocks += 1; -/// } -/// return; -/// }, -/// _ => () -/// } -/// ``` -pub struct LegacyPcapSlice<'a> { - pub header: PcapHeader, - // remaining (unparsed) data - rem: &'a [u8], -} - -impl<'a> LegacyPcapSlice<'a> { - pub fn from_slice(i: &[u8]) -> Result>> { - let (rem, header) = parse_pcap_header(i)?; - Ok(LegacyPcapSlice { header, rem }) - } -} - -/// Iterator for LegacyPcapSlice. Returns a result so parsing errors are not -/// silently ignored -impl<'a> Iterator for LegacyPcapSlice<'a> { - type Item = Result, nom::Err>>; - - fn next(&mut self) -> Option { - if self.rem.is_empty() { - return None; - } - let r = parse_pcap_frame(self.rem).map(|(rem, b)| { - self.rem = rem; - PcapBlockOwned::from(b) - }); - Some(r) - } -} - -/// Generic interface for PCAP file access -pub struct PcapCapture<'a> { - pub header: PcapHeader, - - pub blocks: Vec>, -} - -impl<'a> PcapCapture<'a> { - pub fn from_file(i: &[u8]) -> Result> { - match parse_pcap(i) { - Ok((_, pcap)) => Ok(pcap), - Err(nom::Err::Error(e)) | Err(nom::Err::Failure(e)) => Err(e), - Err(nom::Err::Incomplete(Needed::Size(n))) => Err(PcapError::Incomplete(n.into())), - Err(nom::Err::Incomplete(Needed::Unknown)) => Err(PcapError::Incomplete(0)), - } - } -} - -impl<'a> fmt::Debug for PcapCapture<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - writeln!(f, "PcapCapture:") - } -} - -/// Iterator over `PcapCapture` -pub struct LegacyPcapIterator<'a> { - cap: &'a PcapCapture<'a>, - idx: usize, -} - -impl<'a> Iterator for LegacyPcapIterator<'a> { - type Item = PcapBlock<'a>; - - fn next(&mut self) -> Option> { - self.cap.blocks.get(self.idx).map(|b| { - self.idx += 1; - PcapBlock::from(b) - }) - } -} - -impl<'a> Capture for PcapCapture<'a> { - fn get_datalink(&self) -> Linktype { - self.header.network - } - - fn get_snaplen(&self) -> u32 { - self.header.snaplen - } - - fn iter<'b>(&'b self) -> Box + 'b> { - Box::new(LegacyPcapIterator { cap: self, idx: 0 }) - } -} - -/// Parse the entire file -/// -/// Note: this requires the file to be fully loaded to memory. -pub fn parse_pcap(i: &[u8]) -> IResult<&[u8], PcapCapture, PcapError<&[u8]>> { - let (i, header) = parse_pcap_header(i)?; - let (i, blocks) = many0(complete(parse_pcap_frame))(i)?; - Ok((i, PcapCapture { header, blocks })) -} diff --git a/src/pcapng.rs b/src/pcapng.rs index e5c7803..0a03eec 100644 --- a/src/pcapng.rs +++ b/src/pcapng.rs @@ -35,14 +35,18 @@ // helpers and common modules mod block; +mod capture; mod header; mod option; +mod reader; mod section; mod time; pub use block::*; +pub use capture::*; pub use header::*; pub use option::*; +pub use reader::*; pub use section::*; pub use time::*; diff --git a/src/pcapng/capture.rs b/src/pcapng/capture.rs new file mode 100644 index 0000000..cee2ae0 --- /dev/null +++ b/src/pcapng/capture.rs @@ -0,0 +1,126 @@ +use crate::blocks::{PcapBlock, PcapBlockOwned}; +use crate::error::PcapError; +use crate::pcapng::*; +use nom::combinator::{complete, map}; +use nom::multi::many1; +use nom::{IResult, Needed}; +use std::fmt; + +#[derive(Default)] +pub(crate) struct CurrentSectionInfo { + pub(crate) big_endian: bool, +} + +/// Parsing iterator over pcap-ng data (requires data to be loaded into memory) +/// +/// ```rust +/// use pcap_parser::*; +/// use std::fs::File; +/// use std::io::Read; +/// +/// # let path = "assets/test001-le.pcapng"; +/// let mut file = File::open(path).unwrap(); +/// let mut buffer = Vec::new(); +/// file.read_to_end(&mut buffer).unwrap(); +/// let mut num_blocks = 0; +/// let capture = PcapNGSlice::from_slice(&buffer).expect("parse file"); +/// for _block in capture { +/// num_blocks += 1; +/// } +pub struct PcapNGSlice<'a> { + info: CurrentSectionInfo, + // remaining (unparsed) data + rem: &'a [u8], +} + +impl<'a> PcapNGSlice<'a> { + pub fn from_slice(i: &[u8]) -> Result>> { + // just check that first block is a valid one + let (_rem, _shb) = parse_sectionheaderblock(i)?; + let info = CurrentSectionInfo::default(); + let rem = i; + Ok(PcapNGSlice { info, rem }) + } +} + +/// Iterator for PcapNGSlice. Returns a result so parsing errors are not +/// silently ignored +impl<'a> Iterator for PcapNGSlice<'a> { + type Item = Result, nom::Err>>; + + fn next(&mut self) -> Option { + if self.rem.is_empty() { + return None; + } + let parse = if self.info.big_endian { + parse_block_be + } else { + parse_block_le + }; + let r = parse(self.rem).map(|(rem, b)| { + self.rem = rem; + if let Block::SectionHeader(ref shb) = b { + self.info.big_endian = shb.big_endian(); + } + PcapBlockOwned::from(b) + }); + Some(r) + } +} + +/// Generic interface for PCAPNG file access +pub struct PcapNGCapture<'a> { + pub sections: Vec>, +} + +impl<'a> fmt::Debug for PcapNGCapture<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + writeln!(f, "PcapNGCapture:") + } +} + +/// Iterator over `PcapNGCapture` +pub struct PcapNGCaptureIterator<'a> { + cap: &'a PcapNGCapture<'a>, + idx: usize, +} + +impl<'a> Iterator for PcapNGCaptureIterator<'a> { + type Item = PcapBlock<'a>; + + fn next(&mut self) -> Option> { + if self.cap.sections.len() != 1 { + // XXX only one section supported + unimplemented!(); + } + self.cap.sections[0].blocks.get(self.idx).map(|b| { + self.idx += 1; + PcapBlock::from(b) + }) + } +} + +impl<'a> PcapNGCapture<'a> { + pub fn from_file(i: &[u8]) -> Result> { + // XXX change return type to just an IResult + match parse_pcapng(i) { + Ok((_, pcap)) => Ok(pcap), + Err(nom::Err::Error(e)) | Err(nom::Err::Failure(e)) => Err(e), + Err(nom::Err::Incomplete(Needed::Size(n))) => Err(PcapError::Incomplete(n.into())), + Err(nom::Err::Incomplete(Needed::Unknown)) => Err(PcapError::Incomplete(0)), + } + } + + pub fn iter(&'a self) -> PcapNGCaptureIterator<'a> { + PcapNGCaptureIterator { cap: self, idx: 0 } + } +} + +/// Parse the entire file +/// +/// Note: this requires the file to be fully loaded to memory. +pub fn parse_pcapng(i: &[u8]) -> IResult<&[u8], PcapNGCapture, PcapError<&[u8]>> { + map(many1(complete(parse_section)), |sections| PcapNGCapture { + sections, + })(i) +} diff --git a/src/capture_pcapng.rs b/src/pcapng/reader.rs similarity index 70% rename from src/capture_pcapng.rs rename to src/pcapng/reader.rs index 18cf166..39cfad3 100644 --- a/src/capture_pcapng.rs +++ b/src/pcapng/reader.rs @@ -1,12 +1,9 @@ -use crate::blocks::{PcapBlock, PcapBlockOwned}; +use crate::blocks::PcapBlockOwned; use crate::error::PcapError; use crate::pcapng::*; use crate::traits::PcapReaderIterator; use circular::Buffer; -use nom::combinator::{complete, map}; -use nom::multi::many1; -use nom::{IResult, Needed, Offset}; -use std::fmt; +use nom::{Needed, Offset}; use std::io::Read; /// Parsing iterator over pcap-ng data (streaming version) @@ -231,122 +228,3 @@ where self.reader_exhausted } } - -#[derive(Default)] -pub struct CurrentSectionInfo { - big_endian: bool, -} - -/// Parsing iterator over pcap-ng data (requires data to be loaded into memory) -/// -/// ```rust -/// use pcap_parser::*; -/// use std::fs::File; -/// use std::io::Read; -/// -/// # let path = "assets/test001-le.pcapng"; -/// let mut file = File::open(path).unwrap(); -/// let mut buffer = Vec::new(); -/// file.read_to_end(&mut buffer).unwrap(); -/// let mut num_blocks = 0; -/// let capture = PcapNGSlice::from_slice(&buffer).expect("parse file"); -/// for _block in capture { -/// num_blocks += 1; -/// } -pub struct PcapNGSlice<'a> { - info: CurrentSectionInfo, - // remaining (unparsed) data - rem: &'a [u8], -} - -impl<'a> PcapNGSlice<'a> { - pub fn from_slice(i: &[u8]) -> Result>> { - // just check that first block is a valid one - let (_rem, _shb) = parse_sectionheaderblock(i)?; - let info = CurrentSectionInfo::default(); - let rem = i; - Ok(PcapNGSlice { info, rem }) - } -} - -/// Iterator for PcapNGSlice. Returns a result so parsing errors are not -/// silently ignored -impl<'a> Iterator for PcapNGSlice<'a> { - type Item = Result, nom::Err>>; - - fn next(&mut self) -> Option { - if self.rem.is_empty() { - return None; - } - let parse = if self.info.big_endian { - parse_block_be - } else { - parse_block_le - }; - let r = parse(self.rem).map(|(rem, b)| { - self.rem = rem; - if let Block::SectionHeader(ref shb) = b { - self.info.big_endian = shb.big_endian(); - } - PcapBlockOwned::from(b) - }); - Some(r) - } -} - -/// Generic interface for PCAPNG file access -pub struct PcapNGCapture<'a> { - pub sections: Vec>, -} - -impl<'a> fmt::Debug for PcapNGCapture<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - writeln!(f, "PcapNGCapture:") - } -} - -/// Iterator over `PcapNGCapture` -pub struct PcapNGCaptureIterator<'a> { - cap: &'a PcapNGCapture<'a>, - idx: usize, -} - -impl<'a> Iterator for PcapNGCaptureIterator<'a> { - type Item = PcapBlock<'a>; - - fn next(&mut self) -> Option> { - if self.cap.sections.len() != 1 { - // XXX only one section supported - unimplemented!(); - } - self.cap.sections[0].blocks.get(self.idx).map(|b| { - self.idx += 1; - PcapBlock::from(b) - }) - } -} - -impl<'a> PcapNGCapture<'a> { - pub fn from_file(i: &[u8]) -> Result> { - // XXX change return type to just an IResult - match parse_pcapng(i) { - Ok((_, pcap)) => Ok(pcap), - Err(nom::Err::Error(e)) | Err(nom::Err::Failure(e)) => Err(e), - Err(nom::Err::Incomplete(Needed::Size(n))) => Err(PcapError::Incomplete(n.into())), - Err(nom::Err::Incomplete(Needed::Unknown)) => Err(PcapError::Incomplete(0)), - } - } - - pub fn iter(&'a self) -> PcapNGCaptureIterator<'a> { - PcapNGCaptureIterator { cap: self, idx: 0 } - } -} - -/// Parse the entire file -/// -/// Note: this requires the file to be fully loaded to memory. -pub fn parse_pcapng(i: &[u8]) -> IResult<&[u8], PcapNGCapture, PcapError<&[u8]>> { - map(many1(complete(parse_section)), |sections| PcapNGCapture { - sections, - })(i) -}