1
use std::{net::IpAddr, ptr};
2

            
3
use bitflags::bitflags;
4

            
5
#[cfg(target_os = "windows")]
6
use windows_sys::Win32::Networking::WinSock;
7

            
8
use crate::{
9
    Error,
10
    capture::{Active, Capture},
11
    cstr_to_string, cstr_to_string_lossy, raw,
12
};
13

            
14
bitflags! {
15
    /// Network device flags.
16
    #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
17
    pub struct IfFlags: u32 {
18
        /// Set if the device is a loopback interface
19
        const LOOPBACK = raw::PCAP_IF_LOOPBACK;
20
        /// Set if the device is up
21
        const UP = raw::PCAP_IF_UP;
22
        /// Set if the device is running
23
        const RUNNING = raw::PCAP_IF_RUNNING;
24
        /// Set if the device is a wireless interface; this includes IrDA as well as radio-based
25
        /// networks such as IEEE 802.15.4 and IEEE 802.11, so it doesn't just mean Wi-Fi
26
        const WIRELESS = raw::PCAP_IF_WIRELESS;
27
    }
28
}
29

            
30
impl From<u32> for IfFlags {
31
9
    fn from(flags: u32) -> Self {
32
9
        IfFlags::from_bits_truncate(flags)
33
9
    }
34
}
35

            
36
#[derive(Debug, Clone, PartialEq, Eq)]
37
/// Indication of whether the adapter is connected or not; for wireless interfaces, "connected"
38
/// means "associated with a network".
39
pub enum ConnectionStatus {
40
    /// It's unknown whether the adapter is connected or not
41
    Unknown,
42
    /// The adapter is connected
43
    Connected,
44
    /// The adapter is disconnected
45
    Disconnected,
46
    /// The notion of "connected" and "disconnected" don't apply to this interface; for example, it
47
    /// doesn't apply to a loopback device
48
    NotApplicable,
49
}
50

            
51
impl From<u32> for ConnectionStatus {
52
13
    fn from(flags: u32) -> Self {
53
13
        match flags & raw::PCAP_IF_CONNECTION_STATUS {
54
9
            raw::PCAP_IF_CONNECTION_STATUS_UNKNOWN => ConnectionStatus::Unknown,
55
1
            raw::PCAP_IF_CONNECTION_STATUS_CONNECTED => ConnectionStatus::Connected,
56
1
            raw::PCAP_IF_CONNECTION_STATUS_DISCONNECTED => ConnectionStatus::Disconnected,
57
2
            raw::PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE => ConnectionStatus::NotApplicable,
58
            // DeviceFlags::CONNECTION_STATUS should be a 2-bit mask which means that the four
59
            // values should cover all the possibilities.
60
            // GRCOV_EXCL_START
61
            _ => unreachable!(),
62
            // GRCOV_EXCL_STOP
63
        }
64
13
    }
65
}
66

            
67
#[derive(Debug, Clone)]
68
pub struct DeviceFlags {
69
    pub if_flags: IfFlags,
70
    pub connection_status: ConnectionStatus,
71
}
72

            
73
impl From<u32> for DeviceFlags {
74
9
    fn from(flags: u32) -> Self {
75
9
        DeviceFlags {
76
9
            if_flags: flags.into(),
77
9
            connection_status: flags.into(),
78
9
        }
79
9
    }
80
}
81

            
82
impl DeviceFlags {
83
3
    pub fn empty() -> Self {
84
3
        DeviceFlags {
85
3
            if_flags: IfFlags::empty(),
86
3
            connection_status: ConnectionStatus::Unknown,
87
3
        }
88
3
    }
89

            
90
5
    pub fn contains(&self, if_flags: IfFlags) -> bool {
91
5
        self.if_flags.contains(if_flags)
92
5
    }
93

            
94
1
    pub fn is_loopback(&self) -> bool {
95
1
        self.contains(IfFlags::LOOPBACK)
96
1
    }
97

            
98
1
    pub fn is_up(&self) -> bool {
99
1
        self.contains(IfFlags::UP)
100
1
    }
101

            
102
1
    pub fn is_running(&self) -> bool {
103
1
        self.contains(IfFlags::RUNNING)
104
1
    }
105

            
106
1
    pub fn is_wireless(&self) -> bool {
107
1
        self.contains(IfFlags::WIRELESS)
108
1
    }
109
}
110

            
111
#[derive(Debug, Clone)]
112
/// A network device name and pcap's description of it.
113
pub struct Device {
114
    /// The name of the interface
115
    pub name: String,
116
    /// A textual description of the interface, if available
117
    pub desc: Option<String>,
118
    /// Addresses associated with this interface
119
    pub addresses: Vec<Address>,
120
    /// Interface flags
121
    pub flags: DeviceFlags,
122
}
123

            
124
impl Device {
125
11
    fn new(
126
11
        name: String,
127
11
        desc: Option<String>,
128
11
        addresses: Vec<Address>,
129
11
        flags: DeviceFlags,
130
11
    ) -> Device {
131
11
        Device {
132
11
            name,
133
11
            desc,
134
11
            addresses,
135
11
            flags,
136
11
        }
137
11
    }
138

            
139
    /// Opens a `Capture<Active>` on this device.
140
1
    pub fn open(self) -> Result<Capture<Active>, Error> {
141
1
        Capture::from_device(self)?.open()
142
1
    }
143

            
144
    /// Returns the default Device suitable for captures according to pcap_findalldevs,
145
    /// or an error from pcap. Note that there may be no suitable devices.
146
    ///
147
    /// A device whose name is not valid UTF-8 is passed over, as in `Device::list`.
148
5
    pub fn lookup() -> Result<Option<Device>, Error> {
149
        unsafe {
150
5
            Device::with_all_devs(|all_devs| {
151
4
                let mut dev = all_devs;
152
6
                while !dev.is_null() {
153
4
                    if let Some(device) = Device::from_pcap_if_t(&*dev) {
154
2
                        return Ok(Some(device));
155
2
                    }
156
2
                    dev = (*dev).next;
157
                }
158
2
                Ok(None)
159
4
            })
160
        }
161
5
    }
162

            
163
    /// Returns a vector of `Device`s known by pcap via pcap_findalldevs.
164
    ///
165
    /// A device whose name is not valid UTF-8 is left out, since that name is the only handle
166
    /// libpcap offers on it and it cannot be opened.
167
5
    pub fn list() -> Result<Vec<Device>, Error> {
168
        unsafe {
169
5
            Device::with_all_devs(|all_devs| {
170
4
                let mut devices = vec![];
171
4
                let mut dev = all_devs;
172
11
                while !dev.is_null() {
173
7
                    if let Some(device) = Device::from_pcap_if_t(&*dev) {
174
6
                        devices.push(device);
175
6
                    }
176
7
                    dev = (*dev).next;
177
                }
178
4
                Ok(devices)
179
4
            })
180
        }
181
5
    }
182

            
183
    // Returns `None` when the name cannot be decoded, since the device cannot be opened without
184
    // it. The devices on either side of it are unaffected.
185
11
    unsafe fn from_pcap_if_t(dev: &raw::pcap_if_t) -> Option<Device> {
186
8
        Some(Device::new(
187
11
            unsafe { cstr_to_string(dev.name) }.ok().flatten()?,
188
8
            unsafe { cstr_to_string_lossy(dev.description) },
189
8
            unsafe { Address::new_vec(dev.addresses) },
190
8
            DeviceFlags::from(dev.flags),
191
        ))
192
11
    }
193

            
194
10
    unsafe fn with_all_devs<T, F>(func: F) -> Result<T, Error>
195
10
    where
196
10
        F: FnOnce(*mut raw::pcap_if_t) -> Result<T, Error>,
197
    {
198
10
        let library = raw::require_library()?;
199

            
200
10
        let all_devs = Error::with_errbuf(|err| {
201
10
            let mut all_devs: *mut raw::pcap_if_t = ptr::null_mut();
202
10
            if unsafe { raw::pcap_findalldevs(&library, &mut all_devs, err) } != 0 {
203
2
                return Err(unsafe { Error::new(err) });
204
8
            }
205
8
            Ok(all_devs)
206
10
        })?;
207
8
        let result = func(all_devs);
208
8
        unsafe { raw::pcap_freealldevs(all_devs) };
209
8
        result
210
10
    }
211
}
212

            
213
impl From<&str> for Device {
214
3
    fn from(name: &str) -> Self {
215
3
        Device::new(name.into(), None, Vec::new(), DeviceFlags::empty())
216
3
    }
217
}
218

            
219
#[derive(Debug, Clone)]
220
/// Address information for an interface
221
pub struct Address {
222
    /// The address
223
    pub addr: IpAddr,
224
    /// Network mask for this address
225
    pub netmask: Option<IpAddr>,
226
    /// Broadcast address for this address
227
    pub broadcast_addr: Option<IpAddr>,
228
    /// P2P destination address for this address
229
    pub dst_addr: Option<IpAddr>,
230
}
231

            
232
impl Address {
233
8
    unsafe fn new_vec(mut ptr: *const raw::pcap_addr_t) -> Vec<Address> {
234
8
        let mut vec = Vec::new();
235
11
        while !ptr.is_null() {
236
3
            if let Some(addr) = unsafe { Address::new(ptr) } {
237
3
                vec.push(addr);
238
3
            }
239
3
            ptr = unsafe { (*ptr).next };
240
        }
241
8
        vec
242
8
    }
243

            
244
6
    unsafe fn new(ptr: *const raw::pcap_addr_t) -> Option<Address> {
245
        unsafe {
246
6
            Self::convert_sockaddr((*ptr).addr).map(|addr| Address {
247
5
                addr,
248
5
                netmask: Self::convert_sockaddr((*ptr).netmask),
249
5
                broadcast_addr: Self::convert_sockaddr((*ptr).broadaddr),
250
5
                dst_addr: Self::convert_sockaddr((*ptr).dstaddr),
251
5
            })
252
        }
253
6
    }
254

            
255
    #[cfg(not(windows))]
256
21
    unsafe fn convert_sockaddr(ptr: *const libc::sockaddr) -> Option<IpAddr> {
257
21
        if ptr.is_null() {
258
15
            return None;
259
6
        }
260

            
261
        unsafe {
262
6
            match (*ptr).sa_family as i32 {
263
                libc::AF_INET => {
264
3
                    let ptr: *const libc::sockaddr_in = std::mem::transmute(ptr);
265
3
                    Some(IpAddr::V4(u32::from_be((*ptr).sin_addr.s_addr).into()))
266
                }
267

            
268
                libc::AF_INET6 => {
269
2
                    let ptr: *const libc::sockaddr_in6 = std::mem::transmute(ptr);
270
2
                    Some(IpAddr::V6((*ptr).sin6_addr.s6_addr.into()))
271
                }
272

            
273
1
                _ => None,
274
            }
275
        }
276
21
    }
277

            
278
    #[cfg(windows)]
279
    unsafe fn convert_sockaddr(ptr: *const libc::sockaddr) -> Option<IpAddr> {
280
        if ptr.is_null() {
281
            return None;
282
        }
283

            
284
        unsafe {
285
            match (*ptr).sa_family {
286
                WinSock::AF_INET => {
287
                    let ptr: *const WinSock::SOCKADDR_IN = std::mem::transmute(ptr);
288
                    let addr: [u8; 4] = ((*ptr).sin_addr.S_un.S_addr).to_ne_bytes();
289
                    Some(IpAddr::from(addr))
290
                }
291
                WinSock::AF_INET6 => {
292
                    let ptr: *const WinSock::SOCKADDR_IN6 = std::mem::transmute(ptr);
293
                    let addr = (*ptr).sin6_addr.u.Byte;
294
                    Some(IpAddr::from(addr))
295
                }
296

            
297
                _ => None,
298
            }
299
        }
300
    }
301
}
302

            
303
#[cfg(test)]
304
mod tests {
305
    use std::ffi::CString;
306

            
307
    use crate::raw::testmod::{RAWMTX, as_pcap_t};
308

            
309
    use super::*;
310

            
311
    #[cfg(not(windows))]
312
    enum Sockaddr {
313
        SockaddrIn(libc::sockaddr_in),
314
        SockaddrIn6(libc::sockaddr_in6),
315
    }
316

            
317
    #[cfg(windows)]
318
    enum Sockaddr {
319
        SockaddrIn(WinSock::SOCKADDR_IN),
320
        SockaddrIn6(WinSock::SOCKADDR_IN6),
321
    }
322

            
323
    impl Sockaddr {
324
        fn as_mut_ptr(&mut self) -> *mut libc::sockaddr {
325
            match self {
326
                Sockaddr::SockaddrIn(sin) => sin as *mut _ as _,
327
                Sockaddr::SockaddrIn6(sin6) => sin6 as *mut _ as _,
328
            }
329
        }
330

            
331
        fn set_family(&mut self, family: u16) {
332
            // Annoyingly this differs between Linux (u16) and Mac (u8).
333
            #[cfg(not(windows))]
334
            let family = family as libc::sa_family_t;
335

            
336
            match self {
337
                Sockaddr::SockaddrIn(sin) => sin.sin_family = family,
338
                Sockaddr::SockaddrIn6(sin6) => sin6.sin6_family = family,
339
            }
340
        }
341
    }
342

            
343
    static IF1_NAME: &str = "if1";
344
    static IF2_NAME: &str = "if2";
345
    static IF1_DESC: &str = "if1 desc";
346
    static IF2_DESC: &str = "if2 desc";
347

            
348
    fn dev(name: &[u8], description: Option<&[u8]>) -> raw::pcap_if_t {
349
        raw::pcap_if_t {
350
            next: std::ptr::null_mut(),
351
            name: CString::new(name).unwrap().into_raw(),
352
            description: match description {
353
                Some(description) => CString::new(description).unwrap().into_raw(),
354
                None => std::ptr::null_mut(),
355
            },
356
            addresses: std::ptr::null_mut(),
357
            flags: 0,
358
        }
359
    }
360

            
361
    fn devs() -> Vec<raw::pcap_if_t> {
362
        let mut devs = vec![
363
            raw::pcap_if_t {
364
                next: std::ptr::null_mut(),
365
                name: CString::new(IF1_NAME).unwrap().into_raw(),
366
                description: CString::new(IF1_DESC).unwrap().into_raw(),
367
                addresses: std::ptr::null_mut(),
368
                flags: (raw::PCAP_IF_LOOPBACK | raw::PCAP_IF_UP),
369
            },
370
            raw::pcap_if_t {
371
                next: std::ptr::null_mut(),
372
                name: CString::new(IF2_NAME).unwrap().into_raw(),
373
                description: CString::new(IF2_DESC).unwrap().into_raw(),
374
                addresses: std::ptr::null_mut(),
375
                flags: 0,
376
            },
377
        ];
378
        devs[0].next = &mut devs[1];
379
        devs
380
    }
381

            
382
    trait InetAddressV4 {
383
        fn new() -> Self;
384
        fn set_addr(&mut self, addr: u32);
385
    }
386

            
387
    #[cfg(not(windows))]
388
    impl InetAddressV4 for libc::sockaddr_in {
389
        fn new() -> Self {
390
            let mut addr: Self = unsafe { std::mem::zeroed() };
391
            addr.sin_family = libc::AF_INET as libc::sa_family_t;
392
            addr
393
        }
394

            
395
        fn set_addr(&mut self, addr: u32) {
396
            self.sin_addr.s_addr = addr;
397
        }
398
    }
399

            
400
    #[cfg(windows)]
401
    impl InetAddressV4 for WinSock::SOCKADDR_IN {
402
        fn new() -> Self {
403
            let mut addr: Self = unsafe { std::mem::zeroed() };
404
            addr.sin_family = WinSock::AF_INET;
405
            addr
406
        }
407

            
408
        fn set_addr(&mut self, addr: u32) {
409
            self.sin_addr.S_un.S_addr = addr;
410
        }
411
    }
412

            
413
    fn sockaddr_ipv4() -> Sockaddr {
414
        #[cfg(not(windows))]
415
        let mut addr: libc::sockaddr_in = InetAddressV4::new();
416
        #[cfg(windows)]
417
        let mut addr: WinSock::SOCKADDR_IN = InetAddressV4::new();
418

            
419
        addr.sin_port = 1075;
420
        addr.set_addr(0x0A000042_u32.to_be());
421

            
422
        Sockaddr::SockaddrIn(addr)
423
    }
424

            
425
    trait InetAddressV6 {
426
        fn new() -> Self;
427
        fn set_octet(&mut self, index: usize, octet: u8);
428
    }
429

            
430
    #[cfg(not(windows))]
431
    impl InetAddressV6 for libc::sockaddr_in6 {
432
        fn new() -> Self {
433
            let mut addr: Self = unsafe { std::mem::zeroed() };
434
            addr.sin6_family = libc::AF_INET6 as libc::sa_family_t;
435
            addr.sin6_addr.s6_addr[0] = 0xFE;
436
            addr.sin6_addr.s6_addr[1] = 0x80;
437
            addr
438
        }
439

            
440
        fn set_octet(&mut self, index: usize, octet: u8) {
441
            self.sin6_addr.s6_addr[index] = octet;
442
        }
443
    }
444

            
445
    #[cfg(windows)]
446
    impl InetAddressV6 for WinSock::SOCKADDR_IN6 {
447
        fn new() -> Self {
448
            let mut addr: Self = unsafe { std::mem::zeroed() };
449
            addr.sin6_family = WinSock::AF_INET6;
450
            unsafe {
451
                addr.sin6_addr.u.Byte[0] = 0xFE;
452
                addr.sin6_addr.u.Byte[1] = 0x80;
453
            }
454
            addr
455
        }
456

            
457
        fn set_octet(&mut self, index: usize, octet: u8) {
458
            unsafe { self.sin6_addr.u.Byte[index] = octet };
459
        }
460
    }
461

            
462
    fn sockaddr_ipv6() -> Sockaddr {
463
        #[cfg(not(windows))]
464
        let mut addr: libc::sockaddr_in6 = InetAddressV6::new();
465
        #[cfg(windows)]
466
        let mut addr: WinSock::SOCKADDR_IN6 = InetAddressV6::new();
467

            
468
        addr.sin6_port = 1075;
469
        addr.set_octet(15, 0x42);
470

            
471
        Sockaddr::SockaddrIn6(addr)
472
    }
473

            
474
    impl From<&mut Sockaddr> for raw::pcap_addr_t {
475
        fn from(value: &mut Sockaddr) -> Self {
476
            raw::pcap_addr_t {
477
                next: std::ptr::null_mut(),
478
                addr: value.as_mut_ptr(),
479
                netmask: std::ptr::null_mut(),
480
                broadaddr: std::ptr::null_mut(),
481
                dstaddr: std::ptr::null_mut(),
482
            }
483
        }
484
    }
485

            
486
    #[test]
487
    fn test_device_flags() {
488
        let flags = DeviceFlags::from(
489
            raw::PCAP_IF_LOOPBACK | raw::PCAP_IF_UP | raw::PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE,
490
        );
491

            
492
        assert!(flags.is_loopback());
493
        assert!(flags.is_up());
494
        assert!(flags.contains(IfFlags::LOOPBACK | IfFlags::UP));
495

            
496
        assert!(!flags.is_running());
497
        assert!(!flags.is_wireless());
498

            
499
        assert_ne!(flags.connection_status, ConnectionStatus::Unknown);
500
        assert_ne!(flags.connection_status, ConnectionStatus::Connected);
501
        assert_ne!(flags.connection_status, ConnectionStatus::Disconnected);
502
        assert_eq!(flags.connection_status, ConnectionStatus::NotApplicable);
503

            
504
        assert!(!format!("{flags:?}").is_empty());
505
    }
506

            
507
    #[test]
508
    fn test_connection_status() {
509
        let flags = raw::PCAP_IF_CONNECTION_STATUS_UNKNOWN;
510
        assert_eq!(ConnectionStatus::from(flags), ConnectionStatus::Unknown);
511

            
512
        let flags = raw::PCAP_IF_CONNECTION_STATUS_CONNECTED;
513
        assert_eq!(ConnectionStatus::from(flags), ConnectionStatus::Connected);
514

            
515
        let flags = raw::PCAP_IF_CONNECTION_STATUS_DISCONNECTED;
516
        assert_eq!(
517
            ConnectionStatus::from(flags),
518
            ConnectionStatus::Disconnected
519
        );
520

            
521
        let flags = raw::PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE;
522
        assert_eq!(
523
            ConnectionStatus::from(flags),
524
            ConnectionStatus::NotApplicable
525
        );
526
    }
527

            
528
    #[test]
529
    fn test_into_capture() {
530
        let _m = RAWMTX.lock();
531

            
532
        let mut dummy: isize = 777;
533
        let pcap = as_pcap_t(&mut dummy);
534

            
535
        let ctx = raw::pcap_create_context();
536
        ctx.expect().return_once_st(move |_, _, _| pcap);
537

            
538
        let ctx = raw::pcap_activate_context();
539
        ctx.expect()
540
            .withf_st(move |arg1| *arg1 == pcap)
541
            .return_once(|_| 0);
542

            
543
        let ctx = raw::pcap_close_context();
544
        ctx.expect()
545
            .withf_st(move |ptr| *ptr == pcap)
546
            .return_once(|_| {});
547

            
548
        let device: Device = "device".into();
549
        let _capture: Capture<Active> = device.clone().open().unwrap();
550

            
551
        assert!(!format!("{device:?}").is_empty());
552
    }
553

            
554
    #[test]
555
    fn test_lookup() {
556
        let _m = RAWMTX.lock();
557

            
558
        let ctx = raw::pcap_findalldevs_context();
559
        ctx.expect().return_once_st(move |_, arg1, _| {
560
            unsafe { *arg1 = std::ptr::null_mut() };
561
            0
562
        });
563

            
564
        let ctx = raw::pcap_freealldevs_context();
565
        ctx.expect().return_once(move |_| {});
566

            
567
        let device = Device::lookup().unwrap();
568
        assert!(device.is_none());
569

            
570
        let mut devs = devs();
571
        let mut addrs = sockaddr_ipv4();
572
        let mut pcap_addr = (&mut addrs).into();
573
        devs[0].addresses = &mut pcap_addr;
574
        let devs_ptr = devs.as_mut_ptr();
575

            
576
        let ctx = raw::pcap_findalldevs_context();
577
        ctx.checkpoint();
578
        ctx.expect().return_once_st(move |_, arg1, _| {
579
            unsafe { *arg1 = devs_ptr };
580
            0
581
        });
582

            
583
        let ctx = raw::pcap_freealldevs_context();
584
        ctx.checkpoint();
585
        ctx.expect().return_once(move |_| {});
586

            
587
        let device = Device::lookup().unwrap().unwrap();
588
        assert_eq!(&device.name, IF1_NAME);
589
        assert_eq!(&device.desc.unwrap(), IF1_DESC);
590
        assert_eq!(device.addresses.len(), 1);
591
        assert!(device.addresses[0].addr.is_ipv4());
592

            
593
        let ctx = raw::pcap_findalldevs_context();
594
        ctx.checkpoint();
595
        ctx.expect().return_once_st(move |_, _, _| -1);
596

            
597
        let ctx = raw::pcap_freealldevs_context();
598
        ctx.checkpoint();
599

            
600
        let result = Device::lookup();
601
        assert!(result.is_err());
602
    }
603

            
604
    #[test]
605
    fn test_list() {
606
        let _m = RAWMTX.lock();
607

            
608
        let ctx = raw::pcap_findalldevs_context();
609
        ctx.expect().return_once_st(move |_, arg1, _| {
610
            unsafe { *arg1 = std::ptr::null_mut() };
611
            0
612
        });
613

            
614
        let ctx = raw::pcap_freealldevs_context();
615
        ctx.expect().return_once(move |_| {});
616

            
617
        let devices = Device::list().unwrap();
618
        assert!(devices.is_empty());
619

            
620
        let mut devs = devs();
621
        let mut ipv4s = sockaddr_ipv4();
622
        let mut ipv6s = sockaddr_ipv6();
623
        let mut pcap_addr: raw::pcap_addr_t = (&mut ipv4s).into();
624
        let mut pcap_addr6: raw::pcap_addr_t = (&mut ipv6s).into();
625
        pcap_addr.next = &mut pcap_addr6;
626
        devs[1].addresses = &mut pcap_addr;
627
        let devs_ptr = devs.as_mut_ptr();
628

            
629
        let ctx = raw::pcap_findalldevs_context();
630
        ctx.checkpoint();
631
        ctx.expect().return_once_st(move |_, arg1, _| {
632
            unsafe { *arg1 = devs_ptr };
633
            0
634
        });
635

            
636
        let ctx = raw::pcap_freealldevs_context();
637
        ctx.checkpoint();
638
        ctx.expect().return_once(move |_| {});
639

            
640
        let devices = Device::list().unwrap();
641
        assert_eq!(devices.len(), devs.len());
642

            
643
        assert_eq!(&devices[0].name, IF1_NAME);
644
        assert_eq!(devices[0].desc.as_ref().unwrap(), IF1_DESC);
645
        assert_eq!(devices[0].addresses.len(), 0);
646

            
647
        assert_eq!(&devices[1].name, IF2_NAME);
648
        assert_eq!(devices[1].desc.as_ref().unwrap(), IF2_DESC);
649
        assert_eq!(devices[1].addresses.len(), 2);
650
        assert!(devices[1].addresses[0].addr.is_ipv4());
651
        assert!(devices[1].addresses[1].addr.is_ipv6());
652

            
653
        let ctx = raw::pcap_findalldevs_context();
654
        ctx.checkpoint();
655
        ctx.expect().return_once_st(move |_, _, _| -1);
656

            
657
        let ctx = raw::pcap_freealldevs_context();
658
        ctx.checkpoint();
659

            
660
        let result = Device::list();
661
        assert!(result.is_err());
662
    }
663

            
664
    #[test]
665
    fn test_list_bad_name() {
666
        let _m = RAWMTX.lock();
667

            
668
        let mut devs = vec![
669
            dev(IF1_NAME.as_bytes(), Some(IF1_DESC.as_bytes())),
670
            dev(b"d\xff\xfe0", Some(IF2_DESC.as_bytes())),
671
            dev(IF2_NAME.as_bytes(), Some(IF2_DESC.as_bytes())),
672
        ];
673
        devs[0].next = &mut devs[1];
674
        devs[1].next = &mut devs[2];
675
        let devs_ptr = devs.as_mut_ptr();
676

            
677
        let ctx = raw::pcap_findalldevs_context();
678
        ctx.expect().return_once_st(move |_, arg1, _| {
679
            unsafe { *arg1 = devs_ptr };
680
            0
681
        });
682

            
683
        let ctx = raw::pcap_freealldevs_context();
684
        ctx.expect().return_once(move |_| {});
685

            
686
        // The interface in the middle is gone, the ones either side of it are not.
687
        let devices = Device::list().unwrap();
688
        assert_eq!(devices.len(), 2);
689
        assert_eq!(&devices[0].name, IF1_NAME);
690
        assert_eq!(&devices[1].name, IF2_NAME);
691
    }
692

            
693
    #[test]
694
    fn test_lookup_bad_name() {
695
        let _m = RAWMTX.lock();
696

            
697
        let mut devs = vec![
698
            dev(b"d\xff\xfe0", Some(IF1_DESC.as_bytes())),
699
            dev(IF2_NAME.as_bytes(), Some(IF2_DESC.as_bytes())),
700
        ];
701
        devs[0].next = &mut devs[1];
702
        let devs_ptr = devs.as_mut_ptr();
703

            
704
        let ctx = raw::pcap_findalldevs_context();
705
        ctx.expect().return_once_st(move |_, arg1, _| {
706
            unsafe { *arg1 = devs_ptr };
707
            0
708
        });
709

            
710
        let ctx = raw::pcap_freealldevs_context();
711
        ctx.expect().return_once(move |_| {});
712

            
713
        let device = Device::lookup().unwrap().unwrap();
714
        assert_eq!(&device.name, IF2_NAME);
715
    }
716

            
717
    #[test]
718
    fn test_lookup_all_bad() {
719
        let _m = RAWMTX.lock();
720

            
721
        let mut devs = vec![dev(b"d\xff\xfe0", Some(IF1_DESC.as_bytes()))];
722
        let devs_ptr = devs.as_mut_ptr();
723

            
724
        let ctx = raw::pcap_findalldevs_context();
725
        ctx.expect().return_once_st(move |_, arg1, _| {
726
            unsafe { *arg1 = devs_ptr };
727
            0
728
        });
729

            
730
        let ctx = raw::pcap_freealldevs_context();
731
        ctx.expect().return_once(move |_| {});
732

            
733
        assert!(Device::lookup().unwrap().is_none());
734
    }
735

            
736
    #[test]
737
    fn test_list_descriptions() {
738
        let _m = RAWMTX.lock();
739

            
740
        let mut devs = vec![
741
            dev(IF1_NAME.as_bytes(), Some(b"caf\xe9")),
742
            dev(IF2_NAME.as_bytes(), None),
743
        ];
744
        devs[0].next = &mut devs[1];
745
        let devs_ptr = devs.as_mut_ptr();
746

            
747
        let ctx = raw::pcap_findalldevs_context();
748
        ctx.expect().return_once_st(move |_, arg1, _| {
749
            unsafe { *arg1 = devs_ptr };
750
            0
751
        });
752

            
753
        let ctx = raw::pcap_freealldevs_context();
754
        ctx.expect().return_once(move |_| {});
755

            
756
        // A description in some other encoding costs the caller a replacement character, no more.
757
        let devices = Device::list().unwrap();
758
        assert_eq!(devices.len(), 2);
759
        assert_eq!(devices[0].desc.as_deref(), Some("caf\u{fffd}"));
760
        assert_eq!(devices[1].desc, None);
761
    }
762

            
763
    #[test]
764
    fn test_address_ipv4() {
765
        let mut addr = sockaddr_ipv4();
766
        let pcap_addr: raw::pcap_addr_t = (&mut addr).into();
767

            
768
        let address = unsafe { Address::new(&pcap_addr) }.unwrap();
769

            
770
        assert!(address.addr.is_ipv4());
771
        assert_eq!(address.addr.to_string(), "10.0.0.66");
772

            
773
        assert!(address.netmask.is_none());
774
        assert!(address.broadcast_addr.is_none());
775
        assert!(address.dst_addr.is_none());
776

            
777
        assert!(!format!("{address:?}").is_empty());
778
    }
779

            
780
    #[test]
781
    fn test_address_family() {
782
        let mut addr = sockaddr_ipv4();
783

            
784
        #[cfg(not(windows))]
785
        addr.set_family(libc::AF_IPX as u16);
786
        #[cfg(windows)]
787
        addr.set_family(WinSock::AF_IPX);
788

            
789
        let pcap_addr: raw::pcap_addr_t = (&mut addr).into();
790

            
791
        let address = unsafe { Address::new(&pcap_addr) };
792
        assert!(address.is_none());
793
    }
794

            
795
    #[test]
796
    fn test_address_ipv6() {
797
        let mut addr = sockaddr_ipv6();
798
        let pcap_addr: raw::pcap_addr_t = (&mut addr).into();
799

            
800
        let address = unsafe { Address::new(&pcap_addr) }.unwrap();
801

            
802
        assert!(address.addr.is_ipv6());
803
        assert_eq!(address.addr.to_string(), "fe80::42");
804

            
805
        assert!(address.netmask.is_none());
806
        assert!(address.broadcast_addr.is_none());
807
        assert!(address.dst_addr.is_none());
808

            
809
        assert!(!format!("{address:?}").is_empty());
810
    }
811
}