1
use std::borrow::Borrow;
2

            
3
#[cfg(not(windows))]
4
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
5

            
6
use crate::{
7
    Error,
8
    capture::{Active, Capture, Warning},
9
    raw,
10
};
11

            
12
impl Capture<Active> {
13
    /// The warning libpcap raised when the capture was activated, if there was one.
14
    ///
15
    /// A warning means the capture works but a requested option could not be applied, such
16
    /// as promiscuous mode on a device without it.
17
4
    pub fn warning(&self) -> Option<&Warning> {
18
4
        self.warning.as_ref()
19
4
    }
20

            
21
    /// Sends a packet over this capture handle's interface.
22
4
    pub fn sendpacket<B: Borrow<[u8]>>(&mut self, buf: B) -> Result<(), Error> {
23
4
        let buf = buf.borrow();
24
4
        self.check_err(unsafe {
25
4
            raw::pcap_sendpacket(self.handle.as_ptr(), buf.as_ptr() as _, buf.len() as _) == 0
26
        })
27
4
    }
28

            
29
    /// Sends a packet like [`Self::sendpacket()`], but returns `Error::IoError` with a kind of
30
    /// `WouldBlock` if the device was too busy to take it. Only useful on a non-blocking capture.
31
    ///
32
    /// pcap_sendpacket reports every failure the same way, so errno is all we have to tell a busy
33
    /// device apart from a real error. errno is cleared first, as libpcap rejects some sends
34
    /// without making a system call and would otherwise leave an old EAGAIN behind. It is then
35
    /// read before get_err, which allocates and could overwrite it.
36
    #[cfg(all(target_os = "linux", feature = "capture-stream"))]
37
14
    pub(crate) fn sendpacket_nonblock(&mut self, buf: &[u8]) -> Result<(), Error> {
38
14
        errno::set_errno(errno::Errno(0));
39
14
        let sent = unsafe {
40
14
            raw::pcap_sendpacket(self.handle.as_ptr(), buf.as_ptr() as _, buf.len() as _) == 0
41
        };
42
14
        if sent {
43
6
            return Ok(());
44
8
        }
45
8
        let errno = errno::errno().0;
46
8
        if errno == libc::EAGAIN || errno == libc::EWOULDBLOCK {
47
4
            return Err(Error::IoError(std::io::ErrorKind::WouldBlock));
48
4
        }
49
4
        Err(self.get_err())
50
14
    }
51

            
52
    /// Set the capture to be non-blocking. When this is set, [`Self::next_packet()`] may return an
53
    /// error indicating that there is no packet available to be read.
54
6
    pub fn setnonblock(mut self) -> Result<Capture<Active>, Error> {
55
6
        Error::with_errbuf(|err| unsafe {
56
6
            if raw::pcap_setnonblock(self.handle.as_ptr(), 1, err) != 0 {
57
2
                return Err(Error::new(err));
58
4
            }
59
4
            self.nonblock = true;
60
4
            Ok(self)
61
6
        })
62
6
    }
63
}
64

            
65
#[cfg(not(windows))]
66
impl AsRawFd for Capture<Active> {
67
    /// Returns the file descriptor for a live capture.
68
2
    fn as_raw_fd(&self) -> RawFd {
69
2
        let fd = unsafe { raw::pcap_fileno(self.handle.as_ptr()) };
70
2
        assert!(fd != -1, "Unable to get file descriptor for live capture");
71
2
        fd
72
2
    }
73
}
74

            
75
#[cfg(not(windows))]
76
impl AsFd for Capture<Active> {
77
    /// Returns the file descriptor for a live capture.
78
2
    fn as_fd(&self) -> BorrowedFd<'_> {
79
        // SAFETY: pcap_fileno always succeeds on a live capture,
80
        // and we know this capture is live due to its State.
81
2
        let fd = unsafe { raw::pcap_fileno(self.handle.as_ptr()) };
82
2
        assert!(fd != -1, "Unable to get file descriptor for live capture");
83
        // SAFETY: The lifetime is bound to self, which is correct.
84
        // We have checked that fd != -1.
85
2
        unsafe { BorrowedFd::borrow_raw(fd) }
86
2
    }
