1use std::fs::File;
4use std::io::{BufReader, Cursor, Read, Seek, SeekFrom};
5
6use bytes::Bytes;
7use pcap_file::pcap::PcapReader as PcapFileReader;
8use pcap_file::pcapng::Block;
9use pcap_file::pcapng::PcapNgReader as PcapNgFileReader;
10
11use crate::error::{PacketError, Result};
12use crate::packet::Packet;
13
14use super::{CaptureFormat, CapturedPacket, LinkType, PcapMetadata};
15
16fn detect_format(magic: &[u8; 4]) -> Result<CaptureFormat> {
18 let le = u32::from_le_bytes(*magic);
19 let be = u32::from_be_bytes(*magic);
20
21 if le == 0x0A0D_0D0A || be == 0x0A0D_0D0A {
23 return Ok(CaptureFormat::PcapNg);
24 }
25
26 if le == 0xA1B2_C3D4 || be == 0xA1B2_C3D4 || le == 0xA1B2_3C4D || be == 0xA1B2_3C4D {
28 return Ok(CaptureFormat::Pcap);
29 }
30
31 Err(PacketError::Io(
32 "unknown capture file format (not PCAP or PcapNG)".into(),
33 ))
34}
35
36pub fn rdpcap(path: impl AsRef<Path>) -> Result<Vec<CapturedPacket>> {
41 let iter = CaptureIterator::open(&path)?;
42 iter.collect()
43}
44
45use std::path::Path;
46
47pub struct PcapIterator<R: Read> {
55 inner: PcapFileReader<R>,
56 link_type: LinkType,
57}
58
59impl PcapIterator<BufReader<File>> {
60 pub fn open(path: impl AsRef<Path>) -> Result<Self> {
62 let file = File::open(path.as_ref()).map_err(|e| {
63 PacketError::Io(format!("failed to open {}: {}", path.as_ref().display(), e))
64 })?;
65 let reader = BufReader::new(file);
66 Self::from_reader(reader)
67 }
68}
69
70impl<R: Read> PcapIterator<R> {
71 pub fn from_reader(reader: R) -> Result<Self> {
73 let pcap_reader = PcapFileReader::new(reader)
74 .map_err(|e| PacketError::Io(format!("invalid PCAP: {e}")))?;
75 let link_type = LinkType(u32::from(pcap_reader.header().datalink));
76 Ok(Self {
77 inner: pcap_reader,
78 link_type,
79 })
80 }
81
82 pub fn link_type(&self) -> LinkType {
84 self.link_type
85 }
86}
87
88impl<R: Read> Iterator for PcapIterator<R> {
89 type Item = Result<CapturedPacket>;
90
91 fn next(&mut self) -> Option<Self::Item> {
92 match self.inner.next_packet() {
93 Some(Ok(pcap_pkt)) => {
94 let ts = pcap_pkt.timestamp;
95 let data = Bytes::copy_from_slice(&pcap_pkt.data);
96 let mut pkt = Packet::from_bytes(data);
97 let _ = pkt.parse();
98 Some(Ok(CapturedPacket {
99 packet: pkt,
100 metadata: PcapMetadata {
101 timestamp: ts,
102 orig_len: pcap_pkt.orig_len,
103 interface_id: None,
104 comment: None,
105 },
106 }))
107 },
108 Some(Err(e)) => Some(Err(PacketError::Io(format!("PCAP read error: {e}")))),
109 None => None,
110 }
111 }
112}
113
114pub struct PcapNgIterator<R: Read> {
122 inner: PcapNgFileReader<R>,
123}
124
125impl PcapNgIterator<BufReader<File>> {
126 pub fn open(path: impl AsRef<Path>) -> Result<Self> {
128 let file = File::open(path.as_ref()).map_err(|e| {
129 PacketError::Io(format!("failed to open {}: {}", path.as_ref().display(), e))
130 })?;
131 let reader = BufReader::new(file);
132 Self::from_reader(reader)
133 }
134}
135
136impl<R: Read> PcapNgIterator<R> {
137 pub fn from_reader(reader: R) -> Result<Self> {
139 let ng_reader = PcapNgFileReader::new(reader)
140 .map_err(|e| PacketError::Io(format!("invalid PcapNG: {e}")))?;
141 Ok(Self { inner: ng_reader })
142 }
143
144 pub fn link_type(&self) -> LinkType {
146 self.inner
147 .interfaces()
148 .first()
149 .map(|idb| LinkType(u32::from(idb.linktype)))
150 .unwrap_or(LinkType::ETHERNET)
151 }
152}
153
154impl<R: Read> Iterator for PcapNgIterator<R> {
155 type Item = Result<CapturedPacket>;
156
157 fn next(&mut self) -> Option<Self::Item> {
158 loop {
159 match self.inner.next_block() {
160 Some(Ok(block)) => {
161 match block {
162 Block::EnhancedPacket(epb) => {
163 let data = Bytes::copy_from_slice(&epb.data);
164 let mut pkt = Packet::from_bytes(data);
165 let _ = pkt.parse();
166 return Some(Ok(CapturedPacket {
167 packet: pkt,
168 metadata: PcapMetadata {
169 timestamp: epb.timestamp,
170 orig_len: epb.original_len,
171 interface_id: Some(epb.interface_id),
172 comment: None,
173 },
174 }));
175 },
176 Block::SimplePacket(spb) => {
177 let data = Bytes::copy_from_slice(&spb.data);
178 let mut pkt = Packet::from_bytes(data);
179 let _ = pkt.parse();
180 return Some(Ok(CapturedPacket {
181 packet: pkt,
182 metadata: PcapMetadata {
183 timestamp: std::time::Duration::ZERO,
184 orig_len: spb.original_len,
185 interface_id: Some(0),
186 comment: None,
187 },
188 }));
189 },
190 _ => continue,
192 }
193 },
194 Some(Err(e)) => {
195 return Some(Err(PacketError::Io(format!("PcapNG read error: {e}"))));
196 },
197 None => return None,
198 }
199 }
200 }
201}
202
203pub enum CaptureIterator<R: Read> {
211 Pcap(PcapIterator<R>),
213 PcapNg(PcapNgIterator<R>),
215}
216
217impl CaptureIterator<BufReader<File>> {
218 pub fn open(path: impl AsRef<Path>) -> Result<Self> {
220 let mut file = File::open(path.as_ref()).map_err(|e| {
221 PacketError::Io(format!("failed to open {}: {}", path.as_ref().display(), e))
222 })?;
223
224 let mut magic = [0u8; 4];
225 file.read_exact(&mut magic).map_err(|e| {
226 PacketError::Io(format!(
227 "failed to read magic bytes from {}: {e}",
228 path.as_ref().display()
229 ))
230 })?;
231
232 let format = detect_format(&magic)?;
233
234 file.seek(SeekFrom::Start(0))
236 .map_err(|e| PacketError::Io(format!("failed to seek: {e}")))?;
237
238 let reader = BufReader::new(file);
239 match format {
240 CaptureFormat::Pcap => Ok(Self::Pcap(PcapIterator::from_reader(reader)?)),
241 CaptureFormat::PcapNg => Ok(Self::PcapNg(PcapNgIterator::from_reader(reader)?)),
242 }
243 }
244}
245
246impl<R: Read> CaptureIterator<R> {
247 pub fn from_reader(
252 mut reader: R,
253 ) -> Result<CaptureIterator<std::io::Chain<Cursor<[u8; 4]>, R>>> {
254 let mut magic = [0u8; 4];
255 reader
256 .read_exact(&mut magic)
257 .map_err(|e| PacketError::Io(format!("failed to read magic bytes: {e}")))?;
258
259 let format = detect_format(&magic)?;
260 let chain = Cursor::new(magic).chain(reader);
261
262 match format {
263 CaptureFormat::Pcap => Ok(CaptureIterator::Pcap(PcapIterator::from_reader(chain)?)),
264 CaptureFormat::PcapNg => {
265 Ok(CaptureIterator::PcapNg(PcapNgIterator::from_reader(chain)?))
266 },
267 }
268 }
269
270 pub fn link_type(&self) -> LinkType {
272 match self {
273 Self::Pcap(p) => p.link_type(),
274 Self::PcapNg(p) => p.link_type(),
275 }
276 }
277
278 pub fn format(&self) -> CaptureFormat {
280 match self {
281 Self::Pcap(_) => CaptureFormat::Pcap,
282 Self::PcapNg(_) => CaptureFormat::PcapNg,
283 }
284 }
285}
286
287impl<R: Read> Iterator for CaptureIterator<R> {
288 type Item = Result<CapturedPacket>;
289
290 fn next(&mut self) -> Option<Self::Item> {
291 match self {
292 Self::Pcap(iter) => iter.next(),
293 Self::PcapNg(iter) => iter.next(),
294 }
295 }
296}
297
298#[cfg(test)]
299mod tests {
300 use super::*;
301 use std::time::Duration;
302
303 use pcap_file::pcap::{PcapHeader, PcapPacket, PcapWriter as PcapFileWriter};
304
305 fn sample_ethernet_packet() -> Vec<u8> {
306 vec![
308 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, ]
313 }
314
315 fn create_test_pcap(packets: &[(Duration, &[u8])]) -> Vec<u8> {
316 let mut buf = Vec::new();
317 let header = PcapHeader::default();
318 let mut writer = PcapFileWriter::with_header(Cursor::new(&mut buf), header).unwrap();
319 for (ts, data) in packets {
320 let pkt = PcapPacket::new(*ts, data.len() as u32, data);
321 writer.write_packet(&pkt).unwrap();
322 }
323 drop(writer);
324 buf
325 }
326
327 fn create_test_pcapng(packets: &[(Duration, &[u8])]) -> Vec<u8> {
328 use pcap_file::pcapng::PcapNgWriter;
329 use pcap_file::pcapng::blocks::enhanced_packet::EnhancedPacketBlock;
330 use pcap_file::pcapng::blocks::interface_description::InterfaceDescriptionBlock;
331 use std::borrow::Cow;
332
333 let mut buf = Vec::new();
334 let mut writer = PcapNgWriter::new(Cursor::new(&mut buf)).unwrap();
335
336 let idb = InterfaceDescriptionBlock {
338 linktype: pcap_file::DataLink::ETHERNET,
339 snaplen: 0xFFFF,
340 options: vec![],
341 };
342 writer.write_pcapng_block(idb).unwrap();
343
344 for (ts, data) in packets {
345 let epb = EnhancedPacketBlock {
346 interface_id: 0,
347 timestamp: *ts,
348 original_len: data.len() as u32,
349 data: Cow::Borrowed(data),
350 options: vec![],
351 };
352 writer.write_pcapng_block(epb).unwrap();
353 }
354 drop(writer);
355 buf
356 }
357
358 #[test]
359 fn test_detect_format_pcap() {
360 let magic = [0xD4, 0xC3, 0xB2, 0xA1];
362 assert_eq!(detect_format(&magic).unwrap(), CaptureFormat::Pcap);
363 }
364
365 #[test]
366 fn test_detect_format_pcapng() {
367 let magic = [0x0A, 0x0D, 0x0D, 0x0A];
369 assert_eq!(detect_format(&magic).unwrap(), CaptureFormat::PcapNg);
370 }
371
372 #[test]
373 fn test_detect_format_unknown() {
374 let magic = [0x00, 0x00, 0x00, 0x00];
375 assert!(detect_format(&magic).is_err());
376 }
377
378 #[test]
379 fn test_pcap_iterator_from_reader() {
380 let eth = sample_ethernet_packet();
381 let pcap_data = create_test_pcap(&[
382 (Duration::from_secs(1), ð),
383 (Duration::from_secs(2), ð),
384 ]);
385 let iter = PcapIterator::from_reader(Cursor::new(pcap_data)).unwrap();
386 let packets: Vec<_> = iter.collect::<std::result::Result<Vec<_>, _>>().unwrap();
387 assert_eq!(packets.len(), 2);
388 assert_eq!(packets[0].metadata.timestamp, Duration::from_secs(1));
389 assert_eq!(packets[1].metadata.timestamp, Duration::from_secs(2));
390 assert!(packets[0].metadata.interface_id.is_none());
392 }
393
394 #[test]
395 fn test_pcap_iterator_link_type() {
396 let pcap_data = create_test_pcap(&[]);
397 let iter = PcapIterator::from_reader(Cursor::new(pcap_data)).unwrap();
398 assert_eq!(iter.link_type(), LinkType::ETHERNET);
399 }
400
401 #[test]
402 fn test_pcap_iterator_empty() {
403 let pcap_data = create_test_pcap(&[]);
404 let iter = PcapIterator::from_reader(Cursor::new(pcap_data)).unwrap();
405 let packets: Vec<_> = iter.collect::<std::result::Result<Vec<_>, _>>().unwrap();
406 assert!(packets.is_empty());
407 }
408
409 #[test]
410 fn test_pcap_iterator_metadata() {
411 let eth = sample_ethernet_packet();
412 let pcap_data = create_test_pcap(&[(Duration::from_millis(1500), ð)]);
413 let iter = PcapIterator::from_reader(Cursor::new(pcap_data)).unwrap();
414 let packets: Vec<_> = iter.collect::<std::result::Result<Vec<_>, _>>().unwrap();
415 assert_eq!(packets.len(), 1);
416 assert_eq!(packets[0].metadata.orig_len, eth.len() as u32);
417 assert_eq!(packets[0].packet.len(), eth.len());
418 }
419
420 #[test]
421 fn test_pcap_iterator_is_lazy() {
422 let eth = sample_ethernet_packet();
423 let pcap_data = create_test_pcap(&[
424 (Duration::from_secs(1), ð),
425 (Duration::from_secs(2), ð),
426 (Duration::from_secs(3), ð),
427 ]);
428 let mut iter = PcapIterator::from_reader(Cursor::new(pcap_data)).unwrap();
429 let first = iter.next().unwrap().unwrap();
430 assert_eq!(first.metadata.timestamp, Duration::from_secs(1));
431 let second = iter.next().unwrap().unwrap();
432 assert_eq!(second.metadata.timestamp, Duration::from_secs(2));
433 }
434
435 #[test]
436 fn test_pcapng_iterator_from_reader() {
437 let eth = sample_ethernet_packet();
438 let pcapng_data = create_test_pcapng(&[
439 (Duration::from_secs(10), ð),
440 (Duration::from_secs(20), ð),
441 ]);
442 let iter = PcapNgIterator::from_reader(Cursor::new(pcapng_data)).unwrap();
443 let packets: Vec<_> = iter.collect::<std::result::Result<Vec<_>, _>>().unwrap();
444 assert_eq!(packets.len(), 2);
445 assert_eq!(packets[0].metadata.timestamp, Duration::from_secs(10));
446 assert_eq!(packets[1].metadata.timestamp, Duration::from_secs(20));
447 assert_eq!(packets[0].metadata.interface_id, Some(0));
448 }
449
450 #[test]
451 fn test_pcapng_iterator_empty() {
452 let pcapng_data = create_test_pcapng(&[]);
453 let iter = PcapNgIterator::from_reader(Cursor::new(pcapng_data)).unwrap();
454 let packets: Vec<_> = iter.collect::<std::result::Result<Vec<_>, _>>().unwrap();
455 assert!(packets.is_empty());
456 }
457
458 #[test]
459 fn test_capture_iterator_auto_detect_pcap() {
460 let eth = sample_ethernet_packet();
461 let pcap_data = create_test_pcap(&[(Duration::from_secs(1), ð)]);
462 let iter = CaptureIterator::from_reader(Cursor::new(pcap_data)).unwrap();
463 assert_eq!(iter.format(), CaptureFormat::Pcap);
464 let packets: Vec<_> = iter.collect::<std::result::Result<Vec<_>, _>>().unwrap();
465 assert_eq!(packets.len(), 1);
466 }
467
468 #[test]
469 fn test_capture_iterator_auto_detect_pcapng() {
470 let eth = sample_ethernet_packet();
471 let pcapng_data = create_test_pcapng(&[(Duration::from_secs(5), ð)]);
472 let iter = CaptureIterator::from_reader(Cursor::new(pcapng_data)).unwrap();
473 assert_eq!(iter.format(), CaptureFormat::PcapNg);
474 let packets: Vec<_> = iter.collect::<std::result::Result<Vec<_>, _>>().unwrap();
475 assert_eq!(packets.len(), 1);
476 assert_eq!(packets[0].metadata.timestamp, Duration::from_secs(5));
477 }
478
479 #[test]
480 fn test_rdpcap_pcapng_roundtrip() {
481 let eth = sample_ethernet_packet();
482 let pcapng_data = create_test_pcapng(&[
483 (Duration::from_secs(1), ð),
484 (Duration::from_secs(2), ð),
485 (Duration::from_secs(3), ð),
486 ]);
487 let tmpdir = tempfile::tempdir().unwrap();
489 let path = tmpdir.path().join("test.pcapng");
490 std::fs::write(&path, &pcapng_data).unwrap();
491 let packets = rdpcap(&path).unwrap();
492 assert_eq!(packets.len(), 3);
493 assert_eq!(packets[0].metadata.timestamp, Duration::from_secs(1));
494 assert_eq!(packets[2].metadata.timestamp, Duration::from_secs(3));
495 }
496}