#[cfg(feature = "pcap")]
use bitcode::Decode;
#[cfg(feature = "pcap")]
use bitcode::Encode;
#[cfg(feature = "pcap")]
use byteorder::BigEndian;
#[cfg(feature = "pcap")]
use byteorder::LittleEndian;
#[cfg(feature = "pcap")]
use byteorder::ReadBytesExt;
#[cfg(feature = "pcap")]
use byteorder::WriteBytesExt;
#[cfg(feature = "pcap")]
use serde::Deserialize;
#[cfg(feature = "pcap")]
use serde::Serialize;
#[cfg(feature = "pcap")]
use std::fs::File;
#[cfg(feature = "pcap")]
use std::io::Read;
#[cfg(feature = "pcap")]
use std::io::Write;
#[cfg(all(feature = "pcap", feature = "libpnet"))]
use std::time::SystemTime;
#[cfg(all(feature = "pcap", feature = "libpnet"))]
use std::time::UNIX_EPOCH;
#[cfg(feature = "pcap")]
use strum::IntoEnumIterator;
#[cfg(feature = "pcap")]
use strum_macros::EnumIter;
#[cfg(feature = "pcap")]
use strum_macros::EnumString;
#[cfg(feature = "pcap")]
use crate::PcapByteOrder;
#[cfg(feature = "pcap")]
use crate::error::PcaptureError;
#[cfg(feature = "pcap")]
#[repr(u32)]
#[derive(Debug, Clone, Copy, EnumString, EnumIter, Serialize, Deserialize, Encode, Decode)]
pub enum LinkType {
NULL = 0,
ETHERNET = 1,
AX25 = 3,
IEEE8025 = 6,
ARCNETBSD = 7,
SLIP = 8,
PPP = 9,
FDDI = 10,
PPPHDLC = 50,
PPPETHER = 51,
ATMRFC1483 = 100,
RAW = 101,
CHDLC = 104,
IEEE80211 = 105,
FRELAY = 107,
LOOP = 108,
LINUXSLL = 113,
LTALK = 114,
PFLOG = 117,
IEEE80211PRISM = 119,
IPOVERFC = 122,
SUNATM = 123,
IEEE80211RADIOTAP = 127,
ARCNETLINUX = 129,
APPLEIPOVERIEEE1394 = 138,
MTP2WITHPHDR = 139,
MTP2 = 140,
MTP3 = 141,
SCCP = 142,
DOCSIS = 143,
LINUXIRDA = 144,
IEEE80211AVS = 163,
BACNETMSTP = 165,
PPPPPPD = 166,
GPRSLLC = 169,
GPFT = 170,
GPFF = 171,
LINUXLAPD = 177,
MFR = 182,
BLUETOOTHHCIH4 = 187,
USBLINUX = 189,
PPI = 192,
IEEE802154WITHFCS = 195,
SITA = 196,
ERF = 197,
BLUETOOTHHCIH4WITHPHDR = 201,
AX25KISS = 202,
LAPD = 203,
PPPWITHDIR = 204,
CHDLCWITHDIR = 205,
FRELAYWITHDIR = 206,
LAPBWITHDIR = 207,
IPMBLINUX = 209,
IEEE802154NONASKPHY = 215,
USBLINUXMMAPPED = 220,
FC2 = 224,
FC2WITHFRAMEDELIMS = 225,
IPNET = 226,
CANSOCKETCAN = 227,
IPV4 = 228,
IPV6 = 229,
IEEE802154NOFCS = 230,
DBUS = 231,
DVBCI = 235,
MUX27010 = 236,
STANAG5066DPDU = 237,
NFLOG = 239,
NETANALYZER = 240,
NETANALYZERTRANSPARENT = 241,
IPOIB = 242,
MPEG2TS = 243,
NG40 = 244,
NFCLLCP = 245,
INFINIBAND = 247,
SCTP = 248,
USBPCAP = 249,
RTACSERIAL = 250,
BLUETOOTHLELL = 251,
WIRESHARKUPPERPDU = 252,
NETLINK = 253,
BLUETOOTHLINUXMONITOR = 254,
BLUETOOTHBREDRBB = 255,
BLUETOOTHLELLWITHPHDR = 256,
PROFIBUSDL = 257,
PKTAP = 258,
EPON = 259,
IPMIHPM2 = 260,
ZWAVER1R2 = 261,
ZWAVER3 = 262,
WATTSTOPPERDLM = 263,
ISO14443 = 264,
RDS = 265,
USBDARWIN = 266,
SDLC = 268,
LORATAP = 270,
VSOCK = 271,
NORDICBLE = 272,
DOCSIS31XRA31 = 273,
ETHERNETMPACKET = 274,
DISPLAYPORTAUX = 275,
LINUXSLL2 = 276,
OPENVIZSLA = 278,
EBHSCR = 279,
VPPDISPATCH = 280,
DSATAGBRCM = 281,
DSATAGBRCMPREPEND = 282,
IEEE802154TAP = 283,
DSATAGDSA = 284,
DSATAGEDSA = 285,
ELEE = 286,
WAVESERIAL = 287,
USB20 = 288,
ATSCALP = 289,
}
#[cfg(feature = "pcap")]
impl LinkType {
pub fn to_u32(self) -> u32 {
self as u32
}
pub fn from_u32(value: u32) -> Option<Self> {
Self::iter().find(|&e| e.to_u32() == value)
}
}
#[cfg(feature = "pcap")]
#[repr(C)]
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
pub struct FileHeader {
pub magic_number: u32,
pub major_version: u16,
pub minor_version: u16,
reserved1: u32,
reserved2: u32,
pub snaplen: u32,
pub linktype: LinkType,
}
#[cfg(feature = "pcap")]
impl FileHeader {
pub fn new(if_name: &str) -> Self {
let linktype = if if_name == "any" {
LinkType::LINUXSLL
} else {
LinkType::ETHERNET
};
Self {
magic_number: 0xa1b2c3d4,
major_version: 2,
minor_version: 4,
reserved1: 0,
reserved2: 0,
snaplen: 0,
linktype,
}
}
pub fn write(&self, fs: &mut File, pbo: PcapByteOrder) -> Result<(), PcaptureError> {
match pbo {
PcapByteOrder::LittleEndian | PcapByteOrder::WiresharkDefault => {
fs.write_u32::<LittleEndian>(self.magic_number)?;
fs.write_u16::<LittleEndian>(self.major_version)?;
fs.write_u16::<LittleEndian>(self.minor_version)?;
fs.write_u32::<LittleEndian>(self.reserved1)?;
fs.write_u32::<LittleEndian>(self.reserved2)?;
fs.write_u32::<LittleEndian>(self.snaplen)?;
fs.write_u32::<LittleEndian>(self.linktype.to_u32())?;
}
PcapByteOrder::BigEndian => {
fs.write_u32::<BigEndian>(self.magic_number)?;
fs.write_u16::<BigEndian>(self.major_version)?;
fs.write_u16::<BigEndian>(self.minor_version)?;
fs.write_u32::<BigEndian>(self.reserved1)?;
fs.write_u32::<BigEndian>(self.reserved2)?;
fs.write_u32::<BigEndian>(self.snaplen)?;
fs.write_u32::<BigEndian>(self.linktype.to_u32())?;
}
}
Ok(())
}
pub fn read(fs: &mut File, pbo: PcapByteOrder) -> Result<Self, PcaptureError> {
match pbo {
PcapByteOrder::LittleEndian | PcapByteOrder::WiresharkDefault => {
let magic_number = fs.read_u32::<LittleEndian>()?;
let major_version = fs.read_u16::<LittleEndian>()?;
let minor_version = fs.read_u16::<LittleEndian>()?;
let reserved1 = fs.read_u32::<LittleEndian>()?;
let reserved2 = fs.read_u32::<LittleEndian>()?;
let snaplen = fs.read_u32::<LittleEndian>()?;
let linktype_value = fs.read_u32::<LittleEndian>()?;
let linktype = match LinkType::from_u32(linktype_value) {
Some(l) => l,
None => {
return Err(PcaptureError::UnknownLinkType {
linktype: linktype_value,
});
}
};
Ok(Self {
magic_number,
major_version,
minor_version,
reserved1,
reserved2,
snaplen,
linktype,
})
}
PcapByteOrder::BigEndian => {
let magic_number = fs.read_u32::<BigEndian>()?;
let major_version = fs.read_u16::<BigEndian>()?;
let minor_version = fs.read_u16::<BigEndian>()?;
let reserved1 = fs.read_u32::<BigEndian>()?;
let reserved2 = fs.read_u32::<BigEndian>()?;
let snaplen = fs.read_u32::<BigEndian>()?;
let linktype_value = fs.read_u32::<BigEndian>()?;
let linktype = match LinkType::from_u32(linktype_value) {
Some(l) => l,
None => {
return Err(PcaptureError::UnknownLinkType {
linktype: linktype_value,
});
}
};
Ok(Self {
magic_number,
major_version,
minor_version,
reserved1,
reserved2,
snaplen,
linktype,
})
}
}
}
}
#[cfg(feature = "pcap")]
#[repr(C)]
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
pub struct PacketRecord {
pub ts_sec: u32,
pub ts_usec: u32,
pub captured_packet_length: u32,
pub original_packet_length: u32,
pub packet_data: Vec<u8>,
}
#[cfg(feature = "pcap")]
impl PacketRecord {
#[cfg(feature = "libpnet")]
pub fn new(packet_data: &[u8], snaplen: usize) -> Result<Self, PcaptureError> {
let packet_slice = if packet_data.len() > snaplen {
&packet_data[..snaplen]
} else {
packet_data
};
let dura = SystemTime::now().duration_since(UNIX_EPOCH)?;
let ts_sec = dura.as_secs() as u32;
let ts_usec = dura.subsec_micros();
let captured_packet_length = packet_slice.len() as u32;
let original_packet_length = packet_data.len() as u32;
Ok(Self {
ts_sec,
ts_usec,
captured_packet_length,
original_packet_length,
packet_data: packet_data.to_vec(),
})
}
#[cfg(feature = "libpcap")]
pub fn new(
packet_data: &[u8],
snaplen: usize,
ts_sec: u32,
ts_usec: u32,
) -> Result<Self, PcaptureError> {
let packet_slice = if packet_data.len() > snaplen {
&packet_data[..snaplen]
} else {
packet_data
};
let captured_packet_length = packet_slice.len() as u32;
let original_packet_length = packet_data.len() as u32;
Ok(Self {
ts_sec,
ts_usec,
captured_packet_length,
original_packet_length,
packet_data: packet_data.to_vec(),
})
}
pub fn write(&self, fs: &mut File, pbo: PcapByteOrder) -> Result<(), PcaptureError> {
match pbo {
PcapByteOrder::LittleEndian | PcapByteOrder::WiresharkDefault => {
fs.write_u32::<LittleEndian>(self.ts_sec)?;
fs.write_u32::<LittleEndian>(self.ts_usec)?;
fs.write_u32::<LittleEndian>(self.captured_packet_length)?;
fs.write_u32::<LittleEndian>(self.original_packet_length)?;
fs.write_all(&self.packet_data)?;
}
PcapByteOrder::BigEndian => {
fs.write_u32::<BigEndian>(self.ts_sec)?;
fs.write_u32::<BigEndian>(self.ts_usec)?;
fs.write_u32::<BigEndian>(self.captured_packet_length)?;
fs.write_u32::<BigEndian>(self.original_packet_length)?;
fs.write_all(&self.packet_data)?;
}
}
Ok(())
}
pub fn read(fs: &mut File, pbo: PcapByteOrder) -> Result<Self, PcaptureError> {
let (ts_sec, ts_usec, captured_packet_length, original_packet_length) = match pbo {
PcapByteOrder::LittleEndian | PcapByteOrder::WiresharkDefault => {
let ts_sec = fs.read_u32::<LittleEndian>()?;
let ts_usec = fs.read_u32::<LittleEndian>()?;
let captured_packet_length = fs.read_u32::<LittleEndian>()?;
let original_packet_length = fs.read_u32::<LittleEndian>()?;
(
ts_sec,
ts_usec,
captured_packet_length,
original_packet_length,
)
}
PcapByteOrder::BigEndian => {
let ts_sec = fs.read_u32::<BigEndian>()?;
let ts_usec = fs.read_u32::<BigEndian>()?;
let captured_packet_length = fs.read_u32::<BigEndian>()?;
let original_packet_length = fs.read_u32::<BigEndian>()?;
(
ts_sec,
ts_usec,
captured_packet_length,
original_packet_length,
)
}
};
let mut data = vec![0u8; captured_packet_length as usize]; fs.read_exact(&mut data)?;
Ok(Self {
ts_sec,
ts_usec,
captured_packet_length,
original_packet_length,
packet_data: data,
})
}
}
#[cfg(feature = "pcap")]
#[repr(C)]
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
pub struct Pcap {
pub pbo: PcapByteOrder,
pub header: FileHeader,
pub records: Vec<PacketRecord>,
}
#[cfg(feature = "pcap")]
impl Pcap {
pub fn new(if_name: &str, pbo: PcapByteOrder) -> Self {
Self {
pbo,
header: FileHeader::new(if_name),
records: Vec::new(),
}
}
pub fn append(&mut self, record: PacketRecord) {
if record.packet_data.len() as u32 > self.header.snaplen {
self.header.snaplen = record.packet_data.len() as u32;
}
self.records.push(record);
}
pub fn write(&self, fs: &mut File) -> Result<(), PcaptureError> {
self.header.write(fs, self.pbo)?;
for r in &self.records {
r.write(fs, self.pbo)?;
}
Ok(())
}
pub fn write_all(&self, path: &str) -> Result<(), PcaptureError> {
let mut fs = File::create(path)?;
Self::write(self, &mut fs)?;
Ok(())
}
pub fn read_all(path: &str, pbo: PcapByteOrder) -> Result<Self, PcaptureError> {
let mut fs = File::open(path)?;
let header = FileHeader::read(&mut fs, pbo)?;
let mut record = Vec::new();
loop {
match PacketRecord::read(&mut fs, pbo) {
Ok(r) => record.push(r),
Err(e) => match e {
PcaptureError::IOError(_) => break, _ => return Err(e),
},
}
}
Ok(Self {
pbo,
header,
records: record,
})
}
}