use std::ffi::CStr;
use std::net::IpAddr;
use std::{net, ptr};
use ::std::io::{Error, ErrorKind};
use crate::ifaces::{Interface, Kind, NextHop};
pub const AF_INET: i32 = nix::sys::socket::AddressFamily::Inet as i32;
pub const AF_INET6: i32 = nix::sys::socket::AddressFamily::Inet6 as i32;
#[cfg(any(
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd"
))]
pub const AF_LINK: i32 = nix::libc::AF_LINK;
#[cfg(any(
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd"
))]
pub const AF_PACKET: i32 = -1;
#[cfg(any(target_os = "linux", target_os = "android"))]
pub const AF_LINK: i32 = -1;
#[cfg(any(target_os = "linux", target_os = "android"))]
pub const AF_PACKET: i32 = nix::libc::AF_PACKET;
#[allow(dead_code, non_camel_case_types)]
#[repr(C)]
pub enum SiocgifFlags {
Up = 0x1, Broadcast = 0x2, Debug = 0x4, Loopback = 0x8, Pointopoint = 0x10, Notrailers = 0x20, Running = 0x40, Noarp = 0x80, Promisc = 0x100, Allmulti = 0x200, Master = 0x400, Slave = 0x800, Multicast = 0x1000, Portsel = 0x2000, Automedia = 0x4000, Dynamic = 0x8000, }
#[repr(C)]
pub struct union_ifa_ifu {
pub data: *mut ::std::os::raw::c_void,
}
impl union_ifa_ifu {
pub fn ifu_broadaddr(&mut self) -> *mut nix::sys::socket::sockaddr {
self.data as *mut nix::sys::socket::sockaddr
}
pub fn ifu_dstaddr(&mut self) -> *mut nix::sys::socket::sockaddr {
self.data as *mut nix::sys::socket::sockaddr
}
}
#[repr(C)]
pub struct ifaddrs {
pub ifa_next: *mut ifaddrs,
pub ifa_name: *mut ::std::os::raw::c_char,
pub ifa_flags: ::std::os::raw::c_uint,
pub ifa_addr: *mut nix::sys::socket::sockaddr,
pub ifa_netmask: *mut nix::sys::socket::sockaddr,
pub ifa_ifu: union_ifa_ifu,
pub ifa_data: *mut ::std::os::raw::c_void,
}
extern "C" {
pub fn getifaddrs(ifap: *mut *mut ifaddrs) -> ::std::os::raw::c_int;
pub fn freeifaddrs(ifa: *mut ifaddrs) -> ::std::os::raw::c_void;
#[allow(dead_code)]
pub fn if_nametoindex(ifname: *const ::std::os::raw::c_char) -> ::std::os::raw::c_uint;
}
pub fn nix_socketaddr_to_sockaddr(sa: *mut nix::sys::socket::sockaddr) -> Option<net::SocketAddr> {
if sa.is_null() {
return None;
}
let (addr, port) = match unsafe { *sa }.sa_family as i32 {
AF_INET => {
let sa: *const nix::sys::socket::sockaddr_in = sa as *const nix::libc::sockaddr_in;
let sa = &unsafe { *sa };
let (addr, port) = (sa.sin_addr.s_addr, sa.sin_port);
(IpAddr::V4(Into::into(u32::from_be(addr))), port)
}
AF_INET6 => {
let sa: *const nix::sys::socket::sockaddr_in6 = sa as *const nix::libc::sockaddr_in6;
let sa = &unsafe { *sa };
let (addr, port) = (sa.sin6_addr.s6_addr, sa.sin6_port);
(Into::into(addr), port)
}
_ => return None,
};
Some(net::SocketAddr::new(addr, port))
}
pub fn ifaces() -> Result<Vec<Interface>, Error> {
let mut ifaddrs_ptr: *mut ifaddrs = ptr::null_mut();
match unsafe { getifaddrs(&mut ifaddrs_ptr as *mut _) } {
0 => {
let mut ret = Vec::new();
let mut item: *mut ifaddrs = ifaddrs_ptr;
loop {
if item.is_null() {
break;
}
let name = String::from_utf8(
unsafe { CStr::from_ptr((*item).ifa_name) }
.to_bytes()
.to_vec(),
);
unsafe {
if name.is_err() || (*item).ifa_addr.is_null() {
item = (*item).ifa_next;
continue;
}
}
let kind = match unsafe { (*(*item).ifa_addr).sa_family as i32 } {
AF_INET => Some(Kind::Ipv4),
AF_INET6 => Some(Kind::Ipv6),
AF_PACKET => Some(Kind::Packet),
AF_LINK => Some(Kind::Link),
code => Some(Kind::Unknow(code)),
};
if kind.is_none() {
item = unsafe { (*item).ifa_next };
continue;
}
let addr = nix_socketaddr_to_sockaddr(unsafe { (*item).ifa_addr });
let mask = nix_socketaddr_to_sockaddr(unsafe { (*item).ifa_netmask });
let hop = unsafe {
if (*item).ifa_flags & SiocgifFlags::Broadcast as ::std::os::raw::c_uint
== SiocgifFlags::Broadcast as ::std::os::raw::c_uint
{
nix_socketaddr_to_sockaddr((*item).ifa_ifu.ifu_broadaddr())
.map(NextHop::Broadcast)
} else {
nix_socketaddr_to_sockaddr((*item).ifa_ifu.ifu_dstaddr())
.map(NextHop::Destination)
}
};
if let Some(kind) = kind {
match kind {
Kind::Unknow(_) => {}
_ => {
ret.push(Interface {
name: name.unwrap(),
kind,
addr,
mask,
hop,
});
}
};
};
item = unsafe { (*item).ifa_next };
}
unsafe { freeifaddrs(ifaddrs_ptr) };
Ok(ret)
}
_ => Err(Error::new(ErrorKind::Other, "Oh, no ...")), }
}