pcap_file/pcapng/
writer.rs

1use std::io::Write;
2
3use byteorder_slice::{BigEndian, ByteOrder, LittleEndian};
4
5use super::blocks::block_common::{Block, PcapNgBlock};
6use super::blocks::interface_description::InterfaceDescriptionBlock;
7use super::blocks::section_header::SectionHeaderBlock;
8use super::blocks::SECTION_HEADER_BLOCK;
9use super::RawBlock;
10use crate::{Endianness, PcapError, PcapResult};
11
12
13/// Writes a PcapNg to a writer.
14///
15/// # Examples
16/// ```rust,no_run
17/// use std::fs::File;
18///
19/// use pcap_file::pcapng::{PcapNgReader, PcapNgWriter};
20///
21/// let file_in = File::open("test.pcapng").expect("Error opening file");
22/// let mut pcapng_reader = PcapNgReader::new(file_in).unwrap();
23///
24/// let mut out = Vec::new();
25/// let mut pcapng_writer = PcapNgWriter::new(out).unwrap();
26///
27/// // Read test.pcapng
28/// while let Some(block) = pcapng_reader.next_block() {
29///     // Check if there is no error
30///     let block = block.unwrap();
31///
32///     // Write back parsed Block
33///     pcapng_writer.write_block(&block).unwrap();
34/// }
35/// ```
36pub struct PcapNgWriter<W: Write> {
37    section: SectionHeaderBlock<'static>,
38    interfaces: Vec<InterfaceDescriptionBlock<'static>>,
39    writer: W,
40}
41
42impl<W: Write> PcapNgWriter<W> {
43    /// Creates a new [`PcapNgWriter`] from an existing writer.
44    ///
45    /// Defaults to the native endianness of the CPU.
46    ///
47    /// Writes this global pcapng header to the file:
48    /// ```rust, ignore
49    /// Self {
50    ///     endianness: Endianness::Native,
51    ///     major_version: 1,
52    ///     minor_version: 0,
53    ///     section_length: -1,
54    ///     options: vec![]
55    /// }
56    /// ```
57    ///
58    ///
59    /// # Errors
60    /// The writer can't be written to.
61    pub fn new(writer: W) -> PcapResult<Self> {
62        Self::with_endianness(writer, Endianness::native())
63    }
64
65    /// Creates a new [`PcapNgWriter`] from an existing writer with the given endianness.
66    pub fn with_endianness(writer: W, endianness: Endianness) -> PcapResult<Self> {
67        let section = SectionHeaderBlock { endianness, ..Default::default() };
68
69        Self::with_section_header(writer, section)
70    }
71
72    /// Creates a new [`PcapNgWriter`] from an existing writer with the given section header.
73    pub fn with_section_header(mut writer: W, section: SectionHeaderBlock<'static>) -> PcapResult<Self> {
74        match section.endianness {
75            Endianness::Big => section.clone().into_block().write_to::<BigEndian, _>(&mut writer).map_err(PcapError::IoError)?,
76            Endianness::Little => section.clone().into_block().write_to::<LittleEndian, _>(&mut writer).map_err(PcapError::IoError)?,
77        };
78
79        Ok(Self { section, interfaces: vec![], writer })
80    }
81
82    /// Writes a [`Block`].
83    ///
84    /// # Example
85    /// ```rust,no_run
86    /// use std::borrow::Cow;
87    /// use std::fs::File;
88    /// use std::time::Duration;
89    ///
90    /// use pcap_file::pcapng::blocks::enhanced_packet::EnhancedPacketBlock;
91    /// use pcap_file::pcapng::blocks::interface_description::InterfaceDescriptionBlock;
92    /// use pcap_file::pcapng::{PcapNgBlock, PcapNgWriter};
93    /// use pcap_file::DataLink;
94    ///
95    /// let data = [0u8; 10];
96    ///
97    /// let interface = InterfaceDescriptionBlock {
98    ///     linktype: DataLink::ETHERNET,
99    ///     snaplen: 0xFFFF,
100    ///     options: vec![],
101    /// };
102    ///
103    /// let packet = EnhancedPacketBlock {
104    ///     interface_id: 0,
105    ///     timestamp: Duration::from_secs(0),
106    ///     original_len: data.len() as u32,
107    ///     data: Cow::Borrowed(&data),
108    ///     options: vec![],
109    /// };
110    ///
111    /// let file = File::create("out.pcap").expect("Error creating file");
112    /// let mut pcap_ng_writer = PcapNgWriter::new(file).unwrap();
113    ///
114    /// pcap_ng_writer.write_block(&interface.into_block()).unwrap();
115    /// pcap_ng_writer.write_block(&packet.into_block()).unwrap();
116    /// ```
117    pub fn write_block(&mut self, block: &Block) -> PcapResult<usize> {
118        match block {
119            Block::SectionHeader(a) => {
120                self.section = a.clone().into_owned();
121                self.interfaces.clear();
122            },
123            Block::InterfaceDescription(a) => {
124                self.interfaces.push(a.clone().into_owned());
125            },
126            Block::InterfaceStatistics(a) => {
127                if a.interface_id as usize >= self.interfaces.len() {
128                    return Err(PcapError::InvalidInterfaceId(a.interface_id));
129                }
130            },
131            Block::EnhancedPacket(a) => {
132                if a.interface_id as usize >= self.interfaces.len() {
133                    return Err(PcapError::InvalidInterfaceId(a.interface_id));
134                }
135            },
136
137            _ => (),
138        }
139
140        match self.section.endianness {
141            Endianness::Big => block.write_to::<BigEndian, _>(&mut self.writer).map_err(PcapError::IoError),
142            Endianness::Little => block.write_to::<LittleEndian, _>(&mut self.writer).map_err(PcapError::IoError),
143        }
144    }
145
146    /// Writes a [`PcapNgBlock`].
147    ///
148    /// # Example
149    /// ```rust,no_run
150    /// use std::borrow::Cow;
151    /// use std::fs::File;
152    /// use std::time::Duration;
153    ///
154    /// use pcap_file::pcapng::blocks::enhanced_packet::EnhancedPacketBlock;
155    /// use pcap_file::pcapng::blocks::interface_description::InterfaceDescriptionBlock;
156    /// use pcap_file::pcapng::{PcapNgBlock, PcapNgWriter};
157    /// use pcap_file::DataLink;
158    ///
159    /// let data = [0u8; 10];
160    ///
161    /// let interface = InterfaceDescriptionBlock {
162    ///     linktype: DataLink::ETHERNET,
163    ///     snaplen: 0xFFFF,
164    ///     options: vec![],
165    /// };
166    ///
167    /// let packet = EnhancedPacketBlock {
168    ///     interface_id: 0,
169    ///     timestamp: Duration::from_secs(0),
170    ///     original_len: data.len() as u32,
171    ///     data: Cow::Borrowed(&data),
172    ///     options: vec![],
173    /// };
174    ///
175    /// let file = File::create("out.pcap").expect("Error creating file");
176    /// let mut pcap_ng_writer = PcapNgWriter::new(file).unwrap();
177    ///
178    /// pcap_ng_writer.write_pcapng_block(interface).unwrap();
179    /// pcap_ng_writer.write_pcapng_block(packet).unwrap();
180    /// ```
181    pub fn write_pcapng_block<'a, B: PcapNgBlock<'a>>(&mut self, block: B) -> PcapResult<usize> {
182        self.write_block(&block.into_block())
183    }
184
185    /// Writes a [`RawBlock`].
186    ///
187    /// Doesn't check the validity of the written blocks.
188    pub fn write_raw_block(&mut self, block: &RawBlock) -> PcapResult<usize> {
189        return match self.section.endianness {
190            Endianness::Big => inner::<BigEndian, _>(&mut self.section, block, &mut self.writer),
191            Endianness::Little => inner::<LittleEndian, _>(&mut self.section, block, &mut self.writer),
192        };
193
194        fn inner<B: ByteOrder, W: Write>(section: &mut SectionHeaderBlock, block: &RawBlock, writer: &mut W) -> PcapResult<usize> {
195            if block.type_ == SECTION_HEADER_BLOCK {
196                *section = block.clone().try_into_block::<B>()?.into_owned().into_section_header().unwrap();
197            }
198
199            block.write_to::<B, _>(writer).map_err(PcapError::IoError)
200        }
201    }
202
203    /// Consumes [`Self`], returning the wrapped writer.
204    pub fn into_inner(self) -> W {
205        self.writer
206    }
207
208    /// Gets a reference to the underlying writer.
209    pub fn get_ref(&self) -> &W {
210        &self.writer
211    }
212
213    /// Gets a mutable reference to the underlying writer.
214    ///
215    /// You should not be used unless you really know what you're doing
216    pub fn get_mut(&mut self) -> &mut W {
217        &mut self.writer
218    }
219
220    /// Returns the current [`SectionHeaderBlock`].
221    pub fn section(&self) -> &SectionHeaderBlock<'static> {
222        &self.section
223    }
224
225    /// Returns all the current [`InterfaceDescriptionBlock`].
226    pub fn interfaces(&self) -> &[InterfaceDescriptionBlock<'static>] {
227        &self.interfaces
228    }
229}