1use crate::bpf::Bpf;
2use crate::{errors::Error, pcap_util, stats::Stats};
3use log::*;
4use pcap_sys::{pcap_fileno, pcap_set_immediate_mode};
5use std::os::raw::c_int;
6use std::path::Path;
7
8#[derive(Clone)]
11pub struct Handle {
12 handle: *mut pcap_sys::pcap_t,
13 live_capture: bool,
14 interrupted: std::sync::Arc<std::sync::Mutex<bool>>,
15}
16
17unsafe impl Send for Handle {}
18unsafe impl Sync for Handle {}
19
20impl Handle {
21 pub fn is_live_capture(&self) -> bool {
22 self.live_capture
23 }
24
25 pub fn live_capture(iface: &str) -> Result<std::sync::Arc<Handle>, Error> {
27 let device_str = std::ffi::CString::new(iface).map_err(Error::Ffi)?;
28
29 let errbuf = ([0 as std::os::raw::c_char; 256]).as_mut_ptr();
30 let h = unsafe { pcap_sys::pcap_create(device_str.as_ptr() as _, errbuf) };
31 let r = if h.is_null() {
32 pcap_util::cstr_to_string(errbuf).and_then(|msg| {
33 error!("Failed to create live stream: {}", msg);
34 Err(Error::LiveCapture {
35 iface: iface.to_string(),
36 error: msg,
37 })
38 })
39 } else {
40 info!("Live stream created for interface {}", iface);
41 let handle = std::sync::Arc::new(Handle {
42 handle: h,
43 live_capture: true,
44 interrupted: std::sync::Arc::new(std::sync::Mutex::new(false)),
45 });
46 Ok(handle)
47 };
48 drop(errbuf);
49 r
50 }
51
52 pub fn file_capture<P: AsRef<Path>>(path: P) -> Result<std::sync::Arc<Handle>, Error> {
54 let path = if let Some(s) = path.as_ref().to_str() {
55 s
56 } else {
57 return Err(Error::Custom(format!("Invalid path: {:?}", path.as_ref())));
58 };
59 let device_str = std::ffi::CString::new(path).map_err(Error::Ffi)?;
60
61 let errbuf = ([0 as std::os::raw::c_char; 256]).as_mut_ptr();
62 let h = unsafe { pcap_sys::pcap_open_offline(device_str.as_ptr() as _, errbuf) };
63 let r = if h.is_null() {
64 pcap_util::cstr_to_string(errbuf as _).and_then(|msg| {
65 error!("Failed to create file stream: {}", msg);
66 Err(Error::FileCapture {
67 file: path.to_string(),
68 error: msg,
69 })
70 })
71 } else {
72 info!("File stream created for file {}", path);
73 let handle = std::sync::Arc::new(Handle {
74 handle: h,
75 live_capture: false,
76 interrupted: std::sync::Arc::new(std::sync::Mutex::new(false)),
77 });
78 Ok(handle)
79 };
80 drop(errbuf);
81 r
82 }
83
84 pub fn dead(linktype: i32, snaplen: i32) -> Result<std::sync::Arc<Handle>, Error> {
86 let h = unsafe { pcap_sys::pcap_open_dead(linktype as c_int, snaplen as c_int) };
87 if h.is_null() {
88 error!("Failed to create dead handle");
89 Err(Error::Custom("Could not create dead handle".to_owned()))
90 } else {
91 info!("Dead handle created");
92 let handle = std::sync::Arc::new(Handle {
93 handle: h,
94 live_capture: false,
95 interrupted: std::sync::Arc::new(std::sync::Mutex::new(false)),
96 });
97 Ok(handle)
98 }
99 }
100
101 pub fn lookup() -> Result<std::sync::Arc<Handle>, Error> {
102 let errbuf = ([0 as std::os::raw::c_char; 256]).as_mut_ptr();
103 let dev = unsafe { pcap_sys::pcap_lookupdev(errbuf) };
104 let res = if dev.is_null() {
105 pcap_util::cstr_to_string(errbuf as _).and_then(|msg| Err(Error::LibPcapError(msg)))
106 } else {
107 pcap_util::cstr_to_string(dev as _).and_then(|s| {
108 debug!("Lookup found interface {}", s);
109 Handle::live_capture(&s)
110 })
111 };
112 drop(errbuf);
113 res
114 }
115
116 pub fn set_non_block(&self) -> Result<&Self, Error> {
117 let errbuf = ([0 as std::os::raw::c_char; 256]).as_mut_ptr();
118 if -1 == unsafe { pcap_sys::pcap_setnonblock(self.handle, 1, errbuf) } {
119 pcap_util::cstr_to_string(errbuf as _).and_then(|msg| {
120 error!("Failed to set non block: {}", msg);
121 Err(Error::LibPcapError(msg))
122 })
123 } else {
124 Ok(self)
125 }
126 }
127
128 pub fn set_promiscuous(&self) -> Result<&Self, Error> {
129 if 0 != unsafe { pcap_sys::pcap_set_promisc(self.handle, 1) } {
130 Err(pcap_util::convert_libpcap_error(self.handle))
131 } else {
132 Ok(self)
133 }
134 }
135
136 pub fn set_snaplen(&self, snaplen: u32) -> Result<&Self, Error> {
137 if 0 != unsafe { pcap_sys::pcap_set_snaplen(self.handle, snaplen as _) } {
138 Err(pcap_util::convert_libpcap_error(self.handle))
139 } else {
140 Ok(self)
141 }
142 }
143
144 pub fn set_timeout(&self, dur: &std::time::Duration) -> Result<&Self, Error> {
145 if 0 != unsafe { pcap_sys::pcap_set_timeout(self.handle, dur.as_millis() as _) } {
146 Err(pcap_util::convert_libpcap_error(self.handle))
147 } else {
148 Ok(self)
149 }
150 }
151
152 pub fn set_buffer_size(&self, buffer_size: u32) -> Result<&Self, Error> {
153 if 0 != unsafe { pcap_sys::pcap_set_buffer_size(self.handle, buffer_size as _) } {
154 Err(pcap_util::convert_libpcap_error(self.handle))
155 } else {
156 Ok(self)
157 }
158 }
159
160 pub fn compile_bpf(&self, bpf: &str) -> Result<Bpf, Error> {
161 let mut bpf_program = pcap_sys::bpf_program {
162 bf_len: 0,
163 bf_insns: std::ptr::null_mut(),
164 };
165
166 let bpf_str = std::ffi::CString::new(bpf.clone()).map_err(Error::Ffi)?;
167
168 if 0 != unsafe {
169 pcap_sys::pcap_compile(
170 self.handle,
171 &mut bpf_program,
172 bpf_str.as_ptr(),
173 1,
174 pcap_sys::PCAP_NETMASK_UNKNOWN,
175 )
176 } {
177 return Err(pcap_util::convert_libpcap_error(self.handle));
178 }
179
180 Ok(Bpf::new(bpf_program))
181 }
182
183 pub fn set_bpf(&self, bpf: Bpf) -> Result<&Self, Error> {
184 let mut bpf = bpf;
185
186 let ret_code = unsafe { pcap_sys::pcap_setfilter(self.handle, bpf.inner_mut()) };
187 if ret_code != 0 {
188 return Err(pcap_util::convert_libpcap_error(self.handle));
189 }
190 Ok(self)
191 }
192
193 pub fn set_immediate_mode(&self) -> Result<&Self, Error> {
194 if 0 != unsafe { pcap_sys::pcap_set_immediate_mode(self.handle, 1) } {
195 Err(pcap_util::convert_libpcap_error(self.handle))
196 } else {
197 Ok(self)
198 }
199 }
200
201 pub fn activate(&self) -> Result<&Self, Error> {
202 if 0 != unsafe { pcap_sys::pcap_activate(self.handle) } {
203 Err(pcap_util::convert_libpcap_error(self.handle))
204 } else {
205 Ok(self)
206 }
207 }
208
209 pub fn fd(&self) -> Result<i32, Error> {
210 unsafe {
211 let fd = pcap_sys::pcap_get_selectable_fd(self.handle);
212 if fd == -1 {
213 Err(pcap_util::convert_libpcap_error(self.handle))
214 } else {
215 Ok(fd)
216 }
217 }
218 }
219
220 pub fn as_mut_ptr(&self) -> *mut pcap_sys::pcap_t {
221 self.handle
222 }
223
224 pub fn interrupted(&self) -> bool {
225 self.interrupted.lock().map(|l| *l).unwrap_or(true)
226 }
227
228 pub fn interrupt(&self) {
229 let interrupted = self
230 .interrupted
231 .lock()
232 .map(|mut l| {
233 *l = true;
234 false
235 })
236 .unwrap_or(true);
237 if !interrupted {
238 unsafe {
239 pcap_sys::pcap_breakloop(self.handle);
240 }
241 }
242 }
243
244 pub fn stats(&self) -> Result<Stats, Error> {
245 let mut stats: pcap_sys::pcap_stat = pcap_sys::pcap_stat {
246 ps_recv: 0,
247 ps_drop: 0,
248 ps_ifdrop: 0,
249 };
250 if 0 != unsafe { pcap_sys::pcap_stats(self.handle, &mut stats) } {
251 Err(pcap_util::convert_libpcap_error(self.handle))
252 } else {
253 let stats = Stats {
254 received: stats.ps_recv,
255 dropped_by_kernel: stats.ps_drop,
256 dropped_by_interface: stats.ps_ifdrop,
257 };
258 Ok(stats)
259 }
260 }
261
262 pub fn close(&self) {
263 unsafe { pcap_sys::pcap_close(self.handle) }
264 }
265}
266
267impl Drop for Handle {
268 fn drop(&mut self) {
269 self.close();
270 }
271}
272
273#[cfg(test)]
274mod tests {
275 extern crate env_logger;
276
277 use super::*;
278 use std::path::PathBuf;
279
280 #[test]
281 fn open_file() {
282 let _ = env_logger::try_init();
283
284 let pcap_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
285 .join("resources")
286 .join("canary.pcap");
287
288 let handle = Handle::file_capture(pcap_path.to_str().expect("No path found"));
289
290 assert!(handle.is_ok());
291 }
292 #[test]
293 fn lookup() {
294 let _ = env_logger::try_init();
295
296 let handle = Handle::lookup();
297
298 assert!(handle.is_ok());
299 }
300 #[test]
301 fn open_dead() {
302 let _ = env_logger::try_init();
303
304 let handle = Handle::dead(0, 0);
305
306 assert!(handle.is_ok());
307 }
308 #[test]
309 fn bpf_compile() {
310 let _ = env_logger::try_init();
311
312 let handle = Handle::dead(0, 1555).expect("Could not create dead handle");
313
314 let bpf = handle.compile_bpf(
315 "(not (net 192.168.0.0/16 and port 443)) and (not (host 192.1.2.3 and port 443))",
316 );
317
318 assert!(bpf.is_ok(), "{:?}", bpf);
319 }
320}