netifs 0.3.0

Cross-platform utility library to enumerate local network interfaces.
Documentation
use std::collections::HashMap;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use libc;
use eui48::MacAddress;
use ipnetwork::IpNetwork;

use crate::{Interface, cstr_to_string};

pub fn get_interfaces() -> Result<Vec<Interface>, String> {
    let mut res: HashMap<String, Interface> = HashMap::new();

    unsafe {
        let mut addrptr: *mut libc::ifaddrs = 0 as *mut libc::ifaddrs;
        let ret = libc::getifaddrs(&mut addrptr);
        if ret != 0 {
            return Err(format!("getifaddrs call failed with code: {}", ret));
        }

        let mut current = addrptr;
        while !current.is_null() {
            let addr = *current;
            let name = cstr_to_string(addr.ifa_name);

            let interface = res.entry(name.clone()).or_insert(Interface::new(name.clone()));
            if addr.ifa_flags & libc::IFF_LOOPBACK as u32 != 0 {
                interface.is_loopback = true;
            }
            if addr.ifa_flags & libc::IFF_UP as u32 != 0 {
                interface.is_up = true;
            }

            match (*addr.ifa_addr).sa_family as libc::c_int {
                libc::AF_PACKET => {
                    let sll = *(addr.ifa_addr as *const libc::sockaddr_ll);
                    interface.mac_address = match MacAddress::from_bytes(&sll.sll_addr[..6]) {
                        Ok(m) => Some(m),
                        Err(_) => {eprintln!("Unable to parse mac address on {}", name); None}
                    };
                },
                libc::AF_INET => {
                    let sin = *(addr.ifa_addr as *const libc::sockaddr_in);
                    let sin_mask = *(addr.ifa_netmask as *const libc::sockaddr_in);
                    assert_eq!(sin.sin_family, sin_mask.sin_family);
                    let ip = IpAddr::V4(Ipv4Addr::from(u32::from_be(sin.sin_addr.s_addr)));
                    let mask = IpAddr::V4(Ipv4Addr::from(u32::from_be(sin_mask.sin_addr.s_addr)));

                    match IpNetwork::with_netmask(ip, mask) {
                        Ok(ipn) => interface.ip_addresses.push(ipn),
                        Err(e) => return Err(format!("Unable to construct IpNetwork: {}", e))
                    }
                },
                libc::AF_INET6 => {
                    let sin6 = *(addr.ifa_addr as *const libc::sockaddr_in6);
                    let sin6_mask = *(addr.ifa_netmask as *const libc::sockaddr_in6);
                    assert_eq!(sin6.sin6_family, sin6_mask.sin6_family);
                    let ip = IpAddr::V6(Ipv6Addr::from(sin6.sin6_addr.s6_addr));
                    let mask = IpAddr::V6(Ipv6Addr::from(sin6_mask.sin6_addr.s6_addr));
                    
                    match IpNetwork::with_netmask(ip, mask) {
                        Ok(ipn) => interface.ip_addresses.push(ipn),
                        Err(e) => return Err(format!("Unable to construct IpNetwork: {}", e))
                    }
                },
                x => {
                    eprintln!("Unknown sa_family: {}", x);
                }
            }

            assert_ne!(current, addr.ifa_next);
            current = addr.ifa_next;
        }

        libc::freeifaddrs(addrptr);
    }

    Ok(res.into_iter().map(|(_key, val)| val).collect())
}