pcapforge_core/
capture.rs1use anyhow::{Result, Context, bail};
2use pcap::Capture;
3use pcap_file::{pcap::PcapReader, pcapng::PcapNgReader};
4use std::fs::File;
5use std::path::Path;
6use std::io::Read;
7
8pub enum CaptureSource {
9 Pcap(Capture<pcap::Offline>),
10 PcapFile(Vec<u8>),
11 PcapNg(Vec<u8>),
12}
13
14pub struct PacketCapture {
15 source: CaptureSource,
16 filter: Option<String>,
17}
18
19impl PacketCapture {
20 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
22 let path = path.as_ref();
23 let extension = path.extension()
24 .and_then(|s| s.to_str())
25 .unwrap_or("");
26
27 match extension.to_lowercase().as_str() {
28 "pcap" | "cap" => Self::from_pcap_file(path),
29 "pcapng" | "ntar" => Self::from_pcapng_file(path),
30 _ => {
31 Self::auto_detect(path)
33 }
34 }
35 }
36
37 fn from_pcap_file(path: &Path) -> Result<Self> {
38 let capture = Capture::from_file(path)
39 .context("Failed to open pcap file")?;
40
41 Ok(Self {
42 source: CaptureSource::Pcap(capture),
43 filter: None,
44 })
45 }
46
47 fn from_pcapng_file(path: &Path) -> Result<Self> {
48 let mut file = File::open(path)?;
49 let mut buffer = Vec::new();
50 file.read_to_end(&mut buffer)?;
51
52 Ok(Self {
53 source: CaptureSource::PcapNg(buffer),
54 filter: None,
55 })
56 }
57
58 fn auto_detect(path: &Path) -> Result<Self> {
59 let mut file = File::open(path)?;
60 let mut magic = [0u8; 4];
61 file.read_exact(&mut magic)?;
62
63 match &magic {
65 [0xa1, 0xb2, 0xc3, 0xd4] | [0xd4, 0xc3, 0xb2, 0xa1] |
67 [0xa1, 0xb2, 0x3c, 0x4d] | [0x4d, 0x3c, 0xb2, 0xa1] => {
68 drop(file);
69 Self::from_pcap_file(path)
70 }
71 [0x0a, 0x0d, 0x0d, 0x0a] => {
73 drop(file);
74 Self::from_pcapng_file(path)
75 }
76 _ => bail!("Unknown file format. Expected pcap or pcapng")
77 }
78 }
79
80 pub fn set_filter(&mut self, filter: &str) -> Result<()> {
82 match &mut self.source {
83 CaptureSource::Pcap(capture) => {
84 capture.filter(filter, true)
85 .context("Failed to apply BPF filter")?;
86 self.filter = Some(filter.to_string());
87 Ok(())
88 }
89 _ => {
90 self.filter = Some(filter.to_string());
92 Ok(())
93 }
94 }
95 }
96
97 pub fn stats(&mut self) -> Result<CaptureStats> {
99 let mut stats = CaptureStats::default();
100
101 match &mut self.source {
102 CaptureSource::Pcap(capture) => {
103 while let Ok(packet) = capture.next_packet() {
104 stats.update(&packet.data, packet.header.len);
105 }
106 }
107 CaptureSource::PcapFile(data) => {
108 let mut reader = PcapReader::new(&data[..])?;
109 while let Some(pkt) = reader.next_packet() {
110 let pkt = pkt?;
111 stats.update(&pkt.data, pkt.data.len() as u32);
112 }
113 }
114 CaptureSource::PcapNg(data) => {
115 let mut reader = PcapNgReader::new(&data[..])?;
116 while let Some(block) = reader.next_block() {
117 match block {
118 Ok(pcap_file::pcapng::Block::EnhancedPacket(pkt)) => {
119 stats.update(&pkt.data, pkt.data.len() as u32);
120 }
121 Ok(pcap_file::pcapng::Block::SimplePacket(pkt)) => {
122 stats.update(&pkt.data, pkt.data.len() as u32);
123 }
124 _ => {}
125 }
126 }
127 }
128 }
129
130 Ok(stats)
131 }
132
133 pub fn process_packets<F>(&mut self, mut callback: F) -> Result<()>
135 where
136 F: FnMut(ProcessedPacket) -> Result<()>,
137 {
138 match &mut self.source {
139 CaptureSource::Pcap(capture) => {
140 while let Ok(packet) = capture.next_packet() {
141 let processed = ProcessedPacket {
142 timestamp: packet.header.ts.tv_sec as u64 * 1_000_000
143 + packet.header.ts.tv_usec as u64,
144 data: packet.data.to_vec(),
145 len: packet.header.len,
146 caplen: packet.header.caplen,
147 };
148 callback(processed)?;
149 }
150 }
151 CaptureSource::PcapFile(data) => {
152 let mut reader = PcapReader::new(&data[..])?;
153 while let Some(pkt) = reader.next_packet() {
154 let pkt = pkt?;
155 let processed = ProcessedPacket {
156 timestamp: pkt.timestamp.as_micros() as u64,
157 data: pkt.data.to_vec(),
158 len: pkt.orig_len,
159 caplen: pkt.data.len() as u32,
160 };
161 callback(processed)?;
162 }
163 }
164 CaptureSource::PcapNg(data) => {
165 let mut reader = PcapNgReader::new(&data[..])?;
166 while let Some(block) = reader.next_block() {
167 match block {
168 Ok(pcap_file::pcapng::Block::EnhancedPacket(pkt)) => {
169 let processed = ProcessedPacket {
170 timestamp: pkt.timestamp.as_micros() as u64,
171 data: pkt.data.to_vec(),
172 len: pkt.original_len,
173 caplen: pkt.data.len() as u32,
174 };
175 callback(processed)?;
176 }
177 Ok(pcap_file::pcapng::Block::SimplePacket(pkt)) => {
178 let processed = ProcessedPacket {
179 timestamp: 0, data: pkt.data.to_vec(),
181 len: pkt.original_len,
182 caplen: pkt.data.len() as u32,
183 };
184 callback(processed)?;
185 }
186 _ => {}
187 }
188 }
189 }
190 }
191 Ok(())
192 }
193}
194
195pub struct ProcessedPacket {
196 pub timestamp: u64, pub data: Vec<u8>,
198 pub len: u32,
199 pub caplen: u32,
200}
201
202#[derive(Default, Debug)]
203pub struct CaptureStats {
204 pub total_packets: u64,
205 pub total_bytes: u64,
206 pub avg_packet_size: u64,
207 pub max_packet_size: u32,
208 pub min_packet_size: u32,
209}
210
211impl CaptureStats {
212 fn update(&mut self, _data: &[u8], len: u32) {
213 self.total_packets += 1;
214 self.total_bytes += len as u64;
215
216 if len > self.max_packet_size {
217 self.max_packet_size = len;
218 }
219 if len < self.min_packet_size || self.min_packet_size == 0 {
220 self.min_packet_size = len;
221 }
222
223 if self.total_packets > 0 {
224 self.avg_packet_size = self.total_bytes / self.total_packets;
225 }
226 }
227}