pcapsql_core/pcap/
reader.rs1use std::fs::File;
10use std::io::{Read, Seek, SeekFrom};
11use std::path::Path;
12
13use crate::error::{Error, PcapError as OurPcapError};
14use crate::io::{Compression, FileDecoder, GenericPcapReader, PacketRef, PcapFormat, RawPacket};
15
16pub struct PcapReader {
42 inner: GenericPcapReader<FileDecoder>,
43}
44
45impl PcapReader {
46 pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
50 let path = path.as_ref();
51
52 let mut file = File::open(path).map_err(|_| {
54 Error::Pcap(OurPcapError::FileNotFound {
55 path: path.display().to_string(),
56 })
57 })?;
58
59 let mut header = [0u8; 6];
60 let bytes_read = file.read(&mut header).map_err(|_| {
61 Error::Pcap(OurPcapError::InvalidFormat {
62 reason: "File too short to read header".to_string(),
63 })
64 })?;
65
66 if bytes_read < 4 {
67 return Err(Error::Pcap(OurPcapError::InvalidFormat {
68 reason: "File too short".to_string(),
69 }));
70 }
71
72 let compression = Compression::detect(&header);
74
75 file.seek(SeekFrom::Start(0)).map_err(Error::Io)?;
77
78 let decoder = FileDecoder::new(file, compression).map_err(|e| {
80 Error::Pcap(OurPcapError::InvalidFormat {
81 reason: format!("Failed to create decoder: {e}"),
82 })
83 })?;
84
85 let mut temp_decoder = decoder;
89 let mut magic = [0u8; 4];
90 temp_decoder.read_exact(&mut magic).map_err(|_| {
91 Error::Pcap(OurPcapError::InvalidFormat {
92 reason: "File too short to read magic number".to_string(),
93 })
94 })?;
95
96 let format = PcapFormat::detect(&magic)?;
98
99 drop(temp_decoder);
101 let file = File::open(path)?;
102 let decoder = FileDecoder::new(file, compression).map_err(|e| {
103 Error::Pcap(OurPcapError::InvalidFormat {
104 reason: format!("Failed to create decoder: {e}"),
105 })
106 })?;
107
108 let inner = GenericPcapReader::with_format(decoder, format)?;
110
111 Ok(Self { inner })
112 }
113
114 #[inline]
116 pub fn link_type(&self) -> u16 {
117 self.inner.link_type() as u16
118 }
119
120 #[inline]
122 pub fn frame_count(&self) -> u64 {
123 self.inner.frame_count()
124 }
125
126 #[inline]
130 pub fn next_packet(&mut self) -> Result<Option<RawPacket>, Error> {
131 self.inner.next_packet()
132 }
133
134 #[inline]
142 pub fn process_packets<F>(&mut self, max: usize, f: F) -> Result<usize, Error>
143 where
144 F: FnMut(PacketRef<'_>) -> Result<(), Error>,
145 {
146 self.inner.process_packets(max, f)
147 }
148}
149
150impl Iterator for PcapReader {
152 type Item = Result<RawPacket, Error>;
153
154 fn next(&mut self) -> Option<Self::Item> {
155 match self.next_packet() {
156 Ok(Some(packet)) => Some(Ok(packet)),
157 Ok(None) => None,
158 Err(e) => Some(Err(e)),
159 }
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166 use flate2::write::GzEncoder;
167 use flate2::Compression as GzCompression;
168 use std::io::Write;
169 use tempfile::NamedTempFile;
170
171 #[test]
172 fn test_compression_detection() {
173 let gzip_data = [0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00];
175 assert_eq!(Compression::detect(&gzip_data), Compression::Gzip);
176
177 let pcap_data = [0xd4, 0xc3, 0xb2, 0xa1, 0x00, 0x00];
179 assert_eq!(Compression::detect(&pcap_data), Compression::None);
180 }
181
182 #[test]
183 fn test_create_and_read_gzip_pcap() {
184 let pcap_data = create_minimal_pcap();
186
187 let temp = NamedTempFile::with_suffix(".pcap.gz").unwrap();
189 {
190 let file = File::create(temp.path()).unwrap();
191 let mut encoder = GzEncoder::new(file, GzCompression::default());
192 encoder.write_all(&pcap_data).unwrap();
193 encoder.finish().unwrap();
194 }
195
196 let reader = PcapReader::open(temp.path());
198 assert!(
199 reader.is_ok(),
200 "Failed to open gzipped PCAP: {:?}",
201 reader.err()
202 );
203 }
204
205 #[cfg(feature = "compress-zstd")]
206 #[test]
207 fn test_create_and_read_zstd_pcap() {
208 let pcap_data = create_minimal_pcap();
210
211 let temp = NamedTempFile::with_suffix(".pcap.zst").unwrap();
213 {
214 let file = File::create(temp.path()).unwrap();
215 let mut encoder = zstd::Encoder::new(file, 3).unwrap();
216 encoder.write_all(&pcap_data).unwrap();
217 encoder.finish().unwrap();
218 }
219
220 let reader = PcapReader::open(temp.path());
222 assert!(
223 reader.is_ok(),
224 "Failed to open zstd PCAP: {:?}",
225 reader.err()
226 );
227 }
228
229 fn create_minimal_pcap() -> Vec<u8> {
231 let mut data = Vec::new();
232
233 data.extend_from_slice(&[0xd4, 0xc3, 0xb2, 0xa1]); data.extend_from_slice(&[0x02, 0x00]); data.extend_from_slice(&[0x04, 0x00]); data.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]); data.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]); data.extend_from_slice(&[0xff, 0xff, 0x00, 0x00]); data.extend_from_slice(&[0x01, 0x00, 0x00, 0x00]); let packet_data = [
244 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x08, 0x00, ];
249
250 let ts_sec: u32 = 1000000000;
251 let ts_usec: u32 = 0;
252 let caplen: u32 = packet_data.len() as u32;
253 let origlen: u32 = packet_data.len() as u32;
254
255 data.extend_from_slice(&ts_sec.to_le_bytes());
256 data.extend_from_slice(&ts_usec.to_le_bytes());
257 data.extend_from_slice(&caplen.to_le_bytes());
258 data.extend_from_slice(&origlen.to_le_bytes());
259 data.extend_from_slice(&packet_data);
260
261 data
262 }
263}