system_info/unix/posix/
network.rs

1//! Network information.
2
3extern crate alloc;
4
5use alloc::vec::Vec;
6use alloc::borrow::Cow;
7
8use core::{slice, iter};
9
10pub use crate::data::network::{Ip, Address};
11
12#[inline(always)]
13pub(crate) fn slice_c_str(input: &[u8; libc::IFNAMSIZ]) -> &[u8] {
14    for idx in 0..input.len() {
15        if input[idx] == 0 {
16            return &input[..idx];
17        }
18    }
19
20    &input[..]
21}
22
23///Iterator over socket addresses
24pub struct Addresses<'a> {
25    cursor: iter::Copied<slice::Iter<'a, Address>>
26}
27
28impl<'a> Addresses<'a> {
29    ///Moves cursor, returning address, if it is valid IP address.
30    pub fn next_addr(&mut self) -> Option<Address> {
31        self.cursor.next()
32    }
33}
34
35impl<'a> Iterator for Addresses<'a> {
36    type Item = Address;
37
38    #[inline]
39    fn next(&mut self) -> Option<Self::Item> {
40        self.next_addr()
41    }
42}
43
44pub(crate) struct InterfaceData {
45    pub(crate) name: [u8; libc::IFNAMSIZ],
46    pub(crate) addresses: Vec<Address>
47}
48
49impl InterfaceData {
50    #[inline]
51    pub(crate) fn name(&self) -> &[u8] {
52        slice_c_str(&self.name)
53    }
54
55    #[inline]
56    pub(crate) fn push(&mut self, addr: Address) {
57        self.addresses.push(addr);
58    }
59}
60
61///Network interface
62pub struct Interface<'a> {
63    data: &'a InterfaceData
64}
65
66impl<'a> Interface<'a> {
67    #[inline]
68    ///Returns name of the interface, if available as utf-8 string.
69    pub fn name(&'a self) -> Option<Cow<'a, str>> {
70        let name = self.data.name();
71        match core::str::from_utf8(name) {
72            Ok(name) => Some(name.into()),
73            Err(_) => None,
74        }
75    }
76
77    #[inline(always)]
78    ///Returns iterator over interface's addresses.
79    pub fn addresses(&'a self) -> Addresses<'a> {
80        Addresses {
81            cursor: self.data.addresses.iter().copied()
82        }
83    }
84}
85
86///Iterator over [Interfaces](struct.Interfaces.html)
87pub struct InterfacesIter<'a> {
88    cursor: slice::Iter<'a, InterfaceData>,
89}
90
91impl<'a> InterfacesIter<'a> {
92    #[inline(always)]
93    ///Returns current interface, if any, without moving cursor
94    pub fn interface(&'a self) -> Option<Interface<'a>> {
95        self.cursor.as_slice().get(0).map(|data| Interface {
96            data
97        })
98    }
99
100    #[inline(always)]
101    ///Moves cursor and returns current interface, if there is any.
102    pub fn next_interface(&'a mut self) -> Option<Interface<'a>> {
103        self.next()
104    }
105}
106
107impl<'a> Iterator for InterfacesIter<'a> {
108    type Item = Interface<'a>;
109
110    #[inline]
111    fn next(&mut self) -> Option<Self::Item> {
112        self.cursor.next().map(|data| Interface {
113            data
114        })
115    }
116}
117
118///Network interfaces enumerator.
119pub struct Interfaces {
120    pub(crate) inner: Vec<InterfaceData>,
121}
122
123impl Interfaces {
124    ///Returns iterator over interfaces
125    pub fn iter(&self) -> InterfacesIter<'_> {
126        InterfacesIter {
127            cursor: self.inner.iter()
128        }
129    }
130
131    #[cfg(not(any(target_os = "linux", target_os = "android")))]
132    fn store_interface(&mut self, ifa_name: *const i8) -> &mut InterfaceData {
133        use core::{cmp, ptr};
134
135        let mut name = [0u8; libc::IFNAMSIZ];
136        if !ifa_name.is_null() {
137            unsafe {
138                let len = cmp::min(name.len(), libc::strlen(ifa_name));
139                ptr::copy_nonoverlapping(ifa_name, name.as_mut_ptr() as _, len)
140            };
141        }
142
143        let real_name = slice_c_str(&name);
144
145        match self.inner.binary_search_by_key(&real_name, |interface| interface.name()) {
146            Ok(idx) => unsafe {
147                self.inner.get_unchecked_mut(idx)
148            },
149            Err(idx) => {
150                let interface = InterfaceData {
151                    name,
152                    addresses: Vec::new(),
153                };
154                self.inner.insert(idx, interface);
155
156                unsafe {
157                    self.inner.get_unchecked_mut(idx)
158                }
159            }
160        }
161    }
162
163    #[cfg(not(any(target_os = "linux", target_os = "android")))]
164    ///Creates new instance.
165    ///
166    ///In case of failure please check `std::io::Error::last_os_error()`
167    pub fn new() -> Option<Self> {
168        use core::mem;
169
170        struct IfAddrs(*mut libc::ifaddrs);
171
172        impl IfAddrs {
173            fn iter<'a>(&'a self) -> IfAddrsIter<'a> {
174                unsafe {
175                    IfAddrsIter(self.0.as_ref())
176                }
177            }
178        }
179
180        impl Drop for IfAddrs {
181            fn drop(&mut self) {
182                unsafe {
183                    libc::freeifaddrs(self.0);
184                }
185            }
186        }
187
188        struct IfAddrsIter<'a>(Option<&'a libc::ifaddrs>);
189
190        impl<'a> Iterator for IfAddrsIter<'a> {
191            type Item = &'a libc::ifaddrs;
192            fn next(&mut self) -> Option<Self::Item> {
193                match self.0 {
194                    Some(next) => unsafe {
195                        self.0 = next.ifa_next.as_ref();
196                        Some(next)
197                    },
198                    None => None,
199                }
200            }
201        }
202
203        let mut if_addrs = mem::MaybeUninit::<*mut libc::ifaddrs>::uninit();
204        let if_addrs = unsafe {
205            if libc::getifaddrs(if_addrs.as_mut_ptr()) != 0 {
206                return None;
207            }
208            IfAddrs(if_addrs.assume_init())
209        };
210
211        let mut result = Interfaces {
212            inner: Vec::new()
213        };
214        for addr in if_addrs.iter() {
215            let ifa_addr = unsafe {
216                addr.ifa_addr.as_ref()
217            };
218            //skip empty addresses
219            let ifa_addr = match ifa_addr {
220                Some(ifa_addr) => ifa_addr,
221                None => continue,
222            };
223
224            let interface = result.store_interface(addr.ifa_name);
225            if ifa_addr.sa_family == libc::AF_INET as _ {
226                let ifa_addr: &libc::sockaddr_in = unsafe {
227                    mem::transmute(ifa_addr)
228                };
229
230                let ip = ifa_addr.sin_addr.s_addr.to_ne_bytes();
231                let ip = Ip::V4([ip[0], ip[1], ip[2], ip[3]]);
232                let net_mask = unsafe {
233                    addr.ifa_netmask.as_ref()
234                };
235                let prefix = match net_mask {
236                    //net_mask should have the same family, it is error not to
237                    //which would mean getifaddrs() implementation sucks
238                    Some(net_mask) if net_mask.sa_family == libc::AF_INET as _ => {
239                        let net_mask: &libc::sockaddr_in = unsafe {
240                            mem::transmute(net_mask)
241                        };
242
243                        net_mask.sin_addr.s_addr.count_ones() as u8
244                    },
245                    _ => 0
246                };
247
248                interface.push(Address {
249                    ip,
250                    prefix
251                });
252            } else if ifa_addr.sa_family == libc::AF_INET6 as _ {
253                let ifa_addr: &libc::sockaddr_in6 = unsafe {
254                    mem::transmute(ifa_addr)
255                };
256
257                let ip: [u16; 8] = unsafe {
258                    mem::transmute(ifa_addr.sin6_addr.s6_addr)
259                };
260                let ip = Ip::V6(ip);
261                let net_mask = unsafe {
262                    addr.ifa_netmask.as_ref()
263                };
264                let prefix = match net_mask {
265                    //net_mask should have the same family, it is error not to
266                    //which would mean getifaddrs() implementation sucks
267                    Some(net_mask) if net_mask.sa_family == libc::AF_INET6 as _ => {
268                        let net_mask: &libc::sockaddr_in6 = unsafe {
269                            mem::transmute(net_mask)
270                        };
271
272                        let net_mask: u128 = unsafe {
273                            mem::transmute(net_mask.sin6_addr.s6_addr)
274                        };
275                        net_mask.count_ones() as u8
276                    },
277                    _ => 0
278                };
279
280                interface.push(Address {
281                    ip,
282                    prefix
283                });
284            }
285        }
286
287        Some(result)
288    }
289}
290
291impl<'a> IntoIterator for &'a Interfaces {
292    type Item = Interface<'a>;
293    type IntoIter = InterfacesIter<'a>;
294
295    #[inline(always)]
296    fn into_iter(self) -> Self::IntoIter {
297        self.iter()
298    }
299}