1
// GRCOV_EXCL_START
2
#![allow(dead_code)]
3
#![allow(non_camel_case_types)]
4

            
5
use libc::FILE;
6
use libc::{c_char, c_int, c_long, c_uchar, c_uint, c_ushort, sockaddr, timeval};
7

            
8
#[cfg(test)]
9
use mockall::automock;
10
#[cfg(windows)]
11
use windows_sys::Win32::Foundation::HANDLE;
12

            
13
use crate::Error;
14

            
15
// The values have never changed; libpcap has only ever appended to them, so a version that
16
// predates one of these never returns it.
17
pub const PCAP_WARNING: c_int = 1;
18
pub const PCAP_WARNING_PROMISC_NOTSUP: c_int = 2;
19
pub const PCAP_WARNING_TSTAMP_TYPE_NOTSUP: c_int = 3;
20

            
21
pub const PCAP_ERROR: c_int = -1;
22
pub const PCAP_ERROR_BREAK: c_int = -2;
23
pub const PCAP_ERROR_NOT_ACTIVATED: c_int = -3;
24
pub const PCAP_ERROR_ACTIVATED: c_int = -4;
25
pub const PCAP_ERROR_NO_SUCH_DEVICE: c_int = -5;
26
pub const PCAP_ERROR_RFMON_NOTSUP: c_int = -6;
27
pub const PCAP_ERROR_NOT_RFMON: c_int = -7;
28
pub const PCAP_ERROR_PERM_DENIED: c_int = -8;
29
pub const PCAP_ERROR_IFACE_NOT_UP: c_int = -9;
30
pub const PCAP_ERROR_CANTSET_TSTAMP_TYPE: c_int = -10;
31
pub const PCAP_ERROR_PROMISC_PERM_DENIED: c_int = -11;
32
pub const PCAP_ERROR_TSTAMP_PRECISION_NOTSUP: c_int = -12;
33
pub const PCAP_ERROR_CAPTURE_NOTSUP: c_int = -13;
34

            
35
pub const PCAP_IF_LOOPBACK: u32 = 0x00000001;
36
pub const PCAP_IF_UP: u32 = 0x00000002;
37
pub const PCAP_IF_RUNNING: u32 = 0x00000004;
38
pub const PCAP_IF_WIRELESS: u32 = 0x00000008;
39
pub const PCAP_IF_CONNECTION_STATUS: u32 = 0x00000030;
40
pub const PCAP_IF_CONNECTION_STATUS_UNKNOWN: u32 = 0x00000000;
41
pub const PCAP_IF_CONNECTION_STATUS_CONNECTED: u32 = 0x00000010;
42
pub const PCAP_IF_CONNECTION_STATUS_DISCONNECTED: u32 = 0x00000020;
43
pub const PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE: u32 = 0x00000030;
44

            
45
pub const PCAP_CHAR_ENC_LOCAL: u32 = 0x00000000;
46
pub const PCAP_CHAR_ENC_UTF_8: u32 = 0x00000001;
47

            
48
#[repr(C)]
49
#[derive(Copy, Clone)]
50
pub struct bpf_program {
51
    pub bf_len: c_uint,
52
    pub bf_insns: *mut bpf_insn,
53
}
54

            
55
#[repr(C)]
56
#[derive(Copy, Clone)]
57
pub struct bpf_insn {
58
    pub code: c_ushort,
59
    pub jt: c_uchar,
60
    pub jf: c_uchar,
61
    pub k: c_uint,
62
}
63

            
64
pub enum pcap_t {}
65

            
66
pub enum pcap_dumper_t {}
67

            
68
#[repr(C)]
69
#[derive(Copy, Clone)]
70
pub struct pcap_file_header {
71
    pub magic: c_uint,
72
    pub version_major: c_ushort,
73
    pub version_minor: c_ushort,
74
    pub thiszone: c_int,
75
    pub sigfigs: c_uint,
76
    pub snaplen: c_uint,
77
    pub linktype: c_uint,
78
}
79

            
80
pub type pcap_direction_t = c_uint;
81

            
82
pub const PCAP_D_INOUT: pcap_direction_t = 0;
83
pub const PCAP_D_IN: pcap_direction_t = 1;
84
pub const PCAP_D_OUT: pcap_direction_t = 2;
85

            
86
#[repr(C)]
87
#[derive(Copy, Clone)]
88
pub struct pcap_pkthdr {
89
    pub ts: timeval,
90
    pub caplen: c_uint,
91
    pub len: c_uint,
92
}
93

            
94
#[repr(C)]
95
#[derive(Copy, Clone)]
96
pub struct pcap_stat {
97
    pub ps_recv: c_uint,
98
    pub ps_drop: c_uint,
99
    pub ps_ifdrop: c_uint,
100
}
101

            
102
#[repr(C)]
103
#[derive(Copy, Clone)]
104
pub struct pcap_if_t {
105
    pub next: *mut pcap_if_t,
106
    pub name: *mut c_char,
107
    pub description: *mut c_char,
108
    pub addresses: *mut pcap_addr_t,
109
    pub flags: c_uint,
110
}
111

            
112
#[repr(C)]
113
#[derive(Copy, Clone)]
114
pub struct pcap_addr_t {
115
    pub next: *mut pcap_addr_t,
116
    pub addr: *mut sockaddr,
117
    pub netmask: *mut sockaddr,
118
    pub broadaddr: *mut sockaddr,
119
    pub dstaddr: *mut sockaddr,
120
}
121

            
122
#[cfg(windows)]
123
#[repr(C)]
124
#[derive(Copy, Clone)]
125
pub struct pcap_send_queue {
126
    pub maxlen: c_uint,
127
    pub len: c_uint,
128
    pub buffer: *mut c_char,
129
}
130

            
131
#[cfg(windows)]
132
pub const WINPCAP_MINTOCOPY_DEFAULT: c_int = 16000;
133

            
134
// This is not Option<fn>, pcap functions do not check if the handler is null so it is wrong to
135
// pass them Option::<fn>::None.
136
pub type pcap_handler =
137
    extern "C" fn(arg1: *mut c_uchar, arg2: *const pcap_pkthdr, arg3: *const c_uchar) -> ();
