Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor code #36

Merged
merged 18 commits into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pcap-parser"
version = "0.15.0"
version = "0.16.0"
description = "Parser for the PCAP/PCAPNG format"
license = "MIT/Apache-2.0"
keywords = ["pcap","pcapng","parser","nom"]
Expand Down
3 changes: 1 addition & 2 deletions src/capture.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
11 changes: 3 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! [![Crates.io Version](https://img.shields.io/crates/v/pcap-parser.svg)](https://crates.io/crates/pcap-parser)
//! [![docs.rs](https://docs.rs/pcap-parser/badge.svg)](https://docs.rs/pcap-parser)
//! [![Github CI](https://github.com/rusticata/pcap-parser/workflows/Continuous%20integration/badge.svg)](https://github.com/rusticata/pcap-parser/actions)
//! [![Minimum rustc version](https://img.shields.io/badge/rustc-1.53.0+-lightgray.svg)](#rust-version-requirements)
//! [![Minimum rustc version](https://img.shields.io/badge/rustc-1.63.0+-lightgray.svg)](#rust-version-requirements)
//!
//! # PCAP and PCAPNG parsers
//!
Expand Down Expand Up @@ -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::*;

Expand All @@ -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;
Expand Down
195 changes: 8 additions & 187 deletions src/pcap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,194 +15,15 @@
//! loop over [`parse_pcap_frame`](fn.parse_pcap_frame.html) to get the data.
//! This can be used in a streaming parser.

use crate::error::PcapError;
use crate::linktype::Linktype;
use crate::utils::array_ref4;
use nom::bytes::streaming::take;
use nom::number::streaming::{be_i32, be_u16, be_u32, le_i32, le_u16, le_u32};
use nom::IResult;
mod capture;
mod frame;
mod header;
mod reader;

/// PCAP global header
#[derive(Clone, Debug)]
pub struct PcapHeader {
/// File format and byte ordering. If equal to `0xa1b2c3d4` or `0xa1b23c4d` then the rest of
/// the file uses native byte ordering. If `0xd4c3b2a1` or `0x4d3cb2a1` (swapped), then all
/// following fields will have to be swapped too.
pub magic_number: u32,
/// Version major number (currently 2)
pub version_major: u16,
/// Version minor number (currently 4)
pub version_minor: u16,
/// The correction time in seconds between GMT (UTC) and the local timezone of the following packet header timestamps
pub thiszone: i32,
/// In theory, the accuracy of time stamps in the capture; in practice, all tools set it to 0
pub sigfigs: u32,
/// max len of captured packets, in octets
pub snaplen: u32,
/// Data link type
pub network: Linktype,
}

impl PcapHeader {
pub fn new() -> PcapHeader {
PcapHeader {
magic_number: 0xa1b2_c3d4, // native order
version_major: 2,
version_minor: 4,
thiszone: 0,
sigfigs: 0,
snaplen: 0,
network: Linktype(1), // default: LINKTYPE_ETHERNET
}
}

pub const fn size(&self) -> usize {
24
}

pub fn is_bigendian(&self) -> bool {
(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
}
}

impl Default for PcapHeader {
fn default() -> Self {
PcapHeader::new()
}
}

/// Container for network data in legacy Pcap files
#[derive(Debug)]
pub struct LegacyPcapBlock<'a> {
pub ts_sec: u32,
pub ts_usec: u32,
pub caplen: u32,
pub origlen: u32,
pub data: &'a [u8],
}

/// Read a PCAP record header and data
///
/// 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(i: &[u8]) -> IResult<&[u8], LegacyPcapBlock, PcapError<&[u8]>> {
if i.len() < 16 {
return Err(nom::Err::Incomplete(nom::Needed::new(16 - 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[16..])?;
let block = LegacyPcapBlock {
ts_sec,
ts_usec,
caplen,
origlen,
data,
};
Ok((i, block))
}

/// Read a PCAP record header and data (big-endian)
///
/// 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_be(i: &[u8]) -> IResult<&[u8], LegacyPcapBlock, PcapError<&[u8]>> {
if i.len() < 16 {
return Err(nom::Err::Incomplete(nom::Needed::new(16 - i.len())));
}
let ts_sec = u32::from_be_bytes(*array_ref4(i, 0));
let ts_usec = u32::from_be_bytes(*array_ref4(i, 4));
let caplen = u32::from_be_bytes(*array_ref4(i, 8));
let origlen = u32::from_be_bytes(*array_ref4(i, 12));
let (i, data) = take(caplen as usize)(&i[16..])?;
let block = LegacyPcapBlock {
ts_sec,
ts_usec,
caplen,
origlen,
data,
};
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_cd34 => {
let (i, version_major) = le_u16(i)?;
let (i, version_minor) = le_u16(i)?;
let (i, thiszone) = le_i32(i)?;
let (i, sigfigs) = le_u32(i)?;
let (i, snaplen) = le_u32(i)?;
let (i, network) = le_i32(i)?;
let header = PcapHeader {
magic_number,
version_major,
version_minor,
thiszone,
sigfigs,
snaplen,
network: Linktype(network),
};
Ok((i, header))
}
0xd4c3_b2a1 | 0x4d3c_b2a1 => {
let (i, version_major) = be_u16(i)?;
let (i, version_minor) = be_u16(i)?;
let (i, thiszone) = be_i32(i)?;
let (i, sigfigs) = be_u32(i)?;
let (i, snaplen) = be_u32(i)?;
let (i, network) = be_i32(i)?;
let header = PcapHeader {
magic_number,
version_major,
version_minor,
thiszone,
sigfigs,
snaplen,
network: Linktype(network),
};
Ok((i, header))
}
_ => Err(nom::Err::Error(PcapError::HeaderNotRecognized)),
}
}
pub use capture::*;
pub use frame::*;
pub use header::*;
pub use reader::*;

#[cfg(test)]
pub mod tests {
Expand Down
126 changes: 126 additions & 0 deletions src/pcap/capture.rs
Original file line number Diff line number Diff line change
@@ -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<LegacyPcapSlice, nom::Err<PcapError<&[u8]>>> {
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<PcapBlockOwned<'a>, nom::Err<PcapError<&'a [u8]>>>;

fn next(&mut self) -> Option<Self::Item> {
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<LegacyPcapBlock<'a>>,
}

impl<'a> PcapCapture<'a> {
pub fn from_file(i: &[u8]) -> Result<PcapCapture, PcapError<&[u8]>> {
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<PcapBlock<'a>> {
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<dyn Iterator<Item = PcapBlock> + '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 }))
}
Loading
Loading