1#![warn(
6 missing_docs,
7 unreachable_patterns,
8 clippy::useless_conversion,
9 clippy::redundant_clone,
10 clippy::precedence
11)]
12
13use nom::error::{ErrorKind, ParseError};
20use nom::{Finish as _, IResult, Parser};
21use std::borrow::Cow;
22use thiserror::Error;
23use zerocopy::byteorder::little_endian::{U16, U32, U64};
24use zerocopy::{Immutable, IntoBytes};
25
26#[cfg(feature = "compile")]
28mod bindings;
29#[cfg(feature = "compile")]
31pub mod compile;
32
33#[derive(Copy, Clone, Debug, PartialEq, Eq, strum_macros::FromRepr, strum_macros::Display)]
39#[repr(u16)]
40pub enum LinkType {
41 Ethernet = 1,
43 PureIp = 101,
45}
46
47impl From<LinkType> for u16 {
48 fn from(link_type: LinkType) -> Self {
49 link_type as u16
50 }
51}
52
53#[derive(Copy, Clone, Debug, PartialEq, Eq, strum_macros::FromRepr, strum_macros::Display)]
59#[repr(u16)]
60pub enum InterfaceDescriptionOptionCode {
61 EndOfOpt = 0,
63 IfName = 2,
65}
66
67impl From<InterfaceDescriptionOptionCode> for u16 {
68 fn from(option_code: InterfaceDescriptionOptionCode) -> Self {
69 option_code as u16
70 }
71}
72
73#[derive(Debug, PartialEq, Eq)]
79pub enum InterfaceDescriptionOption<'a> {
80 IfName(Cow<'a, str>),
82 EndOfOpt,
84}
85
86impl<'a> InterfaceDescriptionOption<'a> {
87 pub fn code(&self) -> InterfaceDescriptionOptionCode {
92 match self {
93 Self::IfName(_) => InterfaceDescriptionOptionCode::IfName,
94 Self::EndOfOpt => InterfaceDescriptionOptionCode::EndOfOpt,
95 }
96 }
97}
98
99#[derive(Error, Debug, PartialEq)]
101pub enum ParsingError<'a> {
102 #[error("Unexpected block type: got {got}, want {want:?}")]
104 UnexpectedBlockType {
105 got: u32,
107 want: BlockType,
109 },
110
111 #[error("Block length is shorter than 12 bytes: {0}")]
113 BlockLengthTooShort(u32),
114
115 #[error("Header and footer block lengths disagree: header {header} footer {footer}")]
117 BlockLengthsDisagree {
118 header: u32,
120 footer: u32,
122 },
123
124 #[error("Unsupported Section Header Block options")]
126 SectionHeaderBlockOptionsUnspported,
127
128 #[error("Unsupported Enhanced Packet Block options")]
130 EnhancedPacketBlockOptionsUnspported,
131
132 #[error("Unsupported IDB option code: {0}")]
134 UnsupportedInterfaceDescriptionOption(u16),
135
136 #[error("End-of-option encountered with non-zero length")]
138 EndOfOptLengthNotZero(u16),
139
140 #[error("Duplicate Interface Name option: name1 {name1} name2 {name2}")]
142 DuplicateIfNameOption {
143 name1: Cow<'a, str>,
145 name2: Cow<'a, str>,
147 },
148
149 #[error("Data after end-of-opt")]
151 DataAfterEndOfOpt,
152
153 #[error("Unsupported link type")]
155 UnsupportedLinkType(u16),
156
157 #[error("Invalid Section Header Block magic")]
159 InvalidMagic,
160
161 #[error("Unsupported Section Header Block major version")]
163 UnsupportedMajorVersion(u16),
164
165 #[error("Unsupported Section Header Block minor version")]
167 UnsupportedMinorVersion(u16),
168
169 #[error("Big endian pcapng is not supported")]
171 BigEndianNotSupported,
172
173 #[error("Nom error at {0:?}: {1:?}")]
175 Nom(&'a [u8], ErrorKind),
176}
177
178impl<'a> ParseError<&'a [u8]> for ParsingError<'a> {
179 fn from_error_kind(input: &'a [u8], kind: ErrorKind) -> Self {
180 ParsingError::Nom(input, kind)
181 }
182 fn append(_: &'a [u8], _: ErrorKind, other: Self) -> Self {
183 other
185 }
186}
187
188type PcapResult<'a, T> = IResult<&'a [u8], T, ParsingError<'a>>;
189
190fn parse_idb_option<'a>(input: &'a [u8]) -> PcapResult<'a, InterfaceDescriptionOption<'a>> {
191 let (input, code) = nom::number::complete::le_u16(input)?;
192 let (input, len) = nom::number::complete::le_u16(input)?;
193 match InterfaceDescriptionOptionCode::from_repr(code)
194 .ok_or(nom::Err::Failure(ParsingError::UnsupportedInterfaceDescriptionOption(code)))?
195 {
196 InterfaceDescriptionOptionCode::EndOfOpt => {
197 if len != 0 {
198 return Err(nom::Err::Failure(ParsingError::EndOfOptLengthNotZero(len)));
199 }
200 Ok((input, InterfaceDescriptionOption::EndOfOpt))
201 }
202 InterfaceDescriptionOptionCode::IfName => {
203 let (input, value) = nom::bytes::complete::take(len)(input)?;
204 let padding_len = len.next_multiple_of(4) - len;
205 let (input, _) = nom::bytes::complete::take(padding_len)(input)?;
206 Ok((input, InterfaceDescriptionOption::IfName(String::from_utf8_lossy(value))))
213 }
214 }
215}
216
217#[derive(Debug, PartialEq, Eq)]
221pub struct ParsedInterfaceDescriptionOptions<'a> {
222 pub if_name: Option<Cow<'a, str>>,
224}
225
226fn parse_idb_options<'a>(
227 mut input: &'a [u8],
228) -> PcapResult<'a, ParsedInterfaceDescriptionOptions<'a>> {
229 let mut rtn = ParsedInterfaceDescriptionOptions { if_name: None };
230 while input.len() > 0 {
231 let (remaining, option) = parse_idb_option(input)?;
232 input = remaining;
233 match option {
234 InterfaceDescriptionOption::EndOfOpt => {
235 if input.len() > 0 {
236 return Err(nom::Err::Failure(ParsingError::DataAfterEndOfOpt));
237 }
238 }
239 InterfaceDescriptionOption::IfName(if_name) => {
240 if let Some(name) = rtn.if_name.take() {
241 return Err(nom::Err::Failure(ParsingError::DuplicateIfNameOption {
242 name1: name,
243 name2: if_name,
244 }));
245 }
246 rtn.if_name = Some(if_name);
247 }
248 }
249 }
250 Ok((input, rtn))
251}
252
253#[derive(Debug, PartialEq, Eq)]
257pub struct ParsedSectionHeader {
258 pub section_length: u64,
260}
261
262const BYTE_ORDER_MAGIC: u32 = 0x1A2B3C4D;
266
267#[derive(Debug, PartialEq, Eq)]
271pub struct ParsedInterfaceDescription<'a> {
272 pub link_type: LinkType,
274 pub snap_len: u32,
276 pub options: ParsedInterfaceDescriptionOptions<'a>,
278}
279
280#[derive(Debug, PartialEq, Eq)]
284pub struct ParsedEnhancedPacket<'a> {
285 pub interface_id: u32,
287 pub timestamp: std::time::SystemTime,
289 pub captured_length: u32,
291 pub original_length: u32,
293 pub packet_data: &'a [u8],
295}
296
297#[derive(Copy, Clone, Debug, PartialEq, Eq, strum_macros::FromRepr, strum_macros::Display)]
303#[repr(u32)]
304pub enum BlockType {
305 SectionHeader = 0x0A0D0D0A,
307 InterfaceDescription = 0x00000001,
309 EnhancedPacket = 0x00000006,
311}
312
313impl From<BlockType> for u32 {
314 fn from(block_type: BlockType) -> Self {
315 block_type as u32
316 }
317}
318
319#[derive(Debug)]
323pub struct PcapNgSection<'a> {
324 pub header: ParsedSectionHeader,
328 pub interfaces: Vec<ParsedInterfaceDescription<'a>>,
332 input: &'a [u8],
333}
334
335impl<'a> PcapNgSection<'a> {
336 pub fn packet_blocks(&self) -> PcapNgPacketIter<'a> {
338 PcapNgPacketIter { input: self.input }
339 }
340}
341
342pub struct PcapNgPacketIter<'a> {
346 input: &'a [u8],
347}
348
349impl<'a> Iterator for PcapNgPacketIter<'a> {
350 type Item = Result<ParsedEnhancedPacket<'a>, nom::Err<ParsingError<'a>>>;
351
352 fn next(&mut self) -> Option<Self::Item> {
353 (!self.input.is_empty()).then(|| {
354 parse_block::<ParsedEnhancedPacket<'a>>(self.input).map(|(rem, epb)| {
355 self.input = rem;
356 epb
357 })
358 })
359 }
360}
361
362const BLOCK_HEADER_FOOTER_LEN: u32 = 12;
363
364const MAJOR_VERSION: u16 = 1;
369
370const MINOR_VERSION: u16 = 0;
375
376pub trait PcapNgBlock<'a>: Sized {
378 const BLOCK_TYPE: BlockType;
380 fn parse(body: &'a [u8]) -> PcapResult<'a, Self>;
383}
384
385impl<'a> PcapNgBlock<'a> for ParsedSectionHeader {
386 const BLOCK_TYPE: BlockType = BlockType::SectionHeader;
387
388 fn parse(body: &'a [u8]) -> PcapResult<'a, Self> {
416 let (body, _magic) = nom::bytes::complete::tag(&BYTE_ORDER_MAGIC.to_le_bytes()[..])(body)?;
417 let (body, major_version) = nom::number::complete::le_u16(body)?;
418 if major_version != MAJOR_VERSION {
419 return Err(nom::Err::Failure(ParsingError::UnsupportedMajorVersion(major_version)));
420 }
421 let (body, minor_version) = nom::number::complete::le_u16(body)?;
422 if minor_version != MINOR_VERSION && minor_version != 2 {
429 return Err(nom::Err::Failure(ParsingError::UnsupportedMinorVersion(minor_version)));
430 }
431 let (body, section_length) = nom::number::complete::le_u64(body)?;
432 if body.len() > 0 {
433 return Err(nom::Err::Failure(ParsingError::SectionHeaderBlockOptionsUnspported));
434 }
435 Ok((body, ParsedSectionHeader { section_length }))
436 }
437}
438
439impl<'a> PcapNgBlock<'a> for ParsedInterfaceDescription<'a> {
440 const BLOCK_TYPE: BlockType = BlockType::InterfaceDescription;
441
442 fn parse(body: &'a [u8]) -> PcapResult<'a, Self> {
466 let (body, link_type_code) = nom::number::complete::le_u16(body)?;
467 let link_type = LinkType::from_repr(link_type_code)
468 .ok_or(nom::Err::Failure(ParsingError::UnsupportedLinkType(link_type_code)))?;
469 let (body, _reserved) = nom::number::complete::le_u16(body)?;
470 let (body, snap_len) = nom::number::complete::le_u32(body)?;
471
472 let (body, options) = parse_idb_options(body)?;
473 Ok((body, ParsedInterfaceDescription { link_type, snap_len, options }))
474 }
475}
476
477impl<'a> PcapNgBlock<'a> for ParsedEnhancedPacket<'a> {
478 const BLOCK_TYPE: BlockType = BlockType::EnhancedPacket;
479
480 fn parse(body: &'a [u8]) -> PcapResult<'a, Self> {
515 let (body, interface_id) = nom::number::complete::le_u32(body)?;
516 let (body, timestamp_high) = nom::number::complete::le_u32(body)?;
517 let (body, timestamp_low) = nom::number::complete::le_u32(body)?;
518 let timestamp = std::time::UNIX_EPOCH
519 + std::time::Duration::from_micros(
520 (u64::from(timestamp_high) << 32) | u64::from(timestamp_low),
521 );
522 let (body, captured_length) = nom::number::complete::le_u32(body)?;
523 let (body, original_length) = nom::number::complete::le_u32(body)?;
524 let (body, packet_data) = nom::bytes::complete::take(captured_length)(body)?;
525 let padded_len = captured_length.next_multiple_of(4) - captured_length;
526 let (body, _) = nom::bytes::complete::take(padded_len)(body)?;
527 if body.len() > 0 {
528 return Err(nom::Err::Failure(ParsingError::EnhancedPacketBlockOptionsUnspported));
529 }
530 Ok((
531 body,
532 ParsedEnhancedPacket {
533 interface_id,
534 timestamp,
535 captured_length,
536 original_length,
537 packet_data,
538 },
539 ))
540 }
541}
542
543fn parse_block<'a, T: PcapNgBlock<'a>>(input: &'a [u8]) -> PcapResult<'a, T> {
544 let (input, block_type) = nom::number::complete::le_u32(input)?;
545 if block_type != u32::from(T::BLOCK_TYPE) {
546 return Err(nom::Err::Error(ParsingError::UnexpectedBlockType {
547 got: block_type,
548 want: T::BLOCK_TYPE,
549 }));
550 }
551
552 let (input, total_length) = nom::number::complete::le_u32(input)?;
555 if T::BLOCK_TYPE == BlockType::SectionHeader {
556 let (_, byte_order_magic) =
557 nom::combinator::peek(nom::bytes::complete::take(4usize)).parse(input)?;
558 if byte_order_magic == BYTE_ORDER_MAGIC.to_be_bytes() {
559 return Err(nom::Err::Failure(ParsingError::BigEndianNotSupported));
560 }
561 if byte_order_magic != BYTE_ORDER_MAGIC.to_le_bytes() {
562 return Err(nom::Err::Failure(ParsingError::InvalidMagic));
563 }
564 }
565 let body_len = total_length
566 .checked_sub(BLOCK_HEADER_FOOTER_LEN)
567 .ok_or(nom::Err::Failure(ParsingError::BlockLengthTooShort(total_length)))?;
568
569 let (input, body) = nom::bytes::complete::take(body_len)(input)?;
570 let (input, verify_length) = nom::number::complete::le_u32(input)?;
571 if total_length != verify_length {
572 return Err(nom::Err::Failure(ParsingError::BlockLengthsDisagree {
573 header: total_length,
574 footer: verify_length,
575 }));
576 }
577
578 let (_, result) = T::parse(body)?;
579 Ok((input, result))
580}
581
582pub fn parse_pcapng<'a>(input: &'a [u8]) -> Result<PcapNgSection<'a>, ParsingError<'a>> {
588 let (input, header) = parse_block::<ParsedSectionHeader>(input).finish()?;
589 let (input, interfaces) =
590 nom::multi::many1(parse_block::<ParsedInterfaceDescription<'a>>).parse(input).finish()?;
591 Ok(PcapNgSection { header, interfaces, input })
592}
593
594#[derive(IntoBytes, Immutable)]
598#[repr(C)]
599pub struct SectionHeaderBlock {
600 pub block_type: U32,
602 pub block_total_length: U32,
604 pub byte_order_magic: U32,
606 pub major_version: U16,
608 pub minor_version: U16,
610 pub section_length: U64,
612 pub block_total_length2: U32,
614}
615
616impl SectionHeaderBlock {
617 pub const SIZE: u32 = std::mem::size_of::<Self>() as u32;
619}
620
621pub const UNSPECIFIED_SECTION_SIZE: u64 = u64::MAX;
630
631impl SectionHeaderBlock {
632 pub fn new() -> Self {
638 Self {
639 block_type: U32::new(BlockType::SectionHeader.into()),
640 block_total_length: U32::new(Self::SIZE),
641 byte_order_magic: U32::new(BYTE_ORDER_MAGIC),
642 major_version: U16::new(MAJOR_VERSION),
643 minor_version: U16::new(MINOR_VERSION),
644 section_length: U64::new(UNSPECIFIED_SECTION_SIZE),
645 block_total_length2: U32::new(Self::SIZE),
646 }
647 }
648}
649
650#[derive(IntoBytes, Immutable)]
654#[repr(C)]
655pub struct InterfaceDescriptionBlockHeader {
656 pub block_type: U32,
658 pub block_total_length: U32,
660 pub link_type: U16,
662 pub reserved: U16,
664 pub snap_len: U32,
666}
667
668impl InterfaceDescriptionBlockHeader {
669 pub const SIZE: u32 = std::mem::size_of::<Self>() as u32;
671
672 pub fn new(link_type: LinkType, block_total_length: u32) -> Self {
676 Self {
677 block_type: U32::new(BlockType::InterfaceDescription.into()),
678 block_total_length: U32::new(block_total_length),
679 link_type: U16::new(link_type.into()),
680 reserved: U16::new(0),
681 snap_len: U32::new(0),
682 }
683 }
684}
685
686pub const BLOCK_LEN_FOOTER_SIZE: u32 = 4;
688
689pub fn write_interface_description_block<W: std::io::Write>(
691 mut writer: W,
692 link_type: LinkType,
693 interface_name: &str,
694) -> Result<(), std::io::Error> {
695 let total_len = InterfaceDescriptionBlockHeader::SIZE
696 + OptionHeader::SIZE + u32::try_from(interface_name.len().next_multiple_of(4)).unwrap()
698 + OptionHeader::SIZE + BLOCK_LEN_FOOTER_SIZE;
700 let idb_header = InterfaceDescriptionBlockHeader::new(link_type, total_len);
701 writer.write_all(idb_header.as_bytes())?;
702 write_if_name_option(&mut writer, interface_name)?;
703 writer.write_all(OptionHeader::new_end_of_opt().as_bytes())?;
704 writer.write_all(&total_len.to_le_bytes())?;
705 Ok(())
706}
707
708pub fn write_prelude<W: std::io::Write>(
710 mut writer: W,
711 link_type: LinkType,
712 interface_name: &str,
713) -> Result<(), std::io::Error> {
714 writer.write_all(SectionHeaderBlock::new().as_bytes())?;
715 write_interface_description_block(&mut writer, link_type, interface_name)?;
716 Ok(())
717}
718
719#[derive(IntoBytes, Immutable)]
723#[repr(C)]
724pub struct EnhancedPacketBlockHeader {
725 pub block_type: U32,
727 pub block_total_length: U32,
729 pub interface_id: U32,
731 pub timestamp_upper: U32,
733 pub timestamp_lower: U32,
735 pub captured_packet_length: U32,
737 pub original_packet_length: U32,
739}
740
741impl EnhancedPacketBlockHeader {
742 pub const SIZE: u32 = std::mem::size_of::<Self>() as u32;
744
745 pub fn new(
749 block_total_length: u32,
750 interface_id: u32,
751 timestamp: std::time::SystemTime,
752 captured_packet_length: u32,
753 original_packet_length: u32,
754 ) -> Self {
755 let timestamp = timestamp
756 .duration_since(std::time::UNIX_EPOCH)
757 .expect("current time should be after UNIX epoch")
758 .as_micros();
759 let timestamp = u64::try_from(timestamp)
760 .expect("current time overflows microseconds since UNIX epoch in u64");
761 Self {
762 block_type: U32::new(BlockType::EnhancedPacket.into()),
763 block_total_length: U32::new(block_total_length),
764 interface_id: U32::new(interface_id),
765 timestamp_upper: U32::new((timestamp >> 32) as u32),
766 timestamp_lower: U32::new(timestamp as u32),
767 captured_packet_length: U32::new(captured_packet_length),
768 original_packet_length: U32::new(original_packet_length),
769 }
770 }
771}
772
773pub fn write_enhanced_packet_block<W: std::io::Write>(
775 mut writer: W,
776 interface_id: u32,
777 timestamp: std::time::SystemTime,
778 packet: &[u8],
779 original_packet_len: u32,
780) -> Result<(), std::io::Error> {
781 let packet_padded_len = packet.len().next_multiple_of(4);
782 let total_len = EnhancedPacketBlockHeader::SIZE
783 + u32::try_from(packet_padded_len).unwrap()
784 + BLOCK_LEN_FOOTER_SIZE;
785 let header = EnhancedPacketBlockHeader::new(
786 total_len,
787 interface_id,
788 timestamp,
789 u32::try_from(packet.len()).unwrap(),
790 original_packet_len,
791 );
792 writer.write_all(header.as_bytes())?;
793 writer.write_all(packet)?;
794 writer.write_all(&[0; 3][..packet_padded_len - packet.len()])?;
795 writer.write_all(&total_len.to_le_bytes())?;
796 Ok(())
797}
798
799#[derive(IntoBytes, Immutable)]
801#[repr(C)]
802pub struct OptionHeader {
803 pub code: U16,
805 pub length: U16,
807}
808
809impl OptionHeader {
810 pub const SIZE: u32 = std::mem::size_of::<Self>() as u32;
812
813 pub fn new_end_of_opt() -> Self {
815 Self {
816 code: U16::new(InterfaceDescriptionOptionCode::EndOfOpt.into()),
817 length: U16::new(0),
818 }
819 }
820
821 fn new_if_name(length: u16) -> Self {
822 Self {
823 code: U16::new(InterfaceDescriptionOptionCode::IfName.into()),
824 length: U16::new(length),
825 }
826 }
827}
828
829pub fn write_if_name_option<W: std::io::Write>(mut writer: W, name: &str) -> std::io::Result<()> {
833 let len = name.len();
834 let total_len = len.next_multiple_of(4);
835 writer.write_all(OptionHeader::new_if_name(len.try_into().unwrap()).as_bytes())?;
836 writer.write_all(name.as_bytes())?;
837 writer.write_all(&[0; 3][..total_len - len])?;
838 Ok(())
839}
840
841#[cfg(test)]
842mod tests {
843 use super::*;
844 use zerocopy::IntoBytes;
845 use zerocopy::byteorder::little_endian::U16;
846
847 #[test]
848 fn test_parse_idb_options() {
849 let mut buf = Vec::new();
850 write_if_name_option(&mut buf, "lo").expect("write if name option");
851 buf.extend_from_slice(OptionHeader::new_end_of_opt().as_bytes());
852
853 let (rem, options) = parse_idb_options(&buf).expect("parse options failed");
854 assert!(rem.is_empty());
855 assert_eq!(
856 options,
857 ParsedInterfaceDescriptionOptions { if_name: Some(Cow::Borrowed("lo")) }
858 );
859 }
860
861 #[test]
862 fn test_write_idb_includes_end_of_opt() {
863 let mut buf = Vec::new();
864 let if_name = "lo";
865 write_interface_description_block(&mut buf, LinkType::Ethernet, if_name)
866 .expect("write interface description block");
867
868 let (remaining, _parsed_idb) =
870 parse_block::<ParsedInterfaceDescription<'_>>(&buf).expect("parse block failed");
871 assert!(remaining.is_empty());
872
873 let option_start_offset = std::mem::size_of::<InterfaceDescriptionBlockHeader>();
876 let option_end_offset = buf.len()
877 - usize::try_from(BLOCK_LEN_FOOTER_SIZE).expect("block len footer size fits in usize");
878 let mut options_buf = &buf[option_start_offset..option_end_offset];
879
880 let mut end_of_opt_found = false;
881 while !options_buf.is_empty() {
882 let (rem, option) = parse_idb_option(options_buf).expect("parse option failed");
883 options_buf = rem;
884 if let InterfaceDescriptionOption::EndOfOpt = option {
885 assert_eq!(options_buf, &[] as &[u8]);
886 end_of_opt_found = true;
887 break;
888 }
889 }
890 assert!(end_of_opt_found);
891 }
892
893 #[test]
894 fn test_parse_options_succeed_no_end_of_opt() {
895 let mut buf = Vec::new();
896 write_if_name_option(&mut buf, "lo").expect("write if name option");
897
898 let (rem, options) = parse_idb_options(&buf).expect("parse options failed");
899 assert!(rem.is_empty());
900 assert_eq!(
901 options,
902 ParsedInterfaceDescriptionOptions { if_name: Some(Cow::Borrowed("lo")) }
903 );
904 }
905
906 #[test]
907 fn test_parse_options_fail_non_zero_len_end_of_opt() {
908 let mut buf = Vec::new();
909 buf.extend_from_slice(
910 (OptionHeader {
911 code: U16::new(InterfaceDescriptionOptionCode::EndOfOpt.into()),
912 length: U16::new(1),
913 })
914 .as_bytes(),
915 );
916
917 let res = parse_idb_options(&buf);
918 assert_eq!(res, Err(nom::Err::Failure(ParsingError::EndOfOptLengthNotZero(1))));
919 }
920
921 #[test]
922 fn test_parse_options_fail_if_name_after_end_of_opt() {
923 let mut buf = Vec::new();
924 buf.extend_from_slice(OptionHeader::new_end_of_opt().as_bytes());
925 write_if_name_option(&mut buf, "lo").expect("write if name option");
926
927 let res = parse_idb_options(&buf);
928 assert_eq!(res, Err(nom::Err::Failure(ParsingError::DataAfterEndOfOpt)));
929 }
930
931 #[test]
932 fn test_parse_shb_invalid_magic() {
933 let mut buf = Vec::new();
934 let mut shb = SectionHeaderBlock::new();
935 shb.byte_order_magic = U32::new(BYTE_ORDER_MAGIC + 1);
936 buf.extend_from_slice(shb.as_bytes());
937
938 let res = parse_block::<ParsedSectionHeader>(&buf);
939 assert_eq!(res, Err(nom::Err::Failure(ParsingError::InvalidMagic)));
940 }
941
942 #[test]
943 fn test_parse_shb_unsupported_major() {
944 let mut buf = Vec::new();
945 let mut shb = SectionHeaderBlock::new();
946 let wrong_major_version = 2;
947 shb.major_version = U16::new(wrong_major_version);
948 buf.extend_from_slice(shb.as_bytes());
949
950 let res = parse_block::<ParsedSectionHeader>(&buf);
951 assert_eq!(
952 res,
953 Err(nom::Err::Failure(ParsingError::UnsupportedMajorVersion(wrong_major_version)))
954 );
955 }
956
957 #[test]
958 fn test_parse_shb_unsupported_minor() {
959 let mut buf = Vec::new();
960 let mut shb = SectionHeaderBlock::new();
961 let wrong_minor_version = 1;
962 shb.minor_version = U16::new(wrong_minor_version);
963 buf.extend_from_slice(shb.as_bytes());
964
965 let res = parse_block::<ParsedSectionHeader>(&buf);
966 assert_eq!(
967 res,
968 Err(nom::Err::Failure(ParsingError::UnsupportedMinorVersion(wrong_minor_version)))
969 );
970 }
971
972 #[test]
973 fn test_parse_pcap_valid() {
974 let mut buf = Vec::new();
975
976 let shb = SectionHeaderBlock::new();
978 buf.extend_from_slice(shb.as_bytes());
979
980 write_interface_description_block(&mut buf, LinkType::Ethernet, "lo")
982 .expect("write interface description block");
983
984 let timestamp = std::time::UNIX_EPOCH + std::time::Duration::from_secs(1_000_000_000);
986 let packet = [1, 2, 3, 4, 5];
987 let packet_len = packet.len() as u32;
988 write_enhanced_packet_block(
989 &mut buf, 0, timestamp, &packet, packet_len,
991 )
992 .expect("write enhanced packet block");
993
994 let timestamp2 = timestamp + std::time::Duration::from_secs(2_000_000_000);
996 let packet2 = [6, 7, 8];
997 let packet_len2 = packet2.len() as u32;
998 write_enhanced_packet_block(
999 &mut buf,
1000 0, timestamp2,
1002 &packet2,
1003 packet_len2,
1004 )
1005 .expect("write enhanced packet block");
1006
1007 let file = parse_pcapng(&buf).expect("parse pcap failed");
1008
1009 assert_eq!(file.header, ParsedSectionHeader { section_length: u64::MAX });
1010 assert_eq!(
1011 file.interfaces,
1012 vec![ParsedInterfaceDescription {
1013 link_type: LinkType::Ethernet,
1014 snap_len: 0,
1015 options: ParsedInterfaceDescriptionOptions { if_name: Some(Cow::Borrowed("lo")) },
1016 }]
1017 );
1018
1019 let packets: Vec<_> =
1020 file.packet_blocks().collect::<Result<Vec<_>, _>>().expect("iterate failed");
1021
1022 let expected = vec![
1023 ParsedEnhancedPacket {
1024 interface_id: 0,
1025 timestamp,
1026 captured_length: packet_len,
1027 original_length: packet_len,
1028 packet_data: &packet,
1029 },
1030 ParsedEnhancedPacket {
1031 interface_id: 0,
1032 timestamp: timestamp2,
1033 captured_length: packet_len2,
1034 original_length: packet_len2,
1035 packet_data: &packet2,
1036 },
1037 ];
1038 assert_eq!(packets, expected);
1039 }
1040 #[test]
1041 fn test_parse_block_too_short() {
1042 let mut buf = Vec::new();
1043 let mut shb = SectionHeaderBlock::new();
1044 let short_block_len = BLOCK_HEADER_FOOTER_LEN - 1;
1045 shb.block_total_length = U32::new(short_block_len);
1046 buf.extend_from_slice(shb.as_bytes());
1047
1048 let res = parse_block::<ParsedSectionHeader>(&buf);
1049 assert_eq!(res, Err(nom::Err::Failure(ParsingError::BlockLengthTooShort(short_block_len))));
1050 }
1051
1052 #[test]
1053 fn test_parse_block_length_disagrees() {
1054 let mut buf = Vec::new();
1055 let mut shb = SectionHeaderBlock::new();
1056 shb.block_total_length2 = U32::new(SectionHeaderBlock::SIZE + 1);
1057 buf.extend_from_slice(shb.as_bytes());
1058
1059 let res = parse_block::<ParsedSectionHeader>(&buf);
1060 assert_eq!(
1061 res,
1062 Err(nom::Err::Failure(ParsingError::BlockLengthsDisagree {
1063 header: SectionHeaderBlock::SIZE,
1064 footer: SectionHeaderBlock::SIZE + 1,
1065 }))
1066 );
1067 }
1068
1069 #[test]
1070 fn test_parse_shb_big_endian_not_supported() {
1071 let mut buf = Vec::new();
1072 buf.extend_from_slice(&u32::from(BlockType::SectionHeader).to_be_bytes());
1073 buf.extend_from_slice(&SectionHeaderBlock::SIZE.to_be_bytes());
1076 buf.extend_from_slice(&BYTE_ORDER_MAGIC.to_be_bytes());
1077 let res = parse_block::<ParsedSectionHeader>(&buf);
1081 assert_eq!(res, Err(nom::Err::Failure(ParsingError::BigEndianNotSupported)));
1082 }
1083}