1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use crate::common::MacAddr; |
4 | use std::ptr::null_mut; |
5 | |
6 | /// This iterator yields an interface name and address. |
7 | pub(crate) struct InterfaceAddressIterator { |
8 | /// Pointer to the current `ifaddrs` struct. |
9 | ifap: *mut libc::ifaddrs, |
10 | /// Pointer to the first element in linked list. |
11 | buf: *mut libc::ifaddrs, |
12 | } |
13 | |
14 | impl Iterator for InterfaceAddressIterator { |
15 | type Item = (String, MacAddr); |
16 | |
17 | fn next(&mut self) -> Option<Self::Item> { |
18 | unsafe { |
19 | while !self.ifap.is_null() { |
20 | // advance the pointer until a MAC address is found |
21 | let ifap = self.ifap; |
22 | self.ifap = (*ifap).ifa_next; |
23 | |
24 | if let Some(addr) = parse_interface_address(ifap) { |
25 | // libc::IFNAMSIZ + 6 |
26 | // This size refers to ./apple/network.rs:75 |
27 | let mut name = vec![0u8; libc::IFNAMSIZ + 6]; |
28 | libc::strcpy(name.as_mut_ptr() as _, (*ifap).ifa_name); |
29 | name.set_len(libc::strlen((*ifap).ifa_name)); |
30 | let name = String::from_utf8_unchecked(name); |
31 | |
32 | return Some((name, addr)); |
33 | } |
34 | } |
35 | None |
36 | } |
37 | } |
38 | } |
39 | |
40 | impl Drop for InterfaceAddressIterator { |
41 | fn drop(&mut self) { |
42 | unsafe { |
43 | libc::freeifaddrs(self.buf); |
44 | } |
45 | } |
46 | } |
47 | |
48 | #[cfg (any(target_os = "macos" , target_os = "freebsd" , target_os = "ios" ))] |
49 | impl From<&libc::sockaddr_dl> for MacAddr { |
50 | fn from(value: &libc::sockaddr_dl) -> Self { |
51 | let sdl_data = value.sdl_data; |
52 | // interface name length, NO trailing 0 |
53 | let sdl_nlen = value.sdl_nlen as usize; |
54 | // make sure that it is never out of bound |
55 | if sdl_nlen + 5 < 12 { |
56 | MacAddr([ |
57 | sdl_data[sdl_nlen] as u8, |
58 | sdl_data[sdl_nlen + 1] as u8, |
59 | sdl_data[sdl_nlen + 2] as u8, |
60 | sdl_data[sdl_nlen + 3] as u8, |
61 | sdl_data[sdl_nlen + 4] as u8, |
62 | sdl_data[sdl_nlen + 5] as u8, |
63 | ]) |
64 | } else { |
65 | MacAddr::UNSPECIFIED |
66 | } |
67 | } |
68 | } |
69 | |
70 | #[cfg (any(target_os = "macos" , target_os = "freebsd" , target_os = "ios" ))] |
71 | unsafe fn parse_interface_address(ifap: *const libc::ifaddrs) -> Option<MacAddr> { |
72 | let sock_addr = (*ifap).ifa_addr; |
73 | if sock_addr.is_null() { |
74 | return None; |
75 | } |
76 | match (*sock_addr).sa_family as libc::c_int { |
77 | libc::AF_LINK => { |
78 | let addr = sock_addr as *const libc::sockaddr_dl; |
79 | Some(MacAddr::from(&*addr)) |
80 | } |
81 | _ => None, |
82 | } |
83 | } |
84 | |
85 | #[cfg (any(target_os = "linux" , target_os = "android" ))] |
86 | unsafe fn parse_interface_address(ifap: *const libc::ifaddrs) -> Option<MacAddr> { |
87 | use libc::sockaddr_ll; |
88 | |
89 | let sock_addr: *mut sockaddr = (*ifap).ifa_addr; |
90 | if sock_addr.is_null() { |
91 | return None; |
92 | } |
93 | match (*sock_addr).sa_family as libc::c_int { |
94 | libc::AF_PACKET => { |
95 | let addr: *const sockaddr_ll = sock_addr as *const sockaddr_ll; |
96 | // Take the first 6 bytes |
97 | let [addr: [u8; 6] @ .., _, _] = (*addr).sll_addr; |
98 | Some(MacAddr(addr)) |
99 | } |
100 | _ => None, |
101 | } |
102 | } |
103 | |
104 | /// Return an iterator on (interface_name, address) pairs |
105 | pub(crate) fn get_interface_address() -> Result<InterfaceAddressIterator, String> { |
106 | let mut ifap: *mut ifaddrs = null_mut(); |
107 | unsafe { |
108 | if retry_eintr!(libc::getifaddrs(&mut ifap)) == 0 && !ifap.is_null() { |
109 | Ok(InterfaceAddressIterator { ifap, buf: ifap }) |
110 | } else { |
111 | Err("failed to call getifaddrs()" .to_string()) |
112 | } |
113 | } |
114 | } |
115 | |