diff --git a/assets/err-buffertoosmall.pcapng b/assets/err-buffertoosmall.pcapng new file mode 100755 index 0000000..e677d4a Binary files /dev/null and b/assets/err-buffertoosmall.pcapng differ diff --git a/src/capture_pcap.rs b/src/capture_pcap.rs index 2ec0926..2f9b1d2 100644 --- a/src/capture_pcap.rs +++ b/src/capture_pcap.rs @@ -159,7 +159,15 @@ where Err(PcapError::UnexpectedEof) } else { match n { - Needed::Size(n) => Err(PcapError::Incomplete(n.into())), + Needed::Size(n) => { + if self.buffer.available_data() + usize::from(n) + >= self.buffer.capacity() + { + Err(PcapError::BufferTooSmall) + } else { + Err(PcapError::Incomplete(n.into())) + } + } Needed::Unknown => Err(PcapError::Incomplete(0)), } } diff --git a/src/capture_pcapng.rs b/src/capture_pcapng.rs index 0af7182..1b34b64 100644 --- a/src/capture_pcapng.rs +++ b/src/capture_pcapng.rs @@ -5,7 +5,7 @@ use crate::traits::PcapReaderIterator; use circular::Buffer; use nom::combinator::{complete, map}; use nom::multi::many1; -use nom::{IResult, Offset, Needed}; +use nom::{IResult, Needed, Offset}; use std::fmt; use std::io::Read; @@ -179,11 +179,19 @@ where Err(PcapError::UnexpectedEof) } else { match n { - Needed::Size(n) => Err(PcapError::Incomplete(n.into())), + Needed::Size(n) => { + if self.buffer.available_data() + usize::from(n) + >= self.buffer.capacity() + { + Err(PcapError::BufferTooSmall) + } else { + Err(PcapError::Incomplete(n.into())) + } + } Needed::Unknown => Err(PcapError::Incomplete(0)), } } - }, + } } } fn consume(&mut self, offset: usize) { diff --git a/src/error.rs b/src/error.rs index a85f43b..9d6aea5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,6 +6,8 @@ use std::fmt; pub enum PcapError { /// No more data available Eof, + /// Buffer capacity is too small, and some full frame cannot be stored + BufferTooSmall, /// Expected more data but got EOF UnexpectedEof, /// An error happened during a `read` operation @@ -38,6 +40,7 @@ where pub fn to_owned_vec(&self) -> PcapError<&'static [u8]> { match self { PcapError::Eof => PcapError::Eof, + PcapError::BufferTooSmall => PcapError::BufferTooSmall, PcapError::UnexpectedEof => PcapError::UnexpectedEof, PcapError::ReadError => PcapError::ReadError, PcapError::Incomplete(n) => PcapError::Incomplete(*n), @@ -66,6 +69,7 @@ where fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { PcapError::Eof => write!(f, "End of file"), + PcapError::BufferTooSmall => write!(f, "Buffer is too small"), PcapError::UnexpectedEof => write!(f, "Unexpected end of file"), PcapError::ReadError => write!(f, "Read error"), PcapError::Incomplete(n) => write!(f, "Incomplete read: {n}"), diff --git a/tests/pcapng.rs b/tests/pcapng.rs index 1abe5ac..34ccbc8 100644 --- a/tests/pcapng.rs +++ b/tests/pcapng.rs @@ -403,3 +403,31 @@ fn err_eof() { let res = parse_block_le(data).expect_err("expected incomplete"); assert!(res.is_incomplete()); } + +// related issue: https://github.com/rusticata/pcap-parser/issues/29 +#[test] +fn test_reader_buffer_too_small() { + let file = File::open("assets/err-buffertoosmall.pcapng").unwrap(); + let mut reader = create_reader(1024, file).expect("PcapNGReader"); + let mut num_blocks = 0; + let mut num_refills = 0; + const MAX_REFILLS: usize = 20; + // the only expected way to exit this loop is to encounter BufferTooSmall + // check number of refills to detect infinite loops + loop { + match reader.next() { + Ok((offset, _block)) => { + num_blocks += 1; + reader.consume(offset) + } + Err(PcapError::Incomplete(_)) => { + num_refills += 1; + assert!(num_refills < MAX_REFILLS); + reader.refill().unwrap(); + } + Err(PcapError::BufferTooSmall) => break, + Err(e) => panic!("Unexpected error {:?}", e), + } + } + assert_eq!(num_blocks, 9); +}