87
}
88

            
89
#[cfg(test)]
90
mod tests {
91
    use crate::{
92
        capture::testmod::test_capture,
93
        raw::{
94
            mock_ffi::*,
95
            testmod::{RAWMTX, as_pcap_t, geterr_expect},
96
        },
97
    };
98

            
99
    use super::*;
100

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

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

            
108
        let buffer: [u8; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
109

            
110
        let test_capture = test_capture::<Active>(pcap);
111
        let mut capture = test_capture.capture;
112

            
113
        let ctx = pcap_sendpacket_context();
114
        ctx.expect()
115
            .withf_st(move |arg1, _, _| *arg1 == pcap)
116
            .return_once(|_, _, _| 0);
117

            
118
        let result = capture.sendpacket(buffer);
119
        assert!(result.is_ok());
120

            
121
        let ctx = pcap_sendpacket_context();
122
        ctx.checkpoint();
123
        ctx.expect()
124
            .withf_st(move |arg1, _, _| *arg1 == pcap)
125
            .return_once(|_, _, _| -1);
126

            
127
        let _err = geterr_expect(pcap);
128

            
129
        let result = capture.sendpacket(buffer);
130
        assert!(result.is_err());
131
    }
132

            
133
    #[test]
134
    #[cfg(all(target_os = "linux", feature = "capture-stream"))]
135
    fn test_sendpacket_nonblock() {
136
        let _m = RAWMTX.lock();
137

            
138
        let mut dummy: isize = 777;
139
        let pcap = as_pcap_t(&mut dummy);
140

            
141
        let buffer: [u8; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
142

            
143
        let test_capture = test_capture::<Active>(pcap);
144
        let mut capture = test_capture.capture;
145

            
146
        let ctx = pcap_sendpacket_context();
147
        ctx.expect()
148
            .withf_st(move |arg1, _, _| *arg1 == pcap)
149
            .return_once(|_, _, _| 0);
150

            
151
        assert_eq!(capture.sendpacket_nonblock(&buffer), Ok(()));
152

            
153
        // A busy device is not a failure.
154
        let ctx = pcap_sendpacket_context();
155
        ctx.checkpoint();
156
        ctx.expect()
157
            .withf_st(move |arg1, _, _| *arg1 == pcap)
158
            .return_once(|_, _, _| {
159
                errno::set_errno(errno::Errno(libc::EAGAIN));
160
                -1
161
            });
162

            
163
        assert_eq!(
164
            capture.sendpacket_nonblock(&buffer),
165
            Err(Error::IoError(std::io::ErrorKind::WouldBlock))
166
        );
167

            
168
        // libpcap can also reject a send without making a system call, leaving errno alone. This
169
        // case deliberately runs after the one above so that the EAGAIN it set is still there: it
170
        // must not be mistaken for a busy device, or the sink would wait for a readiness event
171
        // that is never coming.
172
        let ctx = pcap_sendpacket_context();
173
        ctx.checkpoint();
174
        ctx.expect()
175
            .withf_st(move |arg1, _, _| *arg1 == pcap)
176
            .return_once(|_, _, _| -1);
177

            
178
        let _err = geterr_expect(pcap);
179
        assert!(matches!(
180
            capture.sendpacket_nonblock(&buffer),
181
            Err(Error::PcapError(_))
182
        ));
183
    }
184

            
185
    #[test]
186
    fn test_setnonblock() {
187
        let _m = RAWMTX.lock();
188

            
189
        let mut dummy: isize = 777;
190
        let pcap = as_pcap_t(&mut dummy);
191

            
192
        let test_capture = test_capture::<Active>(pcap);
193
        let capture = test_capture.capture;
194
        assert!(!capture.is_nonblock());
195

            
196
        let ctx = pcap_setnonblock_context();
197
        ctx.expect()
198
            .withf_st(move |arg1, arg2, _| (*arg1 == pcap) && (*arg2 == 1))
199
            .return_once(|_, _, _| 0);
200

            
201
        let capture = capture.setnonblock().unwrap();
202
        assert!(capture.is_nonblock());
203
    }
204

            
205
    #[test]
206
    fn test_setnonblock_error() {
207
        let _m = RAWMTX.lock();
208

            
209
        let mut dummy: isize = 777;
210
        let pcap = as_pcap_t(&mut dummy);
211

            
212
        let test_capture = test_capture::<Active>(pcap);
213
        let capture = test_capture.capture;
214
        assert!(!capture.nonblock);
215

            
216
        let ctx = pcap_setnonblock_context();
217
        ctx.expect()
218
            .withf_st(move |arg1, arg2, _| (*arg1 == pcap) && (*arg2 == 1))
219
            .return_once(|_, _, _| -1);
220

            
221
        let result = capture.setnonblock();
222
        assert!(result.is_err());
223
    }
224

            
225
    #[test]
226
    #[cfg(not(windows))]
227
    fn test_as_raw_fd() {
228
        let _m = RAWMTX.lock();
229

            
230
        let mut dummy: isize = 777;
231
        let pcap = as_pcap_t(&mut dummy);
232

            
233
        let test_capture = test_capture::<Active>(pcap);
234
        let capture = test_capture.capture;
235

            
236
        let ctx = pcap_fileno_context();
237
        ctx.expect()
238
            .withf_st(move |arg1| *arg1 == pcap)
239
            .return_once(|_| 7);
240

            
241
        assert_eq!(capture.as_raw_fd(), 7);
242
    }
243

            
244
    #[test]
245
    #[cfg(not(windows))]
246
    fn test_as_fd() {
247
        let _m = RAWMTX.lock();
248

            
249
        let mut dummy: isize = 777;
250
        let pcap = as_pcap_t(&mut dummy);
251

            
252
        let test_capture = test_capture::<Active>(pcap);
253
        let capture = test_capture.capture;
254

            
255
        let ctx = pcap_fileno_context();
256
        ctx.expect()
257
            .withf_st(move |arg1| *arg1 == pcap)
258
            .return_once(|_| 7);
259

            
260
        assert_eq!(capture.as_fd().as_raw_fd(), 7);
261
    }
262
}