use bytepack::Packed;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
fn set<T: Copy + Clone, F>(v: &mut T, func: F)
where F: Fn(T) -> T
{
let r = func(*v);
*v = r;
}
#[derive(Packed)]
#[repr(C,packed)]
pub struct PcapFileHeaderInFile {
pub magic_num: u32,
pub version_major: u16,
pub version_minor: u16,
pub thiszone: i32,
pub sigfigs: u32,
pub snaplen: u32,
pub network: u32,
}
const PCAP_VERSION_MAJOR: u16 = 2;
const PCAP_VERSION_MINOR: u16 = 4;
impl PcapFileHeaderInFile {
pub fn new(snaplen: usize, linktype: u32) -> Option<PcapFileHeaderInFile> {
let snap = snaplen as u32;
if snap as usize != snaplen {
None
} else {
Some(PcapFileHeaderInFile {
magic_num: PcapMagic::NanoSecondResolution.into(),
version_major: PCAP_VERSION_MAJOR,
version_minor: PCAP_VERSION_MINOR,
thiszone: 0,
sigfigs: 0,
snaplen: snap,
network: linktype,
})
}
}
}
pub struct PcapFileHeader {
pub ns_res: bool,
pub need_byte_swap: bool,
pub network: u32,
pub utc_offset: i32,
pub snaplen: usize,
}
impl PcapFileHeader {
pub fn try_from(mut header: PcapFileHeaderInFile) -> Option<Self> {
let magic = {
let magic = header.magic_num;
if let Some(m) = PcapMagic::try_from(magic) {
m
} else {
return None;
}
};
if magic.need_byte_swap() {
set(&mut header.version_major, u16::swap_bytes);
set(&mut header.version_minor, u16::swap_bytes);
set(&mut header.thiszone, i32::swap_bytes);
set(&mut header.sigfigs, u32::swap_bytes);
set(&mut header.snaplen, u32::swap_bytes);
set(&mut header.network, u32::swap_bytes);
}
let snaplen = header.snaplen as usize;
if snaplen as u32 != header.snaplen {
return None;
}
if header.version_major == PCAP_VERSION_MAJOR &&
header.version_minor == PCAP_VERSION_MINOR {
Some(PcapFileHeader {
ns_res: magic.ns_res(),
need_byte_swap: magic.need_byte_swap(),
network: header.network,
utc_offset: header.thiszone,
snaplen: snaplen,
})
} else {
None
}
}
}
#[repr(u32)]
#[allow(dead_code)]
#[derive(Copy,Clone)]
enum PcapMagic {
Normal = 0xa1b2_c3d4,
NanoSecondResolution = 0xa1b2_3c4d,
ByteSwap = 0xd4c3_b2a1,
NanoSecondResolutionByteSwap = 0x4d3c_b2a1,
}
impl PcapMagic {
fn try_from(val: u32) -> Option<PcapMagic> {
if val == PcapMagic::Normal.into() {
Some(PcapMagic::Normal)
} else if val == PcapMagic::NanoSecondResolution.into() {
Some(PcapMagic::NanoSecondResolution)
} else if val == PcapMagic::ByteSwap.into() {
Some(PcapMagic::ByteSwap)
} else if val == PcapMagic::NanoSecondResolutionByteSwap.into() {
Some(PcapMagic::NanoSecondResolutionByteSwap)
} else {
None
}
}
fn need_byte_swap(self) -> bool {
match self {
PcapMagic::Normal |
PcapMagic::NanoSecondResolution => false,
PcapMagic::ByteSwap |
PcapMagic::NanoSecondResolutionByteSwap => true,
}
}
fn ns_res(self) -> bool {
match self {
PcapMagic::Normal | PcapMagic::ByteSwap => false,
PcapMagic::NanoSecondResolution |
PcapMagic::NanoSecondResolutionByteSwap => true,
}
}
}
impl From<PcapMagic> for u32 {
fn from(val: PcapMagic) -> u32 {
val as u32
}
}
#[derive(Packed)]
#[repr(C,packed)]
pub struct PcapRecordHeader {
pub ts_sec: u32,
pub ts_usec: u32,
pub incl_len: u32,
pub orig_len: u32,
}
impl PcapRecordHeader {
pub fn swap_bytes(&mut self, file_header: &PcapFileHeader) {
if file_header.need_byte_swap {
set(&mut self.ts_sec, u32::swap_bytes);
set(&mut self.ts_usec, u32::swap_bytes);
set(&mut self.incl_len, u32::swap_bytes);
set(&mut self.orig_len, u32::swap_bytes);
}
}
pub fn get_time(&self, file_header: &PcapFileHeader) -> Option<SystemTime> {
let nsec = if file_header.ns_res {
self.ts_usec
} else {
match self.ts_usec.checked_mul(1000) {
Some(u) => u,
None => return None,
}
};
let sec = u64::from(self.ts_sec);
let sec = if file_header.utc_offset < 0 {
sec.checked_sub(file_header.utc_offset.abs() as u64)
} else {
sec.checked_add(file_header.utc_offset as u64)
};
match sec {
Some(sec) if nsec < 1_000_000_000 => Some(UNIX_EPOCH + Duration::new(sec, nsec)),
_ => None,
}
}
}
#[derive(Copy,Clone)]
#[repr(u32)]
#[allow(dead_code,non_camel_case_types)]
pub enum Linktype {
NULL = 0,
ETHERNET = 1,
AX25 = 3,
IEEE802_5 = 6,
ARCNET_BSD = 7,
SLIP = 8,
PPP = 9,
FDDI = 10,
PPP_HDLC = 50,
PPP_ETHER = 51,
ATM_RFC1483 = 100,
RAW = 101,
C_HDLC = 104,
IEEE802_11 = 105,
FRELAY = 107,
LOOP = 108,
LINUX_SLL = 113,
LTALK = 114,
PFLOG = 117,
IEEE802_11_PRISM = 119,
IP_OVER_FC = 122,
SUNATM = 123,
IEEE802_11_RADIOTAP = 127,
ARCNET_LINUX = 129,
APPLE_IP_OVER_IEEE1394 = 138,
MTP2_WITH_PHDR = 139,
MTP2 = 140,
MTP3 = 141,
SCCP = 142,
DOCSIS = 143,
LINUX_IRDA = 144,
USER00_LINKTYPE = 147,
USER01_LINKTYPE = 148,
USER02_LINKTYPE = 149,
USER03_LINKTYPE = 150,
USER04_LINKTYPE = 151,
USER05_LINKTYPE = 152,
USER06_LINKTYPE = 153,
USER07_LINKTYPE = 154,
USER08_LINKTYPE = 155,
USER09_LINKTYPE = 156,
USER10_LINKTYPE = 157,
USER11_LINKTYPE = 158,
USER12_LINKTYPE = 159,
USER13_LINKTYPE = 160,
USER14_LINKTYPE = 161,
USER15_LINKTYPE = 162,
IEEE802_11_AVS = 163,
BACNET_MS_TP = 165,
PPP_PPPD = 166,
GPRS_LLC = 169,
GPF_T = 170,
GPF_F = 171,
LINUX_LAPD = 177,
BLUETOOTH_HCI_H4 = 187,
USB_LINUX = 189,
PPI = 192,
IEEE802_15_4 = 195,
SITA = 196,
ERF = 197,
BLUETOOTH_HCI_H4_WITH_PHDR = 201,
AX25_KISS = 202,
LAPD = 203,
PPP_WITH_DIR = 204,
C_HDLC_WITH_DIR = 205,
FRELAY_WITH_DIR = 206,
IPMB_LINUX = 209,
IEEE802_15_4_NONASK_PHY = 215,
USB_LINUX_MMAPPED = 220,
FC_2 = 224,
FC_2_WITH_FRAME_DELIMS = 225,
IPNET = 226,
CAN_SOCKETCAN = 227,
IPV4 = 228,
IPV6 = 229,
IEEE802_15_4_NOFCS = 230,
DBUS = 231,
DVB_CI = 235,
MUX27010 = 236,
STANAG_5066_D_PDU = 237,
NFLOG = 239,
NETANALYZER = 240,
NETANALYZER_TRANSPARENT = 241,
IPOIB = 242,
MPEG_2_TS = 243,
NG40 = 244,
NFC_LLCP = 245,
INFINIBAND = 247,
SCTP = 248,
USBPCAP = 249,
RTAC_SERIAL = 250,
BLUETOOTH_LE_LL = 251,
NETLINK = 253,
BLUETOOTH_LINUX_MONITOR = 254,
BLUETOOTH_BREDR_BB = 255,
BLUETOOTH_LE_LL_WITH_PHDR = 256,
PROFIBUS_DL = 257,
PKTAP = 258,
EPON = 259,
IPMI_HPM_2 = 260,
ZWAVE_R1_R2 = 261,
ZWAVE_R3 = 262,
WATTSTOPPER_DLM = 263,
ISO_14443 = 264,
RDS = 265,
USB_DARWIN = 266,
}
impl From<Linktype> for u32 {
fn from(val: Linktype) -> u32 {
val as u32
}
}