use std::{fmt::Display, time::Duration};
use thiserror::Error;
use crate::{
DataLink,
pcapng::blocks::{block_name, interface_description::InterfaceTsResolution},
};
#[derive(Debug, Error)]
pub enum PcapNgError {
#[error(transparent)]
Parse(#[from] PcapNgParseError),
#[error(transparent)]
Read(#[from] PcapNgReadError),
#[error(transparent)]
Write(#[from] PcapNgWriteError),
#[error(transparent)]
BlockValidation(#[from] ContentValidationError),
#[error(transparent)]
StateUpdate(#[from] StateUpdateError),
}
#[derive(Debug, Error)]
pub enum PcapNgParseError {
#[error("The buffer is too small: need {0}B, got {1}B")]
IncompleteBuffer(usize, usize),
#[error("Invalid raw block format")]
InvalidFormat(#[from] PcapNgFormatError),
#[error(transparent)]
BlockConversion(#[from] BlockConversionError),
#[error(transparent)]
StateUpdate(#[from] StateUpdateError),
}
impl From<RawBlockParseError> for PcapNgParseError {
fn from(value: RawBlockParseError) -> Self {
match value {
RawBlockParseError::IncompleteBuffer(needed, actual) => Self::IncompleteBuffer(needed, actual),
RawBlockParseError::InvalidFormat(format) => Self::InvalidFormat(format),
}
}
}
#[derive(Debug, Error)]
pub enum PcapNgReadError {
#[error("I/O error while reading the pcapng")]
Io(#[source] std::io::Error),
#[error("Invalid raw block format")]
InvalidFormat(#[from] PcapNgFormatError),
#[error(transparent)]
BlockConversion(#[from] BlockConversionError),
#[error(transparent)]
StateUpdate(#[from] StateUpdateError),
}
impl From<PcapNgParseError> for PcapNgReadError {
fn from(value: PcapNgParseError) -> Self {
match value {
PcapNgParseError::IncompleteBuffer(_, _) => {
Self::Io(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))
}
PcapNgParseError::InvalidFormat(e) => Self::InvalidFormat(e),
PcapNgParseError::BlockConversion(e) => Self::BlockConversion(e),
PcapNgParseError::StateUpdate(e) => Self::StateUpdate(e),
}
}
}
#[derive(Debug, Error)]
pub enum PcapNgWriteError {
#[error("I/O error during writing")]
Io(#[from] std::io::Error),
#[error("Field `{field}` failed validation during writing")]
Validation {
field: &'static str,
source: Box<ContentValidationError>,
},
#[error("Invalid raw block format")]
InvalidFormat(#[from] PcapNgFormatError),
#[error("State update error during writing")]
StateUpdate(#[from] StateUpdateError),
}
impl PcapNgWriteError {
pub(crate) fn validation_error(field: &'static str, source: ContentValidationError) -> Self {
Self::Validation {
field,
source: Box::new(source),
}
}
}
#[derive(Debug, Error)]
pub enum PcapNgFormatError {
#[error("The section header is missing")]
MissingSectionHeader,
#[error("Invalid magic number: {0:#X}")]
InvalidMagicNumber(u32),
#[error("Block length is not a multiple of 4: {0}B")]
BlockNotAligned(usize),
#[error("Block is too short: minimum {0}B, actual {1}B")]
BlockTooShort(usize, usize),
#[error("Block length fields don't match: initial {0}B, trailing {1}B")]
BlockLengthMismatch(u32, u32),
#[error("Block length doesn't match body length: expected {expected}B, got {actual}B")]
InvalidBlockLength {
expected: usize,
actual: u32,
},
}
#[derive(Debug, Error)]
pub enum RawBlockParseError {
#[error("The buffer is too small: need {0}B, got {1}B")]
IncompleteBuffer(usize, usize),
#[error("Invalid raw block format")]
InvalidFormat(#[from] PcapNgFormatError),
}
#[derive(Debug, Error)]
pub struct BlockConversionError {
pub type_: u32,
pub source: Box<BlockContentParseError>,
}
impl Display for BlockConversionError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Invalid content for block '{}' ({:#X})",
block_name(self.type_),
self.type_
)
}
}
#[derive(Debug, Error)]
pub enum BlockContentParseError {
#[error("Block content too small: need {needed}B, got {actual}B")]
BlockContentTooSmall {
needed: usize,
actual: usize,
},
#[error(transparent)]
Validation(#[from] ContentValidationError),
#[error("Error parsing the options")]
Option(#[from] OptionParseError),
}
#[derive(Debug, Error)]
pub enum StateUpdateError {
#[error("Failed to convert raw block to update the pcapng state")]
BlockConversion(#[from] BlockConversionError),
}
#[derive(Debug, Error)]
pub enum ContentValidationError {
#[error("Invalid magic number: {0:#X}")]
InvalidMagicNumber(u32),
#[error("Invalid reserved field: {0}")]
InvalidReservedField(u16),
#[error("Invalid timestamp resolution: {0:#X} (is_bin: {1}, resol:{2})")]
InvalidTsResolution(u8, bool, u8),
#[error("Invalid Linktype: {0:?}")]
InvalidLinktype(DataLink),
#[error("Section without any interface")]
NoInterface,
#[error("Invalid interface ID: {0}")]
InvalidInterfaceId(u32),
#[error(
"Failed to decode timestamp (high: {timestamp_high:#X}, low: {timestamp_low:#X}, combined: {}) with resolution {resolution} and offset {offset}s",
((*timestamp_high as u64) << 32) | *timestamp_low as u64
)]
FailedToDecodeTimestamp {
timestamp_high: u32,
timestamp_low: u32,
resolution: InterfaceTsResolution,
offset: i64,
},
#[error("Failed to encode timestamp {timestamp:?} with resolution {resolution} and offset {offset}s")]
FailedToEncodeTimestamp {
timestamp: Duration,
resolution: InterfaceTsResolution,
offset: i64,
},
#[error("The original length of the packet is lower than its actual length: {0}B on wire, {1}B captured")]
InvalidOriginalLen(u32, usize),
#[error("Invalid captured length: expected {expected}B, got {actual}B")]
InvalidCapturedLen {
expected: usize,
actual: usize,
},
#[error("Wrong record size: expected {expected}B, got {actual}B")]
RecordWrongSize {
expected: usize,
actual: usize,
},
#[error("Wrong record minimum size: expected at least {min}B, got {actual}B")]
RecordWrongMinSize {
min: usize,
actual: usize,
},
#[error("Record length doesn't fit on a u16: {0}B")]
RecordTooBig(usize),
#[error("A record name is not valid UTF-8")]
RecordNameNotUtf8(#[source] std::str::Utf8Error),
#[error("Record without any name")]
RecordNamesEmpty,
#[error("Error in custom block conversion for PEN {0}: {1}")]
CustomBlockConversionError(u32, Box<dyn std::error::Error + Sync + Send>),
#[error("Block content doesn't fit on a u32: {0}B")]
BlockContentTooBig(u64),
#[error("Option content doesn't fit on a u16: {0}B")]
OptionTooBig(usize),
}
#[derive(Debug, Error)]
pub enum OptionParseError {
#[error("The option field is too small to parse the options: need {needed}B, got {actual}B")]
ContentTooSmall {
needed: usize,
actual: usize,
},
#[error("Invalid option entry. Code: {code}, Name: {name}")]
InvalidEntry {
code: u16,
name: &'static str,
source: Box<OptionEntryError>,
},
}
#[derive(Debug, Error)]
pub enum OptionEntryError {
#[error("Wrong entry size: expected {expected}B, got {actual}B")]
WrongSize {
expected: usize,
actual: usize,
},
#[error("Invalid UTF-8 format")]
InvalidUtf8(#[from] std::str::Utf8Error),
#[error(transparent)]
Validation(#[from] ContentValidationError),
}