use std::io::Write;
use byteorder::{BigEndian, ByteOrder, LittleEndian};
use byteorder::{ReadBytesExt, WriteBytesExt};
#[cfg(feature = "tokio")]
use tokio::io::AsyncWrite;
#[cfg(feature = "tokio")]
use tokio_byteorder::{AsyncReadBytesExt, AsyncWriteBytesExt};
use crate::errors::*;
use crate::pcap::MAXIMUM_SNAPLEN;
use crate::{DataLink, Endianness, TsResolution};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct PcapHeader {
pub version_major: u16,
pub version_minor: u16,
pub ts_correction: i32,
pub ts_accuracy: u32,
pub snaplen: u32,
pub datalink: DataLink,
pub ts_resolution: TsResolution,
pub endianness: Endianness,
}
impl PcapHeader {
pub fn from_slice(mut slice: &[u8]) -> PcapResult<(&[u8], PcapHeader)> {
if slice.len() < 24 {
return Err(PcapError::IncompleteBuffer);
}
let magic_number = ReadBytesExt::read_u32::<BigEndian>(&mut slice).unwrap();
match magic_number {
0xA1B2C3D4 => return init_pcap_header::<BigEndian>(slice, TsResolution::MicroSecond, Endianness::Big),
0xA1B23C4D => return init_pcap_header::<BigEndian>(slice, TsResolution::NanoSecond, Endianness::Big),
0xD4C3B2A1 => return init_pcap_header::<LittleEndian>(slice, TsResolution::MicroSecond, Endianness::Little),
0x4D3CB2A1 => return init_pcap_header::<LittleEndian>(slice, TsResolution::NanoSecond, Endianness::Little),
_ => return Err(PcapError::InvalidField("PcapHeader: wrong magic number")),
};
fn init_pcap_header<B: ByteOrder>(
mut src: &[u8],
ts_resolution: TsResolution,
endianness: Endianness,
) -> PcapResult<(&[u8], PcapHeader)> {
let header = PcapHeader {
version_major: ReadBytesExt::read_u16::<B>(&mut src).unwrap(),
version_minor: ReadBytesExt::read_u16::<B>(&mut src).unwrap(),
ts_correction: ReadBytesExt::read_i32::<B>(&mut src).unwrap(),
ts_accuracy: ReadBytesExt::read_u32::<B>(&mut src).unwrap(),
snaplen: match ReadBytesExt::read_u32::<B>(&mut src).unwrap() {
0 => MAXIMUM_SNAPLEN,
len => len,
},
datalink: DataLink::from(ReadBytesExt::read_u32::<B>(&mut src).unwrap()),
ts_resolution,
endianness,
};
Ok((src, header))
}
}
#[cfg(feature = "tokio")]
pub async fn async_from_slice(mut slice: &[u8]) -> PcapResult<(&[u8], PcapHeader)> {
if slice.len() < 24 {
return Err(PcapError::IncompleteBuffer);
}
let magic_number = AsyncReadBytesExt::read_u32::<BigEndian>(&mut slice).await.unwrap();
match magic_number {
0xA1B2C3D4 => return init_pcap_header::<BigEndian>(slice, TsResolution::MicroSecond, Endianness::Big).await,
0xA1B23C4D => return init_pcap_header::<BigEndian>(slice, TsResolution::NanoSecond, Endianness::Big).await,
0xD4C3B2A1 => return init_pcap_header::<LittleEndian>(slice, TsResolution::MicroSecond, Endianness::Little).await,
0x4D3CB2A1 => return init_pcap_header::<LittleEndian>(slice, TsResolution::NanoSecond, Endianness::Little).await,
_ => return Err(PcapError::InvalidField("PcapHeader: wrong magic number")),
};
async fn init_pcap_header<B: ByteOrder>(
mut src: &[u8],
ts_resolution: TsResolution,
endianness: Endianness,
) -> PcapResult<(&[u8], PcapHeader)> {
let header = PcapHeader {
version_major: AsyncReadBytesExt::read_u16::<B>(&mut src).await.unwrap(),
version_minor: AsyncReadBytesExt::read_u16::<B>(&mut src).await.unwrap(),
ts_correction: AsyncReadBytesExt::read_i32::<B>(&mut src).await.unwrap(),
ts_accuracy: AsyncReadBytesExt::read_u32::<B>(&mut src).await.unwrap(),
snaplen: match AsyncReadBytesExt::read_u32::<B>(&mut src).await.unwrap() {
0 => MAXIMUM_SNAPLEN,
len => len,
},
datalink: DataLink::from(AsyncReadBytesExt::read_u32::<B>(&mut src).await.unwrap()),
ts_resolution,
endianness,
};
Ok((src, header))
}
}
pub fn write_to<W: Write>(&self, writer: &mut W) -> PcapResult<usize> {
return match self.endianness {
Endianness::Big => write_header::<_, BigEndian>(self, writer),
Endianness::Little => write_header::<_, LittleEndian>(self, writer),
};
fn write_header<W: Write, B: ByteOrder>(header: &PcapHeader, writer: &mut W) -> PcapResult<usize> {
let magic_number = match header.ts_resolution {
TsResolution::MicroSecond => 0xA1B2C3D4,
TsResolution::NanoSecond => 0xA1B23C4D,
};
writer.write_u32::<B>(magic_number).map_err(PcapError::IoError)?;
writer.write_u16::<B>(header.version_major).map_err(PcapError::IoError)?;
writer.write_u16::<B>(header.version_minor).map_err(PcapError::IoError)?;
writer.write_i32::<B>(header.ts_correction).map_err(PcapError::IoError)?;
writer.write_u32::<B>(header.ts_accuracy).map_err(PcapError::IoError)?;
writer.write_u32::<B>(header.snaplen).map_err(PcapError::IoError)?;
writer.write_u32::<B>(header.datalink.into()).map_err(PcapError::IoError)?;
Ok(24)
}
}
#[cfg(feature = "tokio")]
pub async fn async_write_to<W: AsyncWrite + Unpin>(&self, writer: &mut W) -> PcapResult<usize> {
return match self.endianness {
Endianness::Big => write_header::<_, BigEndian>(self, writer).await,
Endianness::Little => write_header::<_, LittleEndian>(self, writer).await,
};
async fn write_header<W: AsyncWrite + Unpin, B: ByteOrder>(header: &PcapHeader, writer: &mut W) -> PcapResult<usize> {
let magic_number = match header.ts_resolution {
TsResolution::MicroSecond => 0xA1B2C3D4,
TsResolution::NanoSecond => 0xA1B23C4D,
};
writer.write_u32::<B>(magic_number).await.map_err(PcapError::IoError)?;
writer.write_u16::<B>(header.version_major).await.map_err(PcapError::IoError)?;
writer.write_u16::<B>(header.version_minor).await.map_err(PcapError::IoError)?;
writer.write_i32::<B>(header.ts_correction).await.map_err(PcapError::IoError)?;
writer.write_u32::<B>(header.ts_accuracy).await.map_err(PcapError::IoError)?;
writer.write_u32::<B>(header.snaplen).await.map_err(PcapError::IoError)?;
writer.write_u32::<B>(header.datalink.into()).await.map_err(PcapError::IoError)?;
Ok(24)
}
}
}
impl Default for PcapHeader {
fn default() -> Self {
PcapHeader {
version_major: 2,
version_minor: 4,
ts_correction: 0,
ts_accuracy: 0,
snaplen: MAXIMUM_SNAPLEN,
datalink: DataLink::ETHERNET,
ts_resolution: TsResolution::MicroSecond,
endianness: Endianness::Big,
}
}
}