1
use std::os::unix::io::{AsRawFd, RawFd};
2

            
3
use crate::{
4
    Error,
5
    capture::{Activated, Capture, State},
6
    raw,
7
};
8

            
9
/// Newtype [`Capture`] wrapper that exposes `pcap_get_selectable_fd()`.
10
pub struct SelectableCapture<T: State + ?Sized> {
11
    inner: Capture<T>,
12
    fd: RawFd,
13
}
14

            
15
impl<T: Activated + ?Sized> SelectableCapture<T> {
16
20
    pub fn new(capture: Capture<T>) -> Result<Self, Error> {
17
20
        let fd = unsafe { raw::pcap_get_selectable_fd(capture.as_ptr()) };
18
20
        if fd == -1 {
19
2
            return Err(Error::InvalidRawFd);
20
18
        }
21
18
        Ok(Self { inner: capture, fd })
22
20
    }
23

            
24
20
    pub fn get_inner_mut(&mut self) -> &mut Capture<T> {
25
20
        &mut self.inner
26
20
    }
27
}
28

            
29
impl<T: Activated + ?Sized> AsRawFd for SelectableCapture<T> {
30
34
    fn as_raw_fd(&self) -> RawFd {
31
34
        self.fd
32
34
    }
33
}
34

            
35
// GRCOV_EXCL_START
36
#[cfg(test)]
37
pub mod testmod {
38
    use super::*;
39

            
40
    // A real file descriptor to stand in for the one libpcap would hand out. AsyncFd registers it
41
    // for real, so the sink and the stream take the same path they would with a live capture.
42
    pub struct FdPair(pub [RawFd; 2]);
43

            
44
    impl FdPair {
45
        pub fn new() -> Self {
46
            let mut fds: [RawFd; 2] = [-1, -1];
47
            let rc =
48
                unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
49
            assert_eq!(rc, 0, "Unable to create a socketpair");
50
            Self(fds)
51
        }
52

            
53
        // The stream waits for the capture to be readable before it asks libpcap for a packet, so
54
        // there has to be something to read.
55
        pub fn make_readable(&self) {
56
            let byte = 0u8;
57
            let rc = unsafe { libc::write(self.0[1], &byte as *const u8 as _, 1) };
58
            assert_eq!(rc, 1, "Unable to write to the socketpair");
59
        }
60
    }
61

            
62
    impl Drop for FdPair {
63
        fn drop(&mut self) {
64
            for fd in self.0 {
65
                unsafe { libc::close(fd) };
66
            }
67
        }
68
    }
69
}
70
// GRCOV_EXCL_STOP
71

            
72
#[cfg(test)]
73
mod tests {
74
    use crate::{
75
        capture::{Active, testmod::test_capture},
76
        raw::testmod::{RAWMTX, as_pcap_t},
77
    };
78

            
79
    use super::*;
80

            
81
    #[test]
82
    fn test_selectable_capture() {
83
        let _m = RAWMTX.lock();
84

            
85
        let mut dummy: isize = 777;
86
        let pcap = as_pcap_t(&mut dummy);
87

            
88
        let test_capture = test_capture::<Active>(pcap);
89
        let capture = test_capture.capture;
90

            
91
        let ctx = raw::pcap_get_selectable_fd_context();
92
        ctx.expect()
93
            .withf_st(move |arg1| *arg1 == pcap)
94
            .return_once(|_| 5);
95

            
96
        let mut selectable = SelectableCapture::new(capture).unwrap();
97
        assert!(!selectable.get_inner_mut().is_nonblock());
98
        assert_eq!(selectable.as_raw_fd(), 5);
99
    }
100

            
101
    #[test]
102
    fn test_selectable_capture_error() {
103
        let _m = RAWMTX.lock();
104

            
105
        let mut dummy: isize = 777;
106
        let pcap = as_pcap_t(&mut dummy);
107

            
108
        let test_capture = test_capture::<Active>(pcap);
109
        let capture = test_capture.capture;
110

            
111
        let ctx = raw::pcap_get_selectable_fd_context();
112
        ctx.expect()
113
            .withf_st(move |arg1| *arg1 == pcap)
114
            .return_once(|_| -1);
115

            
116
        let result = SelectableCapture::new(capture);
117
        assert!(result.is_err());
118
    }
119
}