capybara_util/
ifaddrs.rs

1use std::ffi::CStr;
2use std::io;
3use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
4use std::ptr;
5
6use foreign_types::{foreign_type, ForeignType, ForeignTypeRef};
7
8foreign_type! {
9    pub unsafe type IfAddrs: Sync+Send {
10        type CType = libc::ifaddrs;
11        fn drop = libc::freeifaddrs;
12    }
13}
14
15impl IfAddrs {
16    pub fn get() -> io::Result<IfAddrs> {
17        unsafe {
18            let mut ifaddrs = ptr::null_mut();
19            let r = libc::getifaddrs(&mut ifaddrs);
20            if r == 0 {
21                Ok(IfAddrs::from_ptr(ifaddrs))
22            } else {
23                Err(io::Error::last_os_error())
24            }
25        }
26    }
27}
28
29impl IfAddrsRef {
30    pub fn next(&self) -> Option<&IfAddrsRef> {
31        unsafe {
32            let next = (*self.as_ptr()).ifa_next;
33            if next.is_null() {
34                None
35            } else {
36                Some(IfAddrsRef::from_ptr(next))
37            }
38        }
39    }
40
41    pub fn name(&self) -> &str {
42        unsafe {
43            let s = CStr::from_ptr((*self.as_ptr()).ifa_name);
44            s.to_str().unwrap()
45        }
46    }
47
48    pub fn addr(&self) -> Option<IpAddr> {
49        unsafe {
50            let addr = (*self.as_ptr()).ifa_addr;
51            if addr.is_null() {
52                return None;
53            }
54
55            match (*addr).sa_family as _ {
56                libc::AF_INET => {
57                    let addr = addr as *mut libc::sockaddr_in;
58                    // It seems like this to_be shouldn't be needed?
59                    let addr = Ipv4Addr::from((*addr).sin_addr.s_addr.to_be());
60                    Some(IpAddr::V4(addr))
61                }
62                libc::AF_INET6 => {
63                    let addr = addr as *mut libc::sockaddr_in6;
64                    let addr = Ipv6Addr::from((*addr).sin6_addr.s6_addr);
65                    Some(IpAddr::V6(addr))
66                }
67                _ => None,
68            }
69        }
70    }
71
72    pub fn iter(&self) -> Iter {
73        Iter(Some(self))
74    }
75}
76
77impl<'a> IntoIterator for &'a IfAddrs {
78    type Item = &'a IfAddrsRef;
79    type IntoIter = Iter<'a>;
80
81    fn into_iter(self) -> Iter<'a> {
82        self.iter()
83    }
84}
85
86impl<'a> IntoIterator for &'a IfAddrsRef {
87    type Item = &'a IfAddrsRef;
88    type IntoIter = Iter<'a>;
89
90    fn into_iter(self) -> Iter<'a> {
91        self.iter()
92    }
93}
94
95pub struct Iter<'a>(Option<&'a IfAddrsRef>);
96
97impl<'a> Iterator for Iter<'a> {
98    type Item = &'a IfAddrsRef;
99
100    fn next(&mut self) -> Option<&'a IfAddrsRef> {
101        let cur = match self.0 {
102            Some(cur) => cur,
103            None => return None,
104        };
105
106        self.0 = cur.next();
107        Some(cur)
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use log::info;
114
115    use super::*;
116
117    fn init() {
118        pretty_env_logger::try_init_timed().ok();
119    }
120
121    #[test]
122    fn test_ifaddrs() {
123        init();
124
125        let addrs = IfAddrs::get();
126        assert!(addrs.is_ok());
127
128        addrs
129            .unwrap()
130            .iter()
131            .map(|it| (it.name(), it.addr()))
132            .for_each(|(name, addr)| {
133                info!("{}: {:?}", name, addr);
134            });
135    }
136}