#![doc(html_logo_url =
"https://raw.githubusercontent.com/maidsafe/QA/master/Images/maidsafe_logo.png",
html_favicon_url = "http://maidsafe.net/img/favicon.ico",
html_root_url = "http://maidsafe.github.io/get_if_addrs/")]
#![forbid(bad_style, exceeding_bitshifts, mutable_transmutes, no_mangle_const_items,
unknown_crate_types, warnings)]
#![deny(deprecated, drop_with_repr_extern, improper_ctypes, missing_docs,
non_shorthand_field_patterns, overflowing_literals, plugin_as_library,
private_no_mangle_fns, private_no_mangle_statics, stable_features,
unconditional_recursion, unknown_lints, unsafe_code, unused, unused_allocation,
unused_attributes, unused_comparisons, unused_features, unused_parens, while_true)]
#![warn(trivial_casts, trivial_numeric_casts, unused_extern_crates, unused_import_braces,
unused_qualifications, unused_results, variant_size_differences)]
#![allow(box_pointers, fat_ptr_transmutes, missing_copy_implementations,
missing_debug_implementations)]
extern crate ip;
extern crate libc;
use std::net::Ipv4Addr;
use ip::IpAddr;
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct IfAddr {
pub name: String,
pub addr: IpAddr,
pub netmask: IpAddr,
pub broadcast: IpAddr,
}
impl IfAddr {
pub fn new() -> IfAddr {
IfAddr {
name: String::new(),
addr: IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
netmask: IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
broadcast: IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
}
}
}
#[cfg(not(windows))]
mod getifaddrs_posix {
use super::IfAddr;
use std::net::{Ipv4Addr, Ipv6Addr};
use ip::IpAddr;
use std::{mem, str};
use std::ffi::CStr;
use libc::consts::os::bsd44::{AF_INET, AF_INET6};
use libc::funcs::bsd43::getifaddrs as posix_getifaddrs;
use libc::funcs::bsd43::freeifaddrs as posix_freeifaddrs;
use libc::types::os::common::bsd44::ifaddrs as posix_ifaddrs;
use libc::types::os::common::bsd44::sockaddr as posix_sockaddr;
use libc::types::os::common::bsd44::sockaddr_in as posix_sockaddr_in;
use libc::types::os::common::bsd44::sockaddr_in6 as posix_sockaddr_in6;
#[allow(unsafe_code)]
fn sockaddr_to_ipaddr(sockaddr: *const posix_sockaddr) -> Option<IpAddr> {
if sockaddr.is_null() {
return None;
}
if unsafe { *sockaddr }.sa_family as u32 == AF_INET as u32 {
let sa = &unsafe { *(sockaddr as *const posix_sockaddr_in) };
Some(IpAddr::V4(Ipv4Addr::new(((sa.sin_addr.s_addr) & 255) as u8,
((sa.sin_addr.s_addr >> 8) & 255) as u8,
((sa.sin_addr.s_addr >> 16) & 255) as u8,
((sa.sin_addr.s_addr >> 24) & 255) as u8)))
} else if unsafe { *sockaddr }.sa_family as u32 == AF_INET6 as u32 {
let sa = &unsafe { *(sockaddr as *const posix_sockaddr_in6) };
if sa.sin6_addr.s6_addr[0] == 0x80fe {
return None;
}
Some(IpAddr::V6(Ipv6Addr::new(((sa.sin6_addr.s6_addr[0] & 255) << 8) |
((sa.sin6_addr.s6_addr[0] >> 8) & 255),
((sa.sin6_addr.s6_addr[1] & 255) << 8) |
((sa.sin6_addr.s6_addr[1] >> 8) & 255),
((sa.sin6_addr.s6_addr[2] & 255) << 8) |
((sa.sin6_addr.s6_addr[2] >> 8) & 255),
((sa.sin6_addr.s6_addr[3] & 255) << 8) |
((sa.sin6_addr.s6_addr[3] >> 8) & 255),
((sa.sin6_addr.s6_addr[4] & 255) << 8) |
((sa.sin6_addr.s6_addr[4] >> 8) & 255),
((sa.sin6_addr.s6_addr[5] & 255) << 8) |
((sa.sin6_addr.s6_addr[5] >> 8) & 255),
((sa.sin6_addr.s6_addr[6] & 255) << 8) |
((sa.sin6_addr.s6_addr[6] >> 8) & 255),
((sa.sin6_addr.s6_addr[7] & 255) << 8) |
((sa.sin6_addr.s6_addr[7] >> 8) & 255))))
} else {
None
}
}
#[cfg(any(target_os = "linux", target_os = "android", target_os = "nacl"))]
fn do_broadcast(ifaddr: &posix_ifaddrs) -> IpAddr {
sockaddr_to_ipaddr(ifaddr.ifa_ifu).unwrap_or(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)))
}
#[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "ios"))]
fn do_broadcast(ifaddr: &posix_ifaddrs) -> IpAddr {
sockaddr_to_ipaddr(ifaddr.ifa_dstaddr).unwrap_or(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)))
}
#[allow(unsafe_code)]
pub fn getifaddrs() -> Vec<IfAddr> {
let mut ret = Vec::<IfAddr>::new();
let mut ifaddrs: *mut posix_ifaddrs;
unsafe {
ifaddrs = mem::uninitialized();
if -1 == posix_getifaddrs(&mut ifaddrs) {
panic!("failed to retrieve interface details from getifaddrs()");
}
}
let mut ifaddr = ifaddrs;
let mut first = true;
while !ifaddr.is_null() {
if first {
first = false;
} else {
ifaddr = unsafe { (*ifaddr).ifa_next };
}
if ifaddr.is_null() {
break;
}
let ifaddr = &unsafe { *ifaddr };
if ifaddr.ifa_addr.is_null() {
continue;
}
let mut item = IfAddr::new();
let name = unsafe { CStr::from_ptr(ifaddr.ifa_name) }.to_bytes();
item.name = item.name + str::from_utf8(name).unwrap();
match sockaddr_to_ipaddr(ifaddr.ifa_addr) {
Some(a) => item.addr = a,
None => continue,
};
if let Some(a) = sockaddr_to_ipaddr(ifaddr.ifa_netmask) {
item.netmask = a
};
if (ifaddr.ifa_flags & 2) != 0 {
item.broadcast = do_broadcast(ifaddr);
}
ret.push(item);
}
unsafe {
posix_freeifaddrs(ifaddrs);
}
ret
}
}
#[cfg(not(windows))]
pub fn getifaddrs() -> Vec<IfAddr> {
getifaddrs_posix::getifaddrs()
}
#[cfg(windows)]
mod getifaddrs_windows {
use super::IfAddr;
use std::net::{Ipv4Addr, Ipv6Addr};
use ip::IpAddr;
use std::{str, ptr};
use std::ffi::CStr;
use libc::types::common::c95::c_void;
use libc::types::os::arch::c95::{c_char, c_ulong, size_t, c_int};
use libc::types::os::arch::extra::*; use libc::consts::os::extra::*; use libc::consts::os::bsd44::*; use libc::types::os::common::bsd44::*; use libc;
#[repr(C)]
struct SocketAddress {
pub lp_socket_address: *const sockaddr,
pub i_socket_address_length: c_int,
}
#[repr(C)]
struct IpAdapterUnicastAddress {
pub length: c_ulong,
pub flags: DWORD,
pub next: *const IpAdapterUnicastAddress,
pub address: SocketAddress,
}
#[repr(C)]
struct IpAdapterPrefix {
pub length: c_ulong,
pub flags: DWORD,
pub next: *const IpAdapterPrefix,
pub address: SocketAddress,
pub prefix_length: c_ulong,
}
#[repr(C)]
struct IpAdapterAddresses {
pub length: c_ulong,
pub if_index: DWORD,
pub next: *const IpAdapterAddresses,
pub adapter_name: *const c_char,
pub first_unicast_address: *const IpAdapterUnicastAddress,
first_anycast_address: *const c_void,
first_multicast_address: *const c_void,
first_dns_server_address: *const c_void,
dns_suffix: *const c_void,
description: *const c_void,
friendly_name: *const c_void,
physical_address: [c_char; 8],
physical_address_length: DWORD,
flags: DWORD,
mtu: DWORD,
if_type: DWORD,
oper_status: c_int,
ipv6_if_index: DWORD,
zone_indices: [DWORD; 16],
pub first_prefix: *const IpAdapterPrefix,
}
#[link(name="Iphlpapi")]
extern "system" {
fn GetAdaptersAddresses(family: c_ulong,
flags: c_ulong,
reserved: *const c_void,
addresses: *const IpAdapterAddresses,
size: *mut c_ulong)
-> c_ulong;
}
#[allow(unsafe_code)]
fn sockaddr_to_ipaddr(sockaddr: *const sockaddr) -> Option<IpAddr> {
if sockaddr.is_null() {
return None;
}
if unsafe { *sockaddr }.sa_family as u32 == AF_INET as u32 {
let ref sa = unsafe { *(sockaddr as *const sockaddr_in) };
if sa.sin_addr.s_addr & 65535 == 0xfea9 {
return None;
}
Some(IpAddr::V4(Ipv4Addr::new(((sa.sin_addr.s_addr >> 0) & 255) as u8,
((sa.sin_addr.s_addr >> 8) & 255) as u8,
((sa.sin_addr.s_addr >> 16) & 255) as u8,
((sa.sin_addr.s_addr >> 24) & 255) as u8)))
} else if unsafe { *sockaddr }.sa_family as u32 == AF_INET6 as u32 {
let ref sa = unsafe { *(sockaddr as *const sockaddr_in6) };
if sa.sin6_addr.s6_addr[0] == 0x80fe {
return None;
}
Some(IpAddr::V6(Ipv6Addr::new(((sa.sin6_addr.s6_addr[0] & 255) << 8) |
((sa.sin6_addr.s6_addr[0] >> 8) & 255),
((sa.sin6_addr.s6_addr[1] & 255) << 8) |
((sa.sin6_addr.s6_addr[1] >> 8) & 255),
((sa.sin6_addr.s6_addr[2] & 255) << 8) |
((sa.sin6_addr.s6_addr[2] >> 8) & 255),
((sa.sin6_addr.s6_addr[3] & 255) << 8) |
((sa.sin6_addr.s6_addr[3] >> 8) & 255),
((sa.sin6_addr.s6_addr[4] & 255) << 8) |
((sa.sin6_addr.s6_addr[4] >> 8) & 255),
((sa.sin6_addr.s6_addr[5] & 255) << 8) |
((sa.sin6_addr.s6_addr[5] >> 8) & 255),
((sa.sin6_addr.s6_addr[6] & 255) << 8) |
((sa.sin6_addr.s6_addr[6] >> 8) & 255),
((sa.sin6_addr.s6_addr[7] & 255) << 8) |
((sa.sin6_addr.s6_addr[7] >> 8) & 255))))
} else {
None
}
}
#[allow(unsafe_code, trivial_numeric_casts)]
pub fn getifaddrs() -> Vec<IfAddr> {
let mut ret = Vec::<IfAddr>::new();
let mut ifaddrs: *const IpAdapterAddresses;
let mut buffersize: c_ulong = 15000;
loop {
unsafe {
ifaddrs = libc::malloc(buffersize as size_t) as *mut IpAdapterAddresses;
if ifaddrs.is_null() {
panic!("Failed to allocate buffer in getifaddrs()");
}
let retcode =
GetAdaptersAddresses(0,
0x3e,
ptr::null(),
ifaddrs,
&mut buffersize) as c_int;
match retcode {
ERROR_SUCCESS => break,
111 => {
libc::free(ifaddrs as *mut c_void);
buffersize = buffersize * 2;
continue;
}
_ => panic!("GetAdaptersAddresses() failed with error code {}", retcode),
}
}
}
let mut ifaddr = ifaddrs;
let mut first = true;
while !ifaddr.is_null() {
if first {
first = false;
} else {
ifaddr = unsafe { (*ifaddr).next };
}
if ifaddr.is_null() {
break;
}
let ref ifaddr = unsafe { &*ifaddr };
let mut addr = ifaddr.first_unicast_address;
if addr.is_null() {
continue;
}
let mut firstaddr = true;
while !addr.is_null() {
if firstaddr {
firstaddr = false;
} else {
addr = unsafe { (*addr).next };
}
if addr.is_null() {
break;
}
let mut item = IfAddr::new();
let name = unsafe { CStr::from_ptr(ifaddr.adapter_name) }.to_bytes();
item.name = item.name + str::from_utf8(name).unwrap();
let ipaddr = sockaddr_to_ipaddr(unsafe { (*addr).address.lp_socket_address });
if !ipaddr.is_some() {
continue;
}
item.addr = ipaddr.unwrap();
let mut prefix = ifaddr.first_prefix;
if !prefix.is_null() {
let mut first_prefix = true;
'prefixloop: while !prefix.is_null() {
if first_prefix {
first_prefix = false;
} else {
prefix = unsafe { (*prefix).next };
}
if prefix.is_null() {
break;
}
let ipprefix = sockaddr_to_ipaddr(unsafe {
(*prefix).address.lp_socket_address
});
if !ipprefix.is_some() {
continue;
}
match ipprefix.unwrap() {
IpAddr::V4(ref a) => {
if let IpAddr::V4(b) = item.addr {
let mut netmask: [u8; 4] = [0; 4];
for n in 0..unsafe { (*prefix).prefix_length as usize + 7 } /
8 {
let x_byte = b.octets()[n];
let y_byte = a.octets()[n];
for m in 0..8 {
if (n * 8) + m >
unsafe { (*prefix).prefix_length as usize } {
break;
}
let bit = 1 << m;
if (x_byte & bit) == (y_byte & bit) {
netmask[n] = netmask[n] | bit;
} else {
continue 'prefixloop;
}
}
}
item.netmask = IpAddr::V4(Ipv4Addr::new(netmask[0],
netmask[1],
netmask[2],
netmask[3]));
let mut broadcast: [u8; 4] = b.octets();
for n in 0..4 {
broadcast[n] = broadcast[n] | !netmask[n];
}
item.broadcast = IpAddr::V4(Ipv4Addr::new(broadcast[0],
broadcast[1],
broadcast[2],
broadcast[3]));
break 'prefixloop;
}
}
IpAddr::V6(ref a) => {
if let IpAddr::V6(b) = item.addr {
let mut netmask: [u16; 8] = [0; 8];
for n in 0..unsafe { (*prefix).prefix_length as usize + 15 } /
16 {
let x_word = b.segments()[n];
let y_word = a.segments()[n];
for m in 0..16 {
if (n * 16) + m >
unsafe { (*prefix).prefix_length as usize } {
break;
}
let bit = 1 << m;
if (x_word & bit) == (y_word & bit) {
netmask[n] = netmask[n] | bit;
} else {
continue 'prefixloop;
}
}
}
item.netmask = IpAddr::V6(Ipv6Addr::new(netmask[0],
netmask[1],
netmask[2],
netmask[3],
netmask[4],
netmask[5],
netmask[6],
netmask[7]));
break 'prefixloop;
}
}
};
}
}
ret.push(item);
}
}
unsafe {
libc::free(ifaddrs as *mut c_void);
}
ret
}
}
#[cfg(windows)]
pub fn getifaddrs() -> Vec<IfAddr> {
getifaddrs_windows::getifaddrs()
}
fn is_loopback(ip_addr: &IpAddr) -> bool {
match *ip_addr {
IpAddr::V4(a) => a.octets()[0] == 127,
IpAddr::V6(a) => a.segments() == [0, 0, 0, 0, 0, 0, 0, 1],
}
}
pub fn filter_loopback(mut ifaddrs: Vec<IfAddr>) -> Vec<IfAddr> {
ifaddrs.retain(|x| !is_loopback(&x.addr));
ifaddrs
}
#[cfg(test)]
mod test {
use super::{getifaddrs, filter_loopback, is_loopback};
#[test]
fn test_filter_loopback() {
let ifaddrs = filter_loopback(getifaddrs());
for ifaddr in ifaddrs {
assert!(!is_loopback(&ifaddr.addr));
}
}
}