diff --git a/Cargo.toml b/Cargo.toml index 9a3ff28..ff1dcda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,10 @@ license = "MIT" [dependencies] libc = "0.2" +[target.'cfg(target_os = "android")'.dependencies] +dlopen = "0.1.8" +once_cell = "1.17.1" + [target.'cfg(windows)'.dependencies] memalloc = "0.1.0" diff --git a/src/interface/android.rs b/src/interface/android.rs new file mode 100644 index 0000000..b4d6770 --- /dev/null +++ b/src/interface/android.rs @@ -0,0 +1,64 @@ +use once_cell::sync::OnceCell; + +fn load_symbol(sym: &'static str) -> Option { + const LIB_NAME: &str = "libc.so"; + + println!("loading symbol: {} from {}", sym, LIB_NAME); + match dlopen::raw::Library::open(LIB_NAME) { + Ok(lib) => match unsafe { lib.symbol::(sym) } { + Ok(val) => Some(val), + Err(err) => { + eprintln!("failed to load symbol {} from {}: {:?}", sym, LIB_NAME, err); + None + } + }, + Err(err) => { + eprintln!("failed to load {}: {:?}", LIB_NAME, err); + None + } + } +} + +fn get_getifaddrs() -> Option libc::c_int> { + static INSTANCE: OnceCell< + Option libc::c_int>, + > = OnceCell::new(); + + *INSTANCE.get_or_init(|| load_symbol("getifaddrs")) +} + +fn get_freeifaddrs() -> Option { + static INSTANCE: OnceCell> = OnceCell::new(); + + *INSTANCE.get_or_init(|| load_symbol("freeifaddrs")) +} + +pub unsafe fn getifaddrs(ifap: *mut *mut libc::ifaddrs) -> libc::c_int { + // Android is complicated + + // API 24+ contains the getifaddrs and freeifaddrs functions but the NDK doesn't + // expose those functions in ifaddrs.h when the minimum supported SDK is lower than 24 + // and therefore we need to load them manually. + if let Some(dyn_getifaddrs) = get_getifaddrs() { + return dyn_getifaddrs(ifap); + } + + // If API < 24 (or we can't load libc for some other reason), we fallback to using netlink + netlink_getifaddrs(ifap) +} + +pub unsafe fn freeifaddrs(ifa: *mut libc::ifaddrs) { + if let Some(dyn_freeifaddrs) = get_freeifaddrs() { + return dyn_freeifaddrs(ifa); + } + + netlink_freeifaddrs(ifa) +} + +unsafe fn netlink_getifaddrs(ifap: *mut *mut libc::ifaddrs) -> libc::c_int { + todo!() +} + +unsafe fn netlink_freeifaddrs(ifa: *mut libc::ifaddrs) { + todo!() +} diff --git a/src/interface/linux.rs b/src/interface/linux.rs index b00b8c4..f07688b 100644 --- a/src/interface/linux.rs +++ b/src/interface/linux.rs @@ -1,6 +1,6 @@ +use crate::interface::InterfaceType; use std::convert::TryFrom; use std::fs::read_to_string; -use crate::interface::InterfaceType; pub fn get_interface_type(if_name: String) -> InterfaceType { let if_type_path: String = format!("/sys/class/net/{}/type", if_name); @@ -11,16 +11,16 @@ pub fn get_interface_type(if_name: String) -> InterfaceType { match if_type_string.parse::() { Ok(if_type) => { return InterfaceType::try_from(if_type).unwrap_or(InterfaceType::Unknown); - }, + } Err(_) => { return InterfaceType::Unknown; } } - }, + } Err(_) => { return InterfaceType::Unknown; } - }; + }; } pub fn get_interface_speed(if_name: String) -> Option { @@ -33,14 +33,14 @@ pub fn get_interface_speed(if_name: String) -> Option { Ok(if_speed) => { // Convert Mbps to bps return Some(if_speed * 1000000); - }, + } Err(_) => { return None; } } - }, + } Err(_) => { return None; } - }; + }; } diff --git a/src/interface/mod.rs b/src/interface/mod.rs index e9e89df..67e4f49 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -4,9 +4,25 @@ pub use self::shared::*; mod types; pub use self::types::*; -#[cfg(any(target_os = "linux", target_os = "macos", target_os = "openbsd", target_os = "freebsd", target_os = "netbsd", target_os = "ios", target_os = "android"))] +#[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "openbsd", + target_os = "freebsd", + target_os = "netbsd", + target_os = "ios", + target_os = "android" +))] mod unix; -#[cfg(any(target_os = "linux", target_os = "macos", target_os = "openbsd", target_os = "freebsd", target_os = "netbsd", target_os = "ios", target_os = "android"))] +#[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "openbsd", + target_os = "freebsd", + target_os = "netbsd", + target_os = "ios", + target_os = "android" +))] use self::unix::*; #[cfg(target_os = "windows")] @@ -17,12 +33,15 @@ use self::windows::*; #[cfg(any(target_os = "linux", target_os = "android"))] mod linux; +#[cfg(target_os = "android")] +mod android; + #[cfg(any(target_os = "macos", target_os = "ios"))] mod macos; -use std::net::IpAddr; +use crate::gateway::Gateway; use crate::ip::{Ipv4Net, Ipv6Net}; -use crate::gateway::{Gateway}; +use std::net::IpAddr; /// Structure of MAC address #[derive(Clone, Debug)] @@ -31,24 +50,29 @@ pub struct MacAddr(u8, u8, u8, u8, u8, u8); impl MacAddr { /// Construct a new MacAddr instance from the given octets pub fn new(octets: [u8; 6]) -> MacAddr { - MacAddr(octets[0], octets[1], octets[2], octets[3], octets[4], octets[5]) + MacAddr( + octets[0], octets[1], octets[2], octets[3], octets[4], octets[5], + ) } /// Returns an array of MAC address octets pub fn octets(&self) -> [u8; 6] { - [self.0,self.1,self.2,self.3,self.4,self.5] + [self.0, self.1, self.2, self.3, self.4, self.5] } /// Return a formatted string of MAC address pub fn address(&self) -> String { - format!("{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", self.0,self.1,self.2,self.3,self.4,self.5) + format!( + "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + self.0, self.1, self.2, self.3, self.4, self.5 + ) } /// Construct an all-zero MacAddr instance pub fn zero() -> MacAddr { - MacAddr(0,0,0,0,0,0) + MacAddr(0, 0, 0, 0, 0, 0) } /// Construct a new MacAddr instance from a colon-separated string of hex format pub fn from_hex_format(hex_mac_addr: &str) -> MacAddr { if hex_mac_addr.len() != 17 { - return MacAddr(0,0,0,0,0,0) + return MacAddr(0, 0, 0, 0, 0, 0); } let fields: Vec<&str> = hex_mac_addr.split(":").collect(); let o1: u8 = u8::from_str_radix(&fields[0], 0x10).unwrap_or(0); @@ -63,8 +87,12 @@ impl MacAddr { impl std::fmt::Display for MacAddr { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - let _ = write!(f,"{:<02x}:{:<02x}:{:<02x}:{:<02x}:{:<02x}:{:<02x}",self.0,self.1,self.2,self.3,self.4,self.5); - Ok(()) + let _ = write!( + f, + "{:<02x}:{:<02x}:{:<02x}:{:<02x}:{:<02x}:{:<02x}", + self.0, self.1, self.2, self.3, self.4, self.5 + ); + Ok(()) } } @@ -76,7 +104,7 @@ pub struct Interface { /// Name of network interface pub name: String, /// Friendly Name of network interface - pub friendly_name : Option, + pub friendly_name: Option, /// Description of the network interface pub description: Option, /// Interface Type @@ -99,7 +127,7 @@ pub struct Interface { /// Get default Network Interface pub fn get_default_interface() -> Result { - let local_ip: IpAddr = match get_local_ipaddr(){ + let local_ip: IpAddr = match get_local_ipaddr() { Some(local_ip) => local_ip, None => return Err(String::from("Local IP address not found")), }; @@ -110,12 +138,12 @@ pub fn get_default_interface() -> Result { if iface.ipv4.iter().any(|x| x.addr == local_ipv4) { return Ok(iface); } - }, + } IpAddr::V6(local_ipv6) => { if iface.ipv6.iter().any(|x| x.addr == local_ipv6) { return Ok(iface); } - }, + } } } Err(String::from("Default Interface not found")) @@ -123,7 +151,7 @@ pub fn get_default_interface() -> Result { /// Get default Network Interface index pub fn get_default_interface_index() -> Option { - let local_ip: IpAddr = match get_local_ipaddr(){ + let local_ip: IpAddr = match get_local_ipaddr() { Some(local_ip) => local_ip, None => return None, }; @@ -134,12 +162,12 @@ pub fn get_default_interface_index() -> Option { if iface.ipv4.iter().any(|x| x.addr == local_ipv4) { return Some(iface.index); } - }, + } IpAddr::V6(local_ipv6) => { if iface.ipv6.iter().any(|x| x.addr == local_ipv6) { return Some(iface.index); } - }, + } } } None @@ -147,7 +175,7 @@ pub fn get_default_interface_index() -> Option { /// Get default Network Interface name pub fn get_default_interface_name() -> Option { - let local_ip: IpAddr = match get_local_ipaddr(){ + let local_ip: IpAddr = match get_local_ipaddr() { Some(local_ip) => local_ip, None => return None, }; @@ -158,12 +186,12 @@ pub fn get_default_interface_name() -> Option { if iface.ipv4.iter().any(|x| x.addr == local_ipv4) { return Some(iface.name); } - }, + } IpAddr::V6(local_ipv6) => { if iface.ipv6.iter().any(|x| x.addr == local_ipv6) { return Some(iface.name); } - }, + } } } None diff --git a/src/interface/unix.rs b/src/interface/unix.rs index ec5fb4c..e198b6d 100644 --- a/src/interface/unix.rs +++ b/src/interface/unix.rs @@ -1,21 +1,24 @@ use super::Interface; use super::MacAddr; -use crate::sys; use crate::gateway; use crate::ip::{Ipv4Net, Ipv6Net}; +use crate::sys; +use crate::interface::InterfaceType; use libc; use std::ffi::{CStr, CString}; use std::mem::{self, MaybeUninit}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::os::raw::c_char; use std::str::from_utf8_unchecked; -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use crate::interface::InterfaceType; + +#[cfg(target_os = "android")] +use super::android::{freeifaddrs, getifaddrs}; #[cfg(any(target_os = "openbsd", target_os = "freebsd", target_os = "netbsd"))] pub fn interfaces() -> Vec { let mut interfaces: Vec = unix_interfaces(); - let local_ip: IpAddr = match super::get_local_ipaddr(){ + let local_ip: IpAddr = match super::get_local_ipaddr() { Some(local_ip) => local_ip, None => return interfaces, }; @@ -26,21 +29,21 @@ pub fn interfaces() -> Vec { match gateway::unix::get_default_gateway(iface.name.clone()) { Ok(gateway) => { iface.gateway = Some(gateway); - }, - Err(_) => {}, + } + Err(_) => {} } } - }, + } IpAddr::V6(local_ipv6) => { if iface.ipv6.iter().any(|x| x.addr == local_ipv6) { match gateway::unix::get_default_gateway(iface.name.clone()) { Ok(gateway) => { iface.gateway = Some(gateway); - }, - Err(_) => {}, + } + Err(_) => {} } } - }, + } } } interfaces @@ -52,7 +55,7 @@ pub fn interfaces() -> Vec { let type_map = macos::get_if_type_map(); let mut interfaces: Vec = unix_interfaces(); - let local_ip: IpAddr = match super::get_local_ipaddr(){ + let local_ip: IpAddr = match super::get_local_ipaddr() { Some(local_ip) => local_ip, None => return interfaces, }; @@ -64,21 +67,21 @@ pub fn interfaces() -> Vec { match gateway::unix::get_default_gateway(iface.name.clone()) { Ok(gateway) => { iface.gateway = Some(gateway); - }, - Err(_) => {}, + } + Err(_) => {} } } - }, + } IpAddr::V6(local_ipv6) => { if iface.ipv6.iter().any(|x| x.addr == local_ipv6) { match gateway::unix::get_default_gateway(iface.name.clone()) { Ok(gateway) => { iface.gateway = Some(gateway); - }, - Err(_) => {}, + } + Err(_) => {} } } - }, + } } } interfaces @@ -89,7 +92,7 @@ pub fn interfaces() -> Vec { use super::linux; let mut interfaces: Vec = unix_interfaces(); - let local_ip: IpAddr = match super::get_local_ipaddr(){ + let local_ip: IpAddr = match super::get_local_ipaddr() { Some(local_ip) => local_ip, None => return interfaces, }; @@ -104,21 +107,21 @@ pub fn interfaces() -> Vec { match gateway::linux::get_default_gateway(iface.name.clone()) { Ok(gateway) => { iface.gateway = Some(gateway); - }, - Err(_) => {}, + } + Err(_) => {} } } - }, + } IpAddr::V6(local_ipv6) => { if iface.ipv6.iter().any(|x| x.addr == local_ipv6) { match gateway::linux::get_default_gateway(iface.name.clone()) { Ok(gateway) => { iface.gateway = Some(gateway); - }, - Err(_) => {}, + } + Err(_) => {} } } - }, + } } } interfaces @@ -144,10 +147,8 @@ fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option, Opti (Some(mac), None) } else { - let addr = sys::sockaddr_to_addr( - mem::transmute(sa), - mem::size_of::(), - ); + let addr = + sys::sockaddr_to_addr(mem::transmute(sa), mem::size_of::()); match addr { Ok(SocketAddr::V4(sa)) => (None, Some(IpAddr::V4(*sa.ip()))), @@ -158,7 +159,13 @@ fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option, Opti } } -#[cfg(any(target_os = "openbsd", target_os = "freebsd", target_os = "netbsd", target_os = "macos", target_os = "ios"))] +#[cfg(any( + target_os = "openbsd", + target_os = "freebsd", + target_os = "netbsd", + target_os = "macos", + target_os = "ios" +))] fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option, Option) { use crate::bpf; use std::net::SocketAddr; @@ -180,10 +187,8 @@ fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option, Opti (Some(mac), None) } else { - let addr = sys::sockaddr_to_addr( - mem::transmute(sa), - mem::size_of::(), - ); + let addr = + sys::sockaddr_to_addr(mem::transmute(sa), mem::size_of::()); match addr { Ok(SocketAddr::V4(sa)) => (None, Some(IpAddr::V4(*sa.ip()))), @@ -197,16 +202,16 @@ fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option, Opti pub fn unix_interfaces() -> Vec { let mut ifaces: Vec = vec![]; let mut addrs: MaybeUninit<*mut libc::ifaddrs> = MaybeUninit::uninit(); - if unsafe { libc::getifaddrs(addrs.as_mut_ptr()) } != 0 { + if unsafe { getifaddrs(addrs.as_mut_ptr()) } != 0 { return ifaces; } let addrs = unsafe { addrs.assume_init() }; let mut addr = addrs; while !addr.is_null() { - let addr_ref: &libc::ifaddrs = unsafe {&*addr}; + let addr_ref: &libc::ifaddrs = unsafe { &*addr }; let c_str = addr_ref.ifa_name as *const c_char; let bytes = unsafe { CStr::from_ptr(c_str).to_bytes() }; - let name = unsafe {from_utf8_unchecked(bytes).to_owned() }; + let name = unsafe { from_utf8_unchecked(bytes).to_owned() }; let (mac, ip) = sockaddr_to_network_addr(addr_ref.ifa_addr as *const libc::sockaddr); let (_, netmask) = sockaddr_to_network_addr(addr_ref.ifa_netmask as *const libc::sockaddr); let mut ini_ipv4: Vec = vec![]; @@ -215,33 +220,29 @@ pub fn unix_interfaces() -> Vec { match ip { IpAddr::V4(ipv4) => { let netmask: Ipv4Addr = match netmask { - Some(netmask) => { - match netmask { - IpAddr::V4(netmask) => netmask, - IpAddr::V6(_) => Ipv4Addr::UNSPECIFIED, - } + Some(netmask) => match netmask { + IpAddr::V4(netmask) => netmask, + IpAddr::V6(_) => Ipv4Addr::UNSPECIFIED, }, None => Ipv4Addr::UNSPECIFIED, }; let ipv4_net: Ipv4Net = Ipv4Net::new_with_netmask(ipv4, netmask); ini_ipv4.push(ipv4_net); - }, + } IpAddr::V6(ipv6) => { let netmask: Ipv6Addr = match netmask { - Some(netmask) => { - match netmask { - IpAddr::V4(_) => Ipv6Addr::UNSPECIFIED, - IpAddr::V6(netmask) => netmask, - } + Some(netmask) => match netmask { + IpAddr::V4(_) => Ipv6Addr::UNSPECIFIED, + IpAddr::V6(netmask) => netmask, }, None => Ipv6Addr::UNSPECIFIED, }; let ipv6_net: Ipv6Net = Ipv6Net::new_with_netmask(ipv6, netmask); ini_ipv6.push(ipv6_net); - }, + } } } - let interface: Interface = Interface{ + let interface: Interface = Interface { index: 0, name: name.clone(), friendly_name: None, @@ -265,30 +266,26 @@ pub fn unix_interfaces() -> Vec { match ip { IpAddr::V4(ipv4) => { let netmask: Ipv4Addr = match netmask { - Some(netmask) => { - match netmask { - IpAddr::V4(netmask) => netmask, - IpAddr::V6(_) => Ipv4Addr::UNSPECIFIED, - } + Some(netmask) => match netmask { + IpAddr::V4(netmask) => netmask, + IpAddr::V6(_) => Ipv4Addr::UNSPECIFIED, }, None => Ipv4Addr::UNSPECIFIED, }; let ipv4_net: Ipv4Net = Ipv4Net::new_with_netmask(ipv4, netmask); iface.ipv4.push(ipv4_net); - }, + } IpAddr::V6(ipv6) => { let netmask: Ipv6Addr = match netmask { - Some(netmask) => { - match netmask { - IpAddr::V4(_) => Ipv6Addr::UNSPECIFIED, - IpAddr::V6(netmask) => netmask, - } + Some(netmask) => match netmask { + IpAddr::V4(_) => Ipv6Addr::UNSPECIFIED, + IpAddr::V6(netmask) => netmask, }, None => Ipv6Addr::UNSPECIFIED, }; let ipv6_net: Ipv6Net = Ipv6Net::new_with_netmask(ipv6, netmask); iface.ipv6.push(ipv6_net); - }, + } } } found = true; @@ -299,14 +296,29 @@ pub fn unix_interfaces() -> Vec { } addr = addr_ref.ifa_next; } - unsafe{ libc::freeifaddrs(addrs); } + unsafe { + freeifaddrs(addrs); + } for iface in &mut ifaces { let name = CString::new(iface.name.as_bytes()).unwrap(); - unsafe { iface.index = libc::if_nametoindex(name.as_ptr()); } + unsafe { + iface.index = libc::if_nametoindex(name.as_ptr()); + } } ifaces } +#[cfg(not(target_os = "android"))] +unsafe fn getifaddrs(ifap: *mut *mut libc::ifaddrs) -> libc::c_int { + // Not android, everything is easy + libc::getifaddrs(ifap) +} + +#[cfg(not(target_os = "android"))] +unsafe fn freeifaddrs(ifa: *mut libc::ifaddrs) { + libc::freeifaddrs(ifa); +} + #[cfg(test)] mod tests { use super::*;