1
use std::path::Path;
2

            
3
#[cfg(not(windows))]
4
use std::os::unix::io::RawFd;
5

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

            
12
#[cfg(libpcap_1_5_0)]
13
use crate::capture::Precision;
14

            
15
#[cfg(not(windows))]
16
use crate::capture::activated::open_raw_fd;
17

            
18
impl Capture<Offline> {
19
    /// Opens an offline capture handle from a pcap dump file, given a path.
20
    ///
21
    /// On Windows a path outside ASCII needs `init(CharEncoding::Utf8)` first. The path always
22
    /// reaches libpcap as UTF-8, but until that call libpcap reads it in the local code page,
23
    /// which on most systems is not UTF-8: the name gets mangled and the file is not found.
24
42
    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Capture<Offline>, Error> {
25
42
        let path = path_to_cstring(path.as_ref())?;
26
42
        Capture::new_raw(Some(path), |path, err| unsafe {
27
42
            raw::pcap_open_offline(path, err)
28
42
        })
29
42
    }
30

            
31
    /// Opens an offline capture handle from a pcap dump file, given a path.
32
    /// Takes an additional precision argument specifying the timestamp precision desired.
33
    ///
34
    /// On Windows a path outside ASCII needs `init(CharEncoding::Utf8)` first. The path always
35
    /// reaches libpcap as UTF-8, but until that call libpcap reads it in the local code page,
36
    /// which on most systems is not UTF-8: the name gets mangled and the file is not found.
37
    #[cfg(libpcap_1_5_0)]
38
2
    pub fn from_file_with_precision<P: AsRef<Path>>(
39
2
        path: P,
40
2
        precision: Precision,
41
2
    ) -> Result<Capture<Offline>, Error> {
42
2
        let path = path_to_cstring(path.as_ref())?;
43
2
        Capture::new_raw(Some(path), |path, err| unsafe {
44
2
            raw::pcap_open_offline_with_tstamp_precision(path, precision as _, err)
45
2
        })
46
2
    }
47

            
48
    /// Opens an offline capture handle from a pcap dump file, given a file descriptor.
49
    ///
50
    /// # Safety
51
    ///
52
    /// Unsafe, because the returned Capture assumes it is the sole owner of the file descriptor.
53
    #[cfg(not(windows))]
54
8
    pub unsafe fn from_raw_fd(fd: RawFd) -> Result<Capture<Offline>, Error> {
55
9
        unsafe { open_raw_fd(fd, b'r') }.and_then(|file| {
56
4
            Capture::new_raw(None, |_, err| unsafe { raw::pcap_fopen_offline(file, err) })
57
4
        })
58
8
    }
59

            
60
    /// Opens an offline capture handle from a pcap dump file, given a file descriptor. Takes an
61
    /// additional precision argument specifying the timestamp precision desired.
62
    ///
63
    /// # Safety
64
    ///
65
    /// Unsafe, because the returned Capture assumes it is the sole owner of the file descriptor.
66
    #[cfg(all(not(windows), libpcap_1_5_0))]
67
8
    pub unsafe fn from_raw_fd_with_precision(
68
8
        fd: RawFd,
69
8
        precision: Precision,
70
8
    ) -> Result<Capture<Offline>, Error> {
71
9
        unsafe { open_raw_fd(fd, b'r') }.and_then(|file| {
72
4
            Capture::new_raw(None, |_, err| unsafe {
73
4
                raw::pcap_fopen_offline_with_tstamp_precision(file, precision as _, err)
74
4
            })
75
4
        })
76
8
    }
77

            
78
    /// Get the major version number of the pcap dump file format.
79
10
    pub fn major_version(&self) -> i32 {
80
10
        unsafe { raw::pcap_major_version(self.handle.as_ptr()) }
81
10
    }
82

            
83
    /// Get the minor version number of the pcap dump file format.
84
10
    pub fn minor_version(&self) -> i32 {
85
10
        unsafe { raw::pcap_minor_version(self.handle.as_ptr()) }
86
10
    }
87

            
88
    /// Get the (major, minor) version number of the pcap dump file format.
89
6
    pub fn version(&self) -> (i32, i32) {
90
6
        (self.major_version(), self.minor_version())
91
6
    }
92
}
93

            
94
#[cfg(test)]
95
mod tests {
96
    #[cfg(libpcap_1_5_0)]
97
    use mockall::predicate;
98

            
99
    use crate::{
100
        capture::testmod::test_capture,
101
        raw::testmod::{RAWMTX, as_pcap_t},
102
    };
103

            
104
    use super::*;
105

            
106
    #[test]
107
    fn test_from_file() {
108
        let _m = RAWMTX.lock();
109

            
110
        let mut dummy: isize = 777;
111
        let pcap = as_pcap_t(&mut dummy);
112

            
113
        let ctx = raw::pcap_open_offline_context();
114
        ctx.expect().return_once_st(move |_, _| pcap);
115

            
116
        let ctx = raw::pcap_close_context();
117
        ctx.expect()
118
            .withf_st(move |ptr| *ptr == pcap)
119
            .return_once(|_| {});
120

            
121
        let result = Capture::from_file("path/to/nowhere");
122
        assert!(result.is_ok());
123
    }
124

            
125
    #[test]
126
    #[cfg(libpcap_1_5_0)]
127
    fn test_from_file_with_precision() {
128
        let _m = RAWMTX.lock();
129

            
130
        let mut dummy: isize = 777;
131
        let pcap = as_pcap_t(&mut dummy);
132

            
133
        let ctx = raw::pcap_open_offline_with_tstamp_precision_context();
134
        ctx.expect()
135
            .with(predicate::always(), predicate::eq(1), predicate::always())
136
            .return_once_st(move |_, _, _| pcap);
137

            
138
        let ctx = raw::pcap_close_context();
139
        ctx.expect()
140
            .withf_st(move |ptr| *ptr == pcap)
141
            .return_once(|_| {});
142

            
143
        let result = Capture::from_file_with_precision("path/to/nowhere", Precision::Nano);
144
        assert!(result.is_ok());
145
    }
146

            
147
    #[test]
148
    fn test_version() {
149
        let _m = RAWMTX.lock();
150

            
151
        let mut dummy: isize = 777;
152
        let pcap = as_pcap_t(&mut dummy);
153

            
154
        let ctx = raw::pcap_major_version_context();
155
        ctx.expect()
156
            .withf_st(move |arg| *arg == pcap)
157
            .return_once(|_| 5);
158

            
159
        let ctx = raw::pcap_minor_version_context();
160
        ctx.expect()
161
            .withf_st(move |arg| *arg == pcap)
162
            .return_once(|_| 7);
163

            
164
        let test_capture = test_capture::<Offline>(pcap);
165
        let capture = test_capture.capture;
166

            
167
        assert_eq!(capture.version(), (5, 7));
168
    }
169
}