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
18
    fn from(flags: u32) -> Self {
32
18
        IfFlags::from_bits_truncate(flags)
33
18
    }
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
26
    fn from(flags: u32) -> Self {
53
26
        match flags & raw::PCAP_IF_CONNECTION_STATUS {
54
18
            raw::PCAP_IF_CONNECTION_STATUS_UNKNOWN => ConnectionStatus::Unknown,
55
2
            raw::PCAP_IF_CONNECTION_STATUS_CONNECTED => ConnectionStatus::Connected,
56
2
            raw::PCAP_IF_CONNECTION_STATUS_DISCONNECTED => ConnectionStatus::Disconnected,
57
4
            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
26
    }
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
18
    fn from(flags: u32) -> Self {
75
18
        DeviceFlags {
76
18
            if_flags: flags.into(),
77
18
            connection_status: flags.into(),
78
18
        }
79
18
    }
80
}
81

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

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

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

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

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

            
106
2
    pub fn is_wireless(&self) -> bool {
107
2
        self.contains(IfFlags::WIRELESS)
108
2
    }
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
22
    fn new(
126
22
        name: String,
127
22
        desc: Option<String>,
128
22
        addresses: Vec<Address>,
129
22
        flags: DeviceFlags,
130
22
    ) -> Device {
131
22
        Device {
132
22
            name,
133
22
            desc,
134
22
            addresses,
135
22
            flags,
136
22
        }
137
22
    }
138

            
139
    /// Opens a `Capture<Active>` on this device.
140
2
    pub fn open(self) -> Result<Capture<Active>, Error> {
141
2
        Capture::from_device(self)?.open()
142
2
    }
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
10
    pub fn lookup() -> Result<Option<Device>, Error> {
149
        unsafe {
150
14
            Device::with_all_devs(|all_devs| {
151
8
                let mut dev = all_devs;
152
12
                while !dev.is_null() {
153
8
                    if let Some(device) = Device::from_pcap_if_t(&*dev) {
154
4
                        return Ok(Some(device));
155
4
                    }
156
4
                    dev = (*dev).next;
157
                }
158
4
                Ok(None)
159
8
            })
160
        }
161
10
    }
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
10
    pub fn list() -> Result<Vec<Device>, Error> {
168
        unsafe {
169
14
            Device::with_all_devs(|all_devs| {
170
8
                let mut devices = vec![];
171
8
                let mut dev = all_devs;
172
22
                while !dev.is_null() {
173
14
                    if let Some(device) = Device::from_pcap_if_t(&*dev) {
174
12
                        devices.push(device);
175
12
                    }
176
14
                    dev = (*dev).next;
177
                }
178
8
                Ok(devices)
179
8
            })
180
        }
181
10
    }
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
22
    unsafe fn from_pcap_if_t(dev: &raw::pcap_if_t) -> Option<Device> {
186
16
        Some(Device::new(
187
22
            unsafe { cstr_to_string(dev.name) }.ok().flatten()?,
188
16
            unsafe { cstr_to_string_lossy(dev.description) },
189
16
            unsafe { Address::new_vec(dev.addresses) },
190
16
            DeviceFlags::from(dev.flags),
191
        ))
192
22
    }
193

            
194
20
    unsafe fn with_all_devs<T, F>(func: F) -> Result<T, Error>
195
20
    where
196
20
        F: FnOnce(*mut raw::pcap_if_t) -> Result<T, Error>,
197
    {
198
20
        let all_devs = Error::with_errbuf(|err| {
199
20
            let mut all_devs: *mut raw::pcap_if_t = ptr::null_mut();
200
20
            if unsafe { raw::pcap_findalldevs(&mut all_devs, err) } != 0 {
201
4
                return Err(unsafe { Error::new(err) });
202
16
            }
203
16
            Ok(all_devs)
204
20
        })?;
205
16
        let result = func(all_devs);
206
16
        unsafe { raw::pcap_freealldevs(all_devs) };
207
16
        result
208
20
    }
209
}
210

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

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

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

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

            
253
    #[cfg(not(windows))]
254
42
    unsafe fn convert_sockaddr(ptr: *const libc::sockaddr) -> Option<IpAddr> {
255
42
        if ptr.is_null() {
256
30
            return None;
257
12
        }
258

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

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

            
271
2
                _ => None,
272
            }
273
        }
274
42
    }
275

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

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

            
295
                _ => None,
296
            }
297
        }
298
    }
299
}
300

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

            
305
    use crate::raw::testmod::{RAWMTX, as_pcap_t};
306

            
307
    use super::*;
308

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
417
        addr.sin_port = 1075;
418
        addr.set_addr(0x0A000042_u32.to_be());
419

            
420
        Sockaddr::SockaddrIn(addr)
421
    }
422

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

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

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

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

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

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

            
466
        addr.sin6_port = 1075;
467
        addr.set_octet(15, 0x42);
468

            
469
        Sockaddr::SockaddrIn6(addr)
470
    }
471

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

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

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

            
494
        assert!(!flags.is_running());
495
        assert!(!flags.is_wireless());
496

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

            
502
        assert!(!format!("{flags:?}").is_empty());
503
    }
504

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

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

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

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

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

            
530
        let mut dummy: isize = 777;
531
        let pcap = as_pcap_t(&mut dummy);
532

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

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

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

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

            
549
        assert!(!format!("{device:?}").is_empty());
550
    }
551

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

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

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

            
565
        let device = Device::lookup().unwrap();
566
        assert!(device.is_none());
567

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

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

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

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

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

            
595
        let ctx = raw::pcap_freealldevs_context();
596
        ctx.checkpoint();
597

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

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

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

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

            
615
        let devices = Device::list().unwrap();
616
        assert!(devices.is_empty());
617

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

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

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

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

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

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

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

            
655
        let ctx = raw::pcap_freealldevs_context();
656
        ctx.checkpoint();
657

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
731
        assert!(Device::lookup().unwrap().is_none());
732
    }
733

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

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

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

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

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

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

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

            
768
        assert!(address.addr.is_ipv4());
769
        assert_eq!(address.addr.to_string(), "10.0.0.66");
770

            
771
        assert!(address.netmask.is_none());
772
        assert!(address.broadcast_addr.is_none());
773
        assert!(address.dst_addr.is_none());
774

            
775
        assert!(!format!("{address:?}").is_empty());
776
    }
777

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

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

            
787
        let pcap_addr: raw::pcap_addr_t = (&mut addr).into();
788

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

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

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

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

            
803
        assert!(address.netmask.is_none());
804
        assert!(address.broadcast_addr.is_none());
805
        assert!(address.dst_addr.is_none());
806

            
807
        assert!(!format!("{address:?}").is_empty());
808
    }
809
}