use crate::Error;
pub const PCAP_MAGIC: u32 = 0xa1b2c3d4;
pub const PCAP_MAGIC_SWAPPED: u32 = 0xd4c3b2a1;
pub const PCAP_MAGIC_NANO: u32 = 0xa1b23c4d;
pub const PCAP_MAGIC_NANO_SWAPPED: u32 = 0x4d3cb2a1;
pub const PCAPNG_BYTE_ORDER_MAGIC: u32 = 0x1a2b3c4d;
pub mod mask {
pub const IPV4_DF: u16 = 0x4000;
pub const IPV4_MF: u16 = 0x2000;
pub const IPV4_FRAG_OFFSET: u16 = 0x1fff;
pub const DNS_QR: u16 = 0x8000;
pub const DNS_OPCODE: u16 = 0x7800;
pub const DNS_RCODE: u16 = 0x000f;
}
fn read_u16(data: &[u8], offset: usize) -> Result<u16, Error> {
if data.len() < offset + 2 {
return Err(Error::truncated(offset + 2, data.len()));
}
Ok(u16::from_le_bytes([data[offset], data[offset + 1]]))
}
fn read_u32(data: &[u8], offset: usize) -> Result<u32, Error> {
if data.len() < offset + 4 {
return Err(Error::truncated(offset + 4, data.len()));
}
Ok(u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]))
}
fn read_i32(data: &[u8], offset: usize) -> Result<i32, Error> {
if data.len() < offset + 4 {
return Err(Error::truncated(offset + 4, data.len()));
}
Ok(i32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]))
}
pub mod link_type {
pub const LINKTYPE_ETHERNET: u16 = 1;
pub const LINKTYPE_RAW: u16 = 101;
pub const LINKTYPE_IPV4: u16 = 228;
pub const LINKTYPE_IPV6: u16 = 229;
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct PcapHeader {
pub magic: u32,
pub version_major: u16,
pub version_minor: u16,
pub thiszone: i32,
pub sigfigs: u32,
pub snaplen: u32,
pub network: u32,
}
#[derive(Debug, Clone, Copy)]
pub struct PcapTimestamp {
pub secs: u32,
pub usecs: u32,
}
impl PcapTimestamp {
pub fn to_ns(&self, is_nano: bool) -> u64 {
let frac = if is_nano {
self.usecs
} else {
self.usecs * 1000
};
(self.secs as u64) * 1_000_000_000 + frac as u64
}
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct PcapPacketHeader {
pub ts_sec: u32,
pub ts_usec: u32,
pub incl_len: u32,
pub orig_len: u32,
}
impl PcapHeader {
pub fn parse(input: &[u8]) -> Result<(Self, &[u8]), Error> {
if input.len() < 24 {
return Err(Error::truncated(24, input.len()));
}
let magic = read_u32(input, 0)?;
let version_major = read_u16(input, 4)?;
let version_minor = read_u16(input, 6)?;
let thiszone = read_i32(input, 8)?;
let sigfigs = read_u32(input, 12)?;
let snaplen = read_u32(input, 16)?;
let network = read_u32(input, 20)?;
let header = PcapHeader {
magic,
version_major,
version_minor,
thiszone,
sigfigs,
snaplen,
network,
};
match header.magic {
PCAP_MAGIC | PCAP_MAGIC_SWAPPED | PCAP_MAGIC_NANO | PCAP_MAGIC_NANO_SWAPPED => {}
_ => return Err(Error::InvalidMagic(header.magic)),
}
if header.version_major != 2 {
return Err(Error::InvalidVersion(header.version_major));
}
Ok((header, &input[24..]))
}
pub fn is_swapped(&self) -> bool {
matches!(self.magic, PCAP_MAGIC_SWAPPED | PCAP_MAGIC_NANO_SWAPPED)
}
pub fn is_nano(&self) -> bool {
matches!(self.magic, PCAP_MAGIC_NANO | PCAP_MAGIC_NANO_SWAPPED)
}
}
pub fn parse_packet_header(input: &[u8]) -> Result<(PcapPacketHeader, &[u8]), Error> {
if input.len() < 16 {
return Err(Error::truncated(16, input.len()));
}
let header = PcapPacketHeader {
ts_sec: read_u32(input, 0)?,
ts_usec: read_u32(input, 4)?,
incl_len: read_u32(input, 8)?,
orig_len: read_u32(input, 12)?,
};
Ok((header, &input[16..]))
}
pub fn parse_packet(input: &[u8]) -> Result<(&[u8], &[u8]), Error> {
let (header, rest) = parse_packet_header(input)?;
let data_len = header.incl_len as usize;
if rest.len() < data_len {
return Err(Error::truncated(data_len, rest.len()));
}
let (data, remaining) = rest.split_at(data_len);
Ok((data, remaining))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_pcap_header_valid() {
let data = vec![
0xd4, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, ];
let (header, rest) = PcapHeader::parse(&data).unwrap();
assert_eq!(header.magic, PCAP_MAGIC);
assert_eq!(header.version_major, 2);
assert_eq!(header.version_minor, 4);
assert_eq!(header.network, 1);
assert!(!header.is_swapped());
assert!(!header.is_nano());
assert!(rest.is_empty());
}
#[test]
fn test_parse_pcap_header_nano() {
let data = vec![
0x4d, 0x3c, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, ];
let (header, _) = PcapHeader::parse(&data).unwrap();
assert_eq!(header.magic, PCAP_MAGIC_NANO);
assert!(header.is_nano());
}
#[test]
fn test_parse_pcap_header_invalid_magic() {
let data = vec![
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, ];
let result = PcapHeader::parse(&data);
assert!(result.is_err());
}
#[test]
fn test_parse_pcap_header_truncated() {
let data = vec![0u8; 10];
let result = PcapHeader::parse(&data);
assert!(result.is_err());
}
#[test]
fn test_parse_packet_header() {
let data = vec![
0xd2, 0x02, 0x96, 0x49, 0x40, 0xe2, 0x01, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, ];
let (header, rest) = parse_packet_header(&data).unwrap();
assert_eq!(header.ts_sec, 1234567890);
assert_eq!(header.ts_usec, 123456);
assert_eq!(header.incl_len, 64);
assert_eq!(header.orig_len, 64);
assert!(rest.is_empty());
}
#[test]
fn test_parse_packet_header_truncated() {
let data = vec![0u8; 10];
let result = parse_packet_header(&data);
assert!(result.is_err());
}
#[test]
fn test_parse_packet() {
let data = vec![
0xd2, 0x02, 0x96, 0x49, 0xe8, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef,
];
let (data_out, _rest) = parse_packet(&data).unwrap();
assert_eq!(data_out, &[0xde, 0xad, 0xbe, 0xef]);
}
#[test]
fn test_parse_packet_truncated() {
let data = vec![
0xd2, 0x02, 0x96, 0x49, 0xe8, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00,
0x00, 0x10, 0x00, 0x00, 0x00, 0xde, 0xad,
];
let result = parse_packet(&data);
assert!(result.is_err());
}
}