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 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}