use std::
{
ffi::{CStr, CString},
mem::MaybeUninit,
net::{IpAddr, Ipv4Addr, Ipv6Addr}
};
use nix::libc;
use crate::{error::*, internal_error, internal_error_map};
pub struct IfInfo
{
if_inf: *mut libc::ifaddrs,
}
impl Drop for IfInfo
{
fn drop(&mut self)
{
if self.if_inf.is_null() == true
{
return;
}
unsafe
{
libc::freeifaddrs(self.if_inf);
}
}
}
impl IfInfo
{
pub unsafe
fn get_interfaces_info() -> CDnsResult<Self>
{
let mut addrs = MaybeUninit::<*mut libc::ifaddrs>::uninit();
let ifaddrs = unsafe { libc::getifaddrs(addrs.as_mut_ptr()) };
if ifaddrs == -1
{
internal_error!(CDnsErrorType::InternalError, "{}", std::io::Error::last_os_error());
}
return Ok(
Self { if_inf: unsafe { addrs.assume_init() } }
);
}
pub unsafe
fn get_ifr_ip(&self, ifr: &str, ip_addr: &IpAddr) -> CDnsResult<Option<IpAddr>>
{
if self.if_inf.is_null() == true
{
panic!("if_inf is NULL");
}
let c_ifr =
CString::new(ifr)
.map_err(|e|
internal_error_map!(CDnsErrorType::InternalError, "{}", e)
)?;
let mut tmp = self.if_inf;
while tmp.is_null() == false
{
unsafe
{
if (*tmp).ifa_name.is_null() == true
{
tmp = (*tmp).ifa_next;
continue;
}
}
let name = unsafe { CStr::from_ptr((*tmp).ifa_name) };
if name != c_ifr.as_c_str()
{
unsafe { tmp = (*tmp).ifa_next };
continue;
}
let addr = unsafe { (*tmp).ifa_addr };
if addr.is_null() == false
{
let mut host = [0_u8; libc::NI_MAXHOST as usize];
let fam = unsafe { (*addr).sa_family };
if fam as i32 == libc::AF_INET && ip_addr.is_ipv4() == true
{
let s =
unsafe {
libc::getnameinfo(
addr,
std::mem::size_of::<libc::sockaddr_in>() as u32,
host.as_mut_ptr() as *mut i8,
libc::NI_MAXHOST,
std::ptr::null_mut(),
0,
libc::NI_NUMERICHOST
)
};
if s != 0
{
internal_error!(CDnsErrorType::InternalError, "{}", std::io::Error::last_os_error());
}
let c =
unsafe {
CStr::from_ptr(host.as_ptr() as *const i8)
.to_str()
.map_err(|e| internal_error_map!(CDnsErrorType::InternalError, "{}", e))?
};
let ip4: Ipv4Addr = c.parse().map_err(|e| internal_error_map!(CDnsErrorType::InternalError, "{}", e))?;
return Ok(Some(IpAddr::from(ip4)));
}
else if fam as i32 == libc::AF_INET6 && ip_addr.is_ipv6() == true
{
let s =
unsafe
{
libc::getnameinfo(
addr,
std::mem::size_of::<libc::sockaddr_in6>() as u32,
host.as_mut_ptr() as *mut i8,
libc::NI_MAXHOST,
std::ptr::null_mut(),
0,
libc::NI_NUMERICHOST
)
};
if s != 0
{
internal_error!(CDnsErrorType::InternalError, "{}", std::io::Error::last_os_error());
}
let c =
unsafe
{
CStr::from_ptr(host.as_ptr() as *const i8)
.to_str()
.map_err(|e| internal_error_map!(CDnsErrorType::InternalError, "{}", e))?
};
let ip6: Ipv6Addr =
c
.split_once("%")
.map_or(c, |(ip, _)| ip)
.parse()
.map_err(|e|
internal_error_map!(CDnsErrorType::InternalError, "{}", e)
)?;
return Ok(Some(IpAddr::from(Ipv6Addr::from(ip6))));
}
}
tmp = unsafe { (*tmp).ifa_next };
}
return Ok(None);
}
}