use cfg_if::cfg_if;
use std::ffi;
use std::iter::Iterator;
use std::mem;
use std::option::Option;
use crate::{Result, Errno};
use crate::sys::socket::SockAddr;
use crate::net::if_::*;
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct InterfaceAddress {
pub interface_name: String,
pub flags: InterfaceFlags,
pub address: Option<SockAddr>,
pub netmask: Option<SockAddr>,
pub broadcast: Option<SockAddr>,
pub destination: Option<SockAddr>,
}
cfg_if! {
if #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "fuchsia", target_os = "linux"))] {
fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr {
info.ifa_ifu
}
} else {
fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr {
info.ifa_dstaddr
}
}
}
impl InterfaceAddress {
fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress {
let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) };
let address = unsafe { SockAddr::from_libc_sockaddr(info.ifa_addr) };
let netmask = unsafe { SockAddr::from_libc_sockaddr(info.ifa_netmask) };
let mut addr = InterfaceAddress {
interface_name: ifname.to_string_lossy().to_string(),
flags: InterfaceFlags::from_bits_truncate(info.ifa_flags as i32),
address,
netmask,
broadcast: None,
destination: None,
};
let ifu = get_ifu_from_sockaddr(info);
if addr.flags.contains(InterfaceFlags::IFF_POINTOPOINT) {
addr.destination = unsafe { SockAddr::from_libc_sockaddr(ifu) };
} else if addr.flags.contains(InterfaceFlags::IFF_BROADCAST) {
addr.broadcast = unsafe { SockAddr::from_libc_sockaddr(ifu) };
}
addr
}
}
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct InterfaceAddressIterator {
base: *mut libc::ifaddrs,
next: *mut libc::ifaddrs,
}
impl Drop for InterfaceAddressIterator {
fn drop(&mut self) {
unsafe { libc::freeifaddrs(self.base) };
}
}
impl Iterator for InterfaceAddressIterator {
type Item = InterfaceAddress;
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
match unsafe { self.next.as_ref() } {
Some(ifaddr) => {
self.next = ifaddr.ifa_next;
Some(InterfaceAddress::from_libc_ifaddrs(ifaddr))
}
None => None,
}
}
}
pub fn getifaddrs() -> Result<InterfaceAddressIterator> {
let mut addrs = mem::MaybeUninit::<*mut libc::ifaddrs>::uninit();
unsafe {
Errno::result(libc::getifaddrs(addrs.as_mut_ptr())).map(|_| {
InterfaceAddressIterator {
base: addrs.assume_init(),
next: addrs.assume_init(),
}
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_getifaddrs() {
let _ = getifaddrs();
}
}