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}