pcap_async/
handle.rs

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/// Wrapper around a pcap_t handle to indicate live or offline capture, and allow the handle to
9/// be interrupted to stop capture.
10#[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    /// Create a live capture from a string representing an interface
26    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    /// Create an offline capture from a path to a file
53    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    /// Create a dead handle, typically used for compiling bpf's
85    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}