138

            
139
// Allows a binary that uses this crate to start on a system where Npcap is absent.
140
#[cfg(windows)]
141
mod loader {
142
    use std::ffi::c_void;
143
    use std::iter;
144
    use std::ptr;
145
    use std::sync::OnceLock;
146
    use std::sync::atomic::{AtomicPtr, Ordering};
147

            
148
    use windows_sys::Win32::Foundation::{HMODULE, MAX_PATH};
149
    use windows_sys::Win32::System::LibraryLoader::{
150
        GetProcAddress, LOAD_LIBRARY_FLAGS, LOAD_WITH_ALTERED_SEARCH_PATH, LoadLibraryExW,
151
    };
152
    use windows_sys::Win32::System::SystemInformation::GetSystemDirectoryW;
153

            
154
    struct Module(HMODULE);
155

            
156
    // SAFETY: the handle belongs to the process rather than the thread that opened the
157
    // library, and is never freed.
158
    unsafe impl Send for Module {}
159
    unsafe impl Sync for Module {}
160

            
161
    static LIBRARY: OnceLock<Option<Module>> = OnceLock::new();
162

            
163
    pub struct Entry {
164
        name: &'static str,
165
        address: AtomicPtr<c_void>,
166
    }
167

            
168
    impl Entry {
169
        const MISSING: *mut c_void = usize::MAX as *mut c_void;
170

            
171
        pub const fn new(name: &'static str) -> Entry {
172
            Entry {
173
                name,
174
                address: AtomicPtr::new(ptr::null_mut()),
175
            }
176
        }
177

            
178
        fn resolve(&self) -> *mut c_void {
179
            let cached = self.address.load(Ordering::Relaxed);
180
            if !cached.is_null() {
181
                return cached;
182
            }
183

            
184
            let address = library()
185
                .and_then(|module| unsafe { GetProcAddress(module.0, self.name.as_ptr()) })
186
                .map_or(Self::MISSING, |address| address as *const () as *mut c_void);
187

            
188
            // Both threads in a race resolve the same name to the same address, so the store
189
            // requires no ordering.
190
            self.address.store(address, Ordering::Relaxed);
191
            address
192
        }
193

            
194
        pub fn is_available(&self) -> bool {
195
            self.resolve() != Self::MISSING
196
        }
197

            
198
        pub fn address(&self) -> *mut c_void {
199
            let address = self.resolve();
200
            assert!(
201
                address != Self::MISSING,
202
                "wpcap.dll does not export {}",
203
                self.name.trim_end_matches('\0')
204
            );
205

            
206
            address
207
        }
208
    }
209

            
210
    pub fn is_available() -> bool {
211
        library().is_some()
212
    }
213

            
214
    fn library() -> Option<&'static Module> {
215
        LIBRARY.get_or_init(open).as_ref()
216
    }
217

            
218
    // Npcap installs wpcap.dll into the Npcap subdirectory of the system directory, and places a
219
    // copy in the system directory itself only when installed in WinPcap compatible mode. Try the
220
    // ordinary search order first, so that an application shipping its own copy continues to use
221
    // it, then fall back to the subdirectory, which is not searched by default.
222
    fn open() -> Option<Module> {
223
        load("wpcap.dll", 0).or_else(|| {
224
            let path = format!("{}\\Npcap\\wpcap.dll", system_directory()?);
225
            // wpcap.dll imports Packet.dll, so the directory containing the library must be
226
            // searched as well.
227
            load(&path, LOAD_WITH_ALTERED_SEARCH_PATH)
228
        })
229
    }
230

            
231
    fn load(path: &str, flags: LOAD_LIBRARY_FLAGS) -> Option<Module> {
232
        let path: Vec<u16> = path.encode_utf16().chain(iter::once(0)).collect();
233
        let module = unsafe { LoadLibraryExW(path.as_ptr(), ptr::null_mut(), flags) };
234

            
235
        (!module.is_null()).then_some(Module(module))
236
    }
237

            
238
    fn system_directory() -> Option<String> {
239
        let mut buffer = [0u16; MAX_PATH as usize];
240
        // The return value is the number of characters written, the number the buffer should
241
        // have held if it was too small, or zero if the call failed.
242
        let len = unsafe { GetSystemDirectoryW(buffer.as_mut_ptr(), buffer.len() as u32) } as usize;
243
        if len == 0 || len > buffer.len() {
244
            return None;
245
        }
246

            
247
        String::from_utf16(&buffer[..len]).ok()
248
    }
249

            
250
    #[cfg(test)]
251
    mod tests {
252
        use super::*;
253
        use std::panic::{AssertUnwindSafe, catch_unwind};
254

            
255
        // These read a real wpcap.dll. Return early where Npcap is not installed.
256
        fn library_present() -> bool {
257
            open().is_some()
258
        }
259

            
260
        #[test]
261
        fn test_entry() {
262
            if !library_present() {
263
                return;
264
            }
265

            
266
            let present = Entry::new("pcap_lib_version\0");
267
            assert!(present.is_available());
268
            assert!(!present.address().is_null());
269
            // A second call, which returns the cached address.
270
            assert!(present.is_available());
271

            
272
            let missing = Entry::new("pcap_no_such_entrypoint\0");
273
            assert!(!missing.is_available());
274
            assert!(!missing.is_available());
275
        }
276

            
277
        #[test]
278
        fn test_entry_address_missing() {
279
            if !library_present() {
280
                return;
281
            }
282

            
283
            let panic = catch_unwind(AssertUnwindSafe(|| {
284
                Entry::new("pcap_no_such_entrypoint\0").address()
285
            }))
286
            .unwrap_err();
287
            let message = panic
288
                .downcast_ref::<String>()
289
                .map(String::as_str)
290
                .or_else(|| panic.downcast_ref::<&str>().copied())
291
                .unwrap_or_default();
292
            assert!(
293
                message.contains("wpcap.dll does not export pcap_no_such_entrypoint"),
294
                "{message}"
295
            );
296
        }
297

            
298
        #[test]
299
        fn test_load() {
300
            assert!(load("no-such-library.dll", 0).is_none());
301

            
302
            if !library_present() {
303
                return;
304
            }
305

            
306
            // Where a default installation puts the library, reached only when the
307
            // ordinary search order comes up empty.
308
            if load("wpcap.dll", 0).is_none() {
309
                let path = format!("{}\\Npcap\\wpcap.dll", system_directory().unwrap());
310
                assert!(load(&path, LOAD_WITH_ALTERED_SEARCH_PATH).is_some());
311
            }
312

            
313
            // Succeeds from either location.
314
            assert!(open().is_some());
315
        }
316
    }
317
}
318

            
319
pub struct Library(());
320

            
321
// Under cfg(test) the entrypoints are mocked, so there is no library to find.
322
#[cfg(any(not(windows), test))]
323
pub fn require_library() -> Result<Library, Error> {
324
    Ok(Library(()))
325
}
326

            
327
#[cfg(all(windows, not(test)))]
328
pub fn require_library() -> Result<Library, Error> {
329
    if loader::is_available() {
330
        Ok(Library(()))
331
    } else {
332
        Err(Error::LibraryNotFound)
333
    }
334
}
335

            
336
#[cfg(not(windows))]
337
macro_rules! pcap_entry {
338
    ($name:ident($($arg:ident: $argty:ty),*) $(-> $ret:ty)?) => {{
339
        unsafe extern "C" {
340
            fn $name($($arg: $argty),*) $(-> $ret)?;
341
        }
342

            
343
        unsafe { $name($($arg),*) }
344
    }};
345
}
346

            
347
#[cfg(windows)]
348
macro_rules! pcap_entry {
349
    ($name:ident($($arg:ident: $argty:ty),*) $(-> $ret:ty)?) => {{
350
        static ENTRY: super::loader::Entry =
351
            super::loader::Entry::new(concat!(stringify!($name), "\0"));
352

            
353
        let entry: unsafe extern "C" fn($($argty),*) $(-> $ret)? =
354
            unsafe { std::mem::transmute(ENTRY.address()) };
355

            
356
        unsafe { entry($($arg),*) }
357
    }};
358
}
359

            
360
#[cfg(not(windows))]
361
macro_rules! pcap_ffi {
362
    (
363
        $(#[$modattr:meta])*
364
        pub mod $module:ident {
365
            $(#[$linkattr:meta])*
366
            unsafe extern "C" {
367
                $(
368
                    $(#[$fnattr:meta])*
369
                    pub fn $name:ident($($arg:ident: $argty:ty),* $(,)?) $(-> $ret:ty)?;
370
                )*
371
            }
372
        }
373
    ) => {
374
        $(#[$modattr])*
375
        pub mod $module {
376
            use super::*;
377

            
378
            $(#[$linkattr])*
379
            unsafe extern "C" {
380
                $(
381
                    $(#[$fnattr])*
382
                    pub fn $name($($arg: $argty),*) $(-> $ret)?;
383
                )*
384
            }
385
        }
386
    };
387
}
388

            
389
#[cfg(windows)]
390
macro_rules! pcap_ffi {
391
    (
392
        $(#[$modattr:meta])*
393
        pub mod $module:ident {
394
            $(#[$linkattr:meta])*
395
            unsafe extern "C" {
396
                $(
397
                    $(#[$fnattr:meta])*
398
                    pub fn $name:ident($($arg:ident: $argty:ty),* $(,)?) $(-> $ret:ty)?;
399
                )*
400
            }
401
        }
402
    ) => {
403
        $(#[$modattr])*
404
        pub mod $module {
405
            use super::*;
406

            
407
            $(
408
                $(#[$fnattr])*
409
                pub unsafe fn $name($($arg: $argty),*) $(-> $ret)? {
410
                    pcap_entry!($name($($arg: $argty),*) $(-> $ret)?)
411
                }
412
            )*
413

            
414
            // The names this module passes to GetProcAddress. Nothing verifies them at link
415
            // time.
416
            #[cfg(test)]
417
            #[allow(clippy::vec_init_then_push)]
418
            pub fn entrypoint_names() -> Vec<&'static str> {
419
                let mut names = Vec::new();
420
                $(
421
                    $(#[$fnattr])*
422
                    names.push(concat!(stringify!($name), "\0"));
423
                )*
424
                names
425
            }
426
        }
427
    };
428
}
429

            
430
macro_rules! pcap_ffi_library {
431
    (
432
        $(#[$modattr:meta])*
433
        pub mod $module:ident {
434
            unsafe extern "C" {
435
                $(
436
                    $(#[$fnattr:meta])*
437
                    pub fn $name:ident($($arg:ident: $argty:ty),* $(,)?) $(-> $ret:ty)?;
438
                )*
439
            }
440
        }
441
    ) => {
442
        $(#[$modattr])*
443
        pub mod $module {
444
            use super::*;
445

            
446
            $(
447
                $(#[$fnattr])*
448
                pub unsafe fn $name(_library: &Library, $($arg: $argty),*) $(-> $ret)? {
449
                    pcap_entry!($name($($arg: $argty),*) $(-> $ret)?)
450
                }
451
            )*
452

            
453
            #[cfg(all(windows, test))]
454
            #[allow(clippy::vec_init_then_push)]
455
            pub fn entrypoint_names() -> Vec<&'static str> {
456
                let mut names = Vec::new();
457
                $(
458
                    $(#[$fnattr])*
459
                    names.push(concat!(stringify!($name), "\0"));
460
                )*
461
                names
462
            }
463
        }
464
    };
465
}
466

            
467
pcap_ffi! {
468
    #[cfg_attr(test, automock)]
469
    pub mod ffi {
470
        unsafe extern "C" {
471
            // [OBSOLETE] pub fn pcap_lookupdev(arg1: *mut c_char) -> *mut c_char;
472
            // pub fn pcap_lookupnet(arg1: *const c_char, arg2: *mut c_uint, arg3: *mut c_uint,
473
            //                       arg4: *mut c_char) -> c_int;
474
            pub fn pcap_set_snaplen(arg1: *mut pcap_t, arg2: c_int) -> c_int;
475
            pub fn pcap_set_promisc(arg1: *mut pcap_t, arg2: c_int) -> c_int;
476
            // pub fn pcap_can_set_rfmon(arg1: *mut pcap_t) -> c_int;
477
            pub fn pcap_set_timeout(arg1: *mut pcap_t, arg2: c_int) -> c_int;
478
            pub fn pcap_set_buffer_size(arg1: *mut pcap_t, arg2: c_int) -> c_int;
479
            pub fn pcap_activate(arg1: *mut pcap_t) -> c_int;
480
            // pub fn pcap_open_live(arg1: *const c_char, arg2: c_int, arg3: c_int, arg4: c_int,
481
            //                       arg5: *mut c_char) -> *mut pcap_t;
482
            pub fn pcap_close(arg1: *mut pcap_t);
483
            pub fn pcap_loop(
484
                arg1: *mut pcap_t,
485
                arg2: c_int,
486
                arg3: pcap_handler,
487
                arg4: *mut c_uchar,
488
            ) -> c_int;
489
            pub fn pcap_dispatch(
490
                arg1: *mut pcap_t,
491
                arg2: c_int,
492
                arg3: pcap_handler,
493
                arg4: *mut c_uchar,
494
            ) -> c_int;
495
            // pub fn pcap_next(arg1: *mut pcap_t, arg2: *mut pcap_pkthdr) -> *const c_uchar;
496
            pub fn pcap_next_ex(
497
                arg1: *mut pcap_t,
498
                arg2: *mut *mut pcap_pkthdr,
499
                arg3: *mut *const c_uchar,
500
            ) -> c_int;
501
            pub fn pcap_breakloop(arg1: *mut pcap_t);
502
            pub fn pcap_stats(arg1: *mut pcap_t, arg2: *mut pcap_stat) -> c_int;
503
            pub fn pcap_setfilter(arg1: *mut pcap_t, arg2: *mut bpf_program) -> c_int;
504
            pub fn pcap_setdirection(arg1: *mut pcap_t, arg2: pcap_direction_t) -> c_int;
505
            // pub fn pcap_getnonblock(arg1: *mut pcap_t, arg2: *mut c_char) -> c_int;
506
            pub fn pcap_setnonblock(arg1: *mut pcap_t, arg2: c_int, arg3: *mut c_char) -> c_int;
507
            pub fn pcap_sendpacket(arg1: *mut pcap_t, arg2: *const c_uchar, arg3: c_int) -> c_int;
508
            // pub fn pcap_statustostr(arg1: c_int) -> *const c_char;
509
            // pub fn pcap_strerror(arg1: c_int) -> *const c_char;
510
            pub fn pcap_geterr(arg1: *mut pcap_t) -> *mut c_char;
511
            // pub fn pcap_perror(arg1: *mut pcap_t, arg2: *mut c_char);
512
            pub fn pcap_compile(
513
                arg1: *mut pcap_t,
514
                arg2: *mut bpf_program,
515
                arg3: *const c_char,
516
                arg4: c_int,
517
                arg5: c_uint,
518
            ) -> c_int;
519
            // pub fn pcap_compile_nopcap(arg1: c_int, arg2: c_int, arg3: *mut bpf_program,
520
            //                            arg4: *const c_char, arg5: c_int, arg6: c_uint) -> c_int;
521
            pub fn pcap_freecode(arg1: *mut bpf_program);
522
            pub fn pcap_offline_filter(
523
                arg1: *const bpf_program,
524
                arg2: *const pcap_pkthdr,
525
                arg3: *const c_uchar,
526
            ) -> c_int;
527
            pub fn pcap_datalink(arg1: *mut pcap_t) -> c_int;
528
            // pub fn pcap_datalink_ext(arg1: *mut pcap_t) -> c_int;
529
            pub fn pcap_list_datalinks(arg1: *mut pcap_t, arg2: *mut *mut c_int) -> c_int;
530
            pub fn pcap_set_datalink(arg1: *mut pcap_t, arg2: c_int) -> c_int;
531
            pub fn pcap_free_datalinks(arg1: *mut c_int);
532
            pub fn pcap_snapshot(arg1: *mut pcap_t) -> c_int;
533
            // pub fn pcap_is_swapped(arg1: *mut pcap_t) -> c_int;
534
            pub fn pcap_major_version(arg1: *mut pcap_t) -> c_int;
535
            pub fn pcap_minor_version(arg1: *mut pcap_t) -> c_int;
536
            // The one FILE * entrypoint that is not a macro on Windows. What it points at belongs
537
            // to the C runtime libpcap was linked against, which is not necessarily the
538
            // caller's, so only ever compare it to null.
539
            pub fn pcap_file(arg1: *mut pcap_t) -> *mut FILE;
540
            pub fn pcap_fileno(arg1: *mut pcap_t) -> c_int;
541
            pub fn pcap_dump_open(arg1: *mut pcap_t, arg2: *const c_char) -> *mut pcap_dumper_t;
542
            // wpcap does export this one, but the FILE * it returns belongs to the C
543
            // runtime wpcap was linked against, which is not necessarily the caller's, so it is
544
            // no more usable on Windows than the entrypoints above.
545
            #[cfg(not(windows))]
546
            pub fn pcap_dump_file(arg1: *mut pcap_dumper_t) -> *mut FILE;
547
            pub fn pcap_dump_ftell(arg1: *mut pcap_dumper_t) -> c_long;
548
            pub fn pcap_dump_flush(arg1: *mut pcap_dumper_t) -> c_int;
549
            pub fn pcap_dump_close(arg1: *mut pcap_dumper_t);
550
            pub fn pcap_dump(arg1: *mut c_uchar, arg2: *const pcap_pkthdr, arg3: *const c_uchar);
551
            pub fn pcap_freealldevs(arg1: *mut pcap_if_t);
552
            // pub fn pcap_lib_version() -> *const c_char;
553
            // pub fn bpf_image(arg1: *const bpf_insn, arg2: c_int) -> *mut c_char;
554
            // pub fn bpf_dump(arg1: *const bpf_program, arg2: c_int);
555

            
556
            // pub fn pcap_free_tstamp_types(arg1: *mut c_int) -> ();
557
            // pub fn pcap_list_tstamp_types(arg1: *mut pcap_t, arg2: *mut *mut c_int) -> c_int;
558
            // pub fn pcap_tstamp_type_name_to_val(arg1: *const c_char) -> c_int;
559
            // pub fn pcap_tstamp_type_val_to_description(arg1: c_int) -> *const c_char;
560
            // pub fn pcap_tstamp_type_val_to_name(arg1: c_int) -> *const c_char;
561
            #[cfg(libpcap_1_2_1)]
562
            pub fn pcap_set_tstamp_type(arg1: *mut pcap_t, arg2: c_int) -> c_int;
563

            
564
            // pub fn pcap_get_tstamp_precision(arg1: *mut pcap_t) -> c_int;
565
            #[cfg(libpcap_1_5_0)]
566
            pub fn pcap_set_immediate_mode(arg1: *mut pcap_t, arg2: c_int) -> c_int;
567
            #[cfg(libpcap_1_5_0)]
568
            pub fn pcap_set_tstamp_precision(arg1: *mut pcap_t, arg2: c_int) -> c_int;
569

            
570
            #[cfg(libpcap_1_7_2)]
571
            pub fn pcap_dump_open_append(
572
                arg1: *mut pcap_t,
573
                arg2: *const c_char,
574
            ) -> *mut pcap_dumper_t;
575

            
576
            #[cfg(libpcap_1_9_0)]
577
            pub fn pcap_dump_ftell64(arg1: *mut pcap_dumper_t) -> i64;
578

            
579
            // From libpcap 1.9.0, not bound:
580
            // pcap_bufsize
581
            // pcap_createsrcstr
582
            // pcap_findalldevs_ex
583
            // pcap_get_required_select_timeout
584
            // pcap_open
585
            // pcap_parsesrcstr
586
            // pcap_remoteact_accept
587
            // pcap_remoteact_cleanup
588
            // pcap_remoteact_close
589
            // pcap_remoteact_list
590
            // pcap_set_protocol_linux
591
            // pcap_setsampling
592

            
593
            // From libpcap 1.9.1, not bound:
594
            // pcap_datalink_val_to_description_or_dlt
595

            
596
            // From libpcap 1.10.0, not bound:
597
            // pcap_remoteact_accept_ex
598
        }
599
    }
600
}
601

            
602
#[cfg(not(windows))]
603
pcap_ffi! {
604
    #[cfg_attr(test, automock)]
605
    pub mod ffi_unix {
606
        #[link(name = "pcap")]
607
        unsafe extern "C" {
608
            // pub fn pcap_inject(arg1: *mut pcap_t, arg2: *const c_void, arg3: size_t) -> c_int;
609
            pub fn pcap_set_rfmon(arg1: *mut pcap_t, arg2: c_int) -> c_int;
610
            pub fn pcap_get_selectable_fd(arg1: *mut pcap_t) -> c_int;
611
            // wpcap exports no FILE * entrypoints: libpcap may be linked against a different C
612
            // runtime than its caller. On Windows pcap.h defines these names as macros that pull
613
            // the OS handle out of the FILE * and call pcap_hopen_offline()/pcap_dump_hopen().
614
            pub fn pcap_dump_fopen(arg1: *mut pcap_t, fp: *mut FILE) -> *mut pcap_dumper_t;
615
        }
616
    }
617
}
618

            
619
#[cfg(target_os = "macos")]
620
pcap_ffi! {
621
    #[cfg_attr(test, automock)]
622
    pub mod ffi_macos {
623
        unsafe extern "C" {
624
            #[cfg(libpcap_1_5_3)]
625
            pub fn pcap_set_want_pktap(arg1: *mut pcap_t, arg2: c_int) -> c_int;
626
        }
627
    }
628
}
629

            
630
#[cfg(windows)]
631
pcap_ffi! {
632
    #[cfg_attr(test, automock)]
633
    pub mod ffi_windows {
634
        unsafe extern "C" {
635
            pub fn pcap_setmintocopy(arg1: *mut pcap_t, arg2: c_int) -> c_int;
636
            pub fn pcap_getevent(p: *mut pcap_t) -> HANDLE;
637
            pub fn pcap_sendqueue_destroy(queue: *mut pcap_send_queue);
638
            pub fn pcap_sendqueue_queue(
639
                queue: *mut pcap_send_queue,
640
                pkt_header: *const pcap_pkthdr,
641
                pkt_data: *const c_uchar,
642
            ) -> c_int;
643
            pub fn pcap_sendqueue_transmit(
644
                p: *mut pcap_t,
645
                queue: *mut pcap_send_queue,
646
                sync: c_int,
647
            ) -> c_uint;
648
        }
649
    }
650
}
651

            
652
pcap_ffi_library! {
653
    #[cfg_attr(test, automock)]
654
    pub mod ffi_library {
655
        unsafe extern "C" {
656
            pub fn pcap_create(arg1: *const c_char, arg2: *mut c_char) -> *mut pcap_t;
657
            pub fn pcap_open_dead(arg1: c_int, arg2: c_int) -> *mut pcap_t;
658
            pub fn pcap_open_offline(arg1: *const c_char, arg2: *mut c_char) -> *mut pcap_t;
659
            pub fn pcap_datalink_name_to_val(arg1: *const c_char) -> c_int;
660
            pub fn pcap_datalink_val_to_name(arg1: c_int) -> *const c_char;
661
            pub fn pcap_datalink_val_to_description(arg1: c_int) -> *const c_char;
662
            pub fn pcap_findalldevs(arg1: *mut *mut pcap_if_t, arg2: *mut c_char) -> c_int;
663

            
664
            // The FILE * entrypoint wpcap does not export; see ffi_unix above.
665
            #[cfg(not(windows))]
666
            pub fn pcap_fopen_offline(arg1: *mut FILE, arg2: *mut c_char) -> *mut pcap_t;
667

            
668
            #[cfg(windows)]
669
            pub fn pcap_sendqueue_alloc(memsize: c_uint) -> *mut pcap_send_queue;
670

            
671
            #[cfg(libpcap_1_5_0)]
672
            pub fn pcap_open_dead_with_tstamp_precision(
673
                arg1: c_int,
674
                arg2: c_int,
675
                arg3: c_uint,
676
            ) -> *mut pcap_t;
677
            #[cfg(libpcap_1_5_0)]
678
            pub fn pcap_open_offline_with_tstamp_precision(
679
                arg1: *const c_char,
680
                arg2: c_uint,
681
                arg3: *mut c_char,
682
            ) -> *mut pcap_t;
683
            #[cfg(all(not(windows), libpcap_1_5_0))]
684
            pub fn pcap_fopen_offline_with_tstamp_precision(
685
                arg1: *mut FILE,
686
                arg2: c_uint,
687
                arg3: *mut c_char,
688
            ) -> *mut pcap_t;
689

            
690
            #[cfg(libpcap_1_10_0)]
691
            pub fn pcap_init(arg1: c_uint, arg2: *mut c_char) -> c_int;
692
        }
693
    }
694
}
695

            
696
#[cfg(windows)]
697
#[cfg_attr(test, automock)]
698
pub mod optional {
699
    use super::loader::Entry;
700

            
701
    /// `pcap_set_tstamp_type`, added in libpcap 1.2.1.
702
    pub fn has_tstamp_type() -> bool {
703
        static ENTRY: Entry = Entry::new("pcap_set_tstamp_type\0");
704
        ENTRY.is_available()
705
    }
706

            
707
    /// `pcap_open_dead_with_tstamp_precision`, added in libpcap 1.5.0.
708
    pub fn has_dead_precision() -> bool {
709
        static ENTRY: Entry = Entry::new("pcap_open_dead_with_tstamp_precision\0");
710
        ENTRY.is_available()
711
    }
712

            
713
    /// `pcap_open_offline_with_tstamp_precision`, added in libpcap 1.5.0.
714
    pub fn has_offline_precision() -> bool {
715
        static ENTRY: Entry = Entry::new("pcap_open_offline_with_tstamp_precision\0");
716
        ENTRY.is_available()
717
    }
718

            
719
    /// `pcap_set_immediate_mode`, added in libpcap 1.5.0.
720
    pub fn has_immediate_mode() -> bool {
721
        static ENTRY: Entry = Entry::new("pcap_set_immediate_mode\0");
722
        ENTRY.is_available()
723
    }
724

            
725
    /// `pcap_set_tstamp_precision`, added in libpcap 1.5.0.
726
    pub fn has_tstamp_precision() -> bool {
727
        static ENTRY: Entry = Entry::new("pcap_set_tstamp_precision\0");
728
        ENTRY.is_available()
729
    }
730

            
731
    /// `pcap_dump_open_append`, added in libpcap 1.7.2.
732
    pub fn has_dump_append() -> bool {
733
        static ENTRY: Entry = Entry::new("pcap_dump_open_append\0");
734
        ENTRY.is_available()
735
    }
736

            
737
    /// `pcap_dump_ftell64`, added in libpcap 1.9.0.
738
    pub fn has_dump_ftell64() -> bool {
739
        static ENTRY: Entry = Entry::new("pcap_dump_ftell64\0");
740
        ENTRY.is_available()
741
    }
742

            
743
    /// `pcap_init`, added in libpcap 1.10.0.
744
    pub fn has_init() -> bool {
745
        static ENTRY: Entry = Entry::new("pcap_init\0");
746
        ENTRY.is_available()
747
    }
748
}
749

            
750
#[cfg(test)]
751
#[cfg(windows)]
752
mod optional_tests {
753
    use super::{loader, optional};
754

            
755
    // A library without one of these is reported through optional rather than called.
756
    const OPTIONAL: &[&str] = &[
757
        "pcap_set_tstamp_type\0",
758
        "pcap_open_dead_with_tstamp_precision\0",
759
        "pcap_open_offline_with_tstamp_precision\0",
760
        "pcap_set_immediate_mode\0",
761
        "pcap_set_tstamp_precision\0",
762
        "pcap_dump_open_append\0",
763
        "pcap_dump_ftell64\0",
764
        "pcap_init\0",
765
    ];
766

            
767
    // wpcap.dll is resolved by name at runtime, so a name the library does not export becomes
768
    // a panic at the call site rather than a link error. Resolve every one in advance.
769
    #[test]
770
    fn test_entrypoints_resolve() {
771
        if !loader::is_available() {
772
            return;
773
        }
774

            
775
        let names = super::ffi::entrypoint_names()
776
            .into_iter()
777
            .chain(super::ffi_windows::entrypoint_names())
778
            .chain(super::ffi_library::entrypoint_names())
779
            .filter(|name| !OPTIONAL.contains(name));
780
        for name in names {
781
            assert!(
782
                loader::Entry::new(name).is_available(),
783
                "wpcap.dll does not export {}",
784
                name.trim_end_matches('\0')
785
            );
786
        }
787
    }
788

            
789
    #[test]
790
    fn test_entrypoint_names() {
791
        if !optional::has_init() {
792
            return;
793
        }
794

            
795
        assert!(optional::has_tstamp_type());
796
        assert!(optional::has_dead_precision());
797
        assert!(optional::has_offline_precision());
798
        assert!(optional::has_immediate_mode());
799
        assert!(optional::has_tstamp_precision());
800
        assert!(optional::has_dump_append());
801
        assert!(optional::has_dump_ftell64());
802
        assert!(optional::has_init());
803
    }
804
}
805

            
806
// This approach allows all the other modules to keep using the `raw` module as before.
807
#[cfg(not(test))]
808
pub use ffi::*;
809

            
810
#[cfg(not(test))]
811
#[cfg(not(windows))]
812
pub use ffi_unix::*;
813

            
814
#[cfg(not(test))]
815
#[cfg(target_os = "macos")]
816
pub use ffi_macos::*;
817

            
818
#[cfg(not(test))]
819
#[cfg(windows)]
820
pub use ffi_windows::*;
821

            
822
#[cfg(not(test))]
823
pub use ffi_library::*;
824

            
825
#[cfg(not(test))]
826
#[cfg(windows)]
827
pub use optional::*;
828

            
829
#[cfg(test)]
830
pub use mock_ffi::*;
831

            
832
#[cfg(test)]
833
#[cfg(not(windows))]
834
pub use mock_ffi_unix::*;
835

            
836
#[cfg(test)]
837
#[cfg(target_os = "macos")]
838
pub use mock_ffi_macos::*;
839

            
840
#[cfg(test)]
841
#[cfg(windows)]
842
pub use mock_ffi_windows::*;
843

            
844
#[cfg(test)]
845
pub use mock_ffi_library::*;
846

            
847
#[cfg(test)]
848
#[cfg(windows)]
849
pub use mock_optional::*;
850

            
851
#[cfg(test)]
852
pub mod testmod {
853
    use std::{ffi::CString, sync::Mutex};
854

            
855
    use once_cell::sync::Lazy;
856

            
857
    use super::*;
858

            
859
    pub struct GeterrContext(__pcap_geterr::Context);
860

            
861
    // Must be acquired by any test using mock FFI.
862
    pub static RAWMTX: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
863

            
864
    pub fn as_pcap_t<T: ?Sized>(value: &mut T) -> *mut pcap_t {
865
        value as *mut T as *mut pcap_t
866
    }
867

            
868
    pub fn as_pcap_dumper_t<T: ?Sized>(value: &mut T) -> *mut pcap_dumper_t {
869
        value as *mut T as *mut pcap_dumper_t
870
    }
871

            
872
    pub fn as_file<T: ?Sized>(value: &mut T) -> *mut libc::FILE {
873
        value as *mut T as *mut libc::FILE
874
    }
875

            
876
    pub fn geterr_expect(pcap: *mut pcap_t) -> GeterrContext {
877
        // Lock must be acquired by caller.
878
        assert!(RAWMTX.try_lock().is_err());
879

            
880
        let err = CString::new("oh oh").unwrap();
881
        let ctx = pcap_geterr_context();
882
        ctx.checkpoint();
883
        ctx.expect()
884
            .withf_st(move |arg1| *arg1 == pcap)
885
            .return_once_st(|_| err.into_raw());
886

            
887
        GeterrContext(ctx)
888
    }
889
}
890
// GRCOV_EXCL_STOP