From a8bed97314bcf8472b9235706fef0f2785b3ec4b Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 10 Mar 2023 16:19:32 +0100 Subject: [PATCH 1/8] feat: try to dynamically load libc.so on Android --- Cargo.toml | 4 ++ src/interface/android.rs | 64 ++++++++++++++++++ src/interface/linux.rs | 14 ++-- src/interface/mod.rs | 70 ++++++++++++++------ src/interface/unix.rs | 138 +++++++++++++++++++++------------------ 5 files changed, 199 insertions(+), 91 deletions(-) create mode 100644 src/interface/android.rs 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::*; From f629ab3ccb3088867d68ce89e569596aac104f9c Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sun, 12 Mar 2023 00:35:17 +0100 Subject: [PATCH 2/8] feat: use netlink as a fallback on android --- Cargo.toml | 5 + src/interface/android.rs | 211 +++++++++++++++++++++++++++++++++++---- src/interface/unix.rs | 46 +++++---- 3 files changed, 224 insertions(+), 38 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ff1dcda..13f4421 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,13 @@ license = "MIT" libc = "0.2" [target.'cfg(target_os = "android")'.dependencies] +# DL Open dlopen = "0.1.8" once_cell = "1.17.1" +# netlink +netlink-packet-core = "0.5" +netlink-packet-route = "0.15" +netlink-sys = "0.8" [target.'cfg(windows)'.dependencies] memalloc = "0.1.0" diff --git a/src/interface/android.rs b/src/interface/android.rs index b4d6770..0798384 100644 --- a/src/interface/android.rs +++ b/src/interface/android.rs @@ -1,5 +1,15 @@ use once_cell::sync::OnceCell; +pub fn get_libc_ifaddrs() -> Option<( + unsafe extern "C" fn(*mut *mut libc::ifaddrs) -> libc::c_int, + unsafe extern "C" fn(*mut libc::ifaddrs), +)> { + match (get_getifaddrs(), get_freeifaddrs()) { + (Some(a), Some(b)) => Some((a, b)), + _ => None, + } +} + fn load_symbol(sym: &'static str) -> Option { const LIB_NAME: &str = "libc.so"; @@ -33,32 +43,191 @@ fn get_freeifaddrs() -> Option { *INSTANCE.get_or_init(|| load_symbol("freeifaddrs")) } -pub unsafe fn getifaddrs(ifap: *mut *mut libc::ifaddrs) -> libc::c_int { - // Android is complicated +mod netlink { + //! Netlink based getifaddrs. + //! + //! Based on the logic found in https://git.musl-libc.org/cgit/musl/tree/src/network/getifaddrs.c - // 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); - } + use netlink_packet_core::{ + NetlinkHeader, NetlinkMessage, NetlinkPayload, NLM_F_DUMP, NLM_F_REQUEST, + }; + use netlink_packet_route::{ + rtnl::address::nlas::Nla as AddressNla, rtnl::link::nlas::Nla as LinkNla, AddressMessage, + LinkMessage, RtnlMessage, + }; + use netlink_sys::{protocols::NETLINK_ROUTE, Socket}; + use std::net::{Ipv4Addr, Ipv6Addr}; - // If API < 24 (or we can't load libc for some other reason), we fallback to using netlink - netlink_getifaddrs(ifap) -} + use crate::interface::{Interface, InterfaceType, Ipv4Net, Ipv6Net, MacAddr}; + + pub fn unix_interfaces() -> Vec { + let socket = Socket::new(NETLINK_ROUTE).unwrap(); -pub unsafe fn freeifaddrs(ifa: *mut libc::ifaddrs) { - if let Some(dyn_freeifaddrs) = get_freeifaddrs() { - return dyn_freeifaddrs(ifa); + let mut ifaces = Vec::new(); + enumerate_netlink( + &socket, + RtnlMessage::GetLink(LinkMessage::default()), + &mut ifaces, + ); + enumerate_netlink( + &socket, + RtnlMessage::GetAddress(AddressMessage::default()), + &mut ifaces, + ); + + ifaces } - netlink_freeifaddrs(ifa) -} + fn enumerate_netlink(socket: &Socket, msg: RtnlMessage, ifaces: &mut Vec) { + let mut packet = NetlinkMessage::new(NetlinkHeader::default(), NetlinkPayload::from(msg)); + packet.header.flags = NLM_F_DUMP | NLM_F_REQUEST; + packet.header.sequence_number = 1; + packet.finalize(); -unsafe fn netlink_getifaddrs(ifap: *mut *mut libc::ifaddrs) -> libc::c_int { - todo!() -} + let mut buf = vec![0; packet.header.length as usize]; + + // TODO: gracefully handle error + assert!(buf.len() == packet.buffer_len()); + packet.serialize(&mut buf[..]); + + socket.send(&buf[..], 0).unwrap(); -unsafe fn netlink_freeifaddrs(ifa: *mut libc::ifaddrs) { - todo!() + let mut receive_buffer = vec![0; 4096]; + let mut offset = 0; + + loop { + let size = socket.recv(&mut &mut receive_buffer[..], 0).unwrap(); + + loop { + let bytes = &receive_buffer[offset..]; + let rx_packet: NetlinkMessage = + NetlinkMessage::deserialize(bytes).unwrap(); + + match rx_packet.payload { + NetlinkPayload::Done => { + return; + } + NetlinkPayload::Error(err) => { + eprintln!("Error: {:?}", err); + return; + } + NetlinkPayload::InnerMessage(msg) => { + match msg { + RtnlMessage::NewLink(link_msg) => { + let mut interface: Interface = Interface { + index: link_msg.header.index, + name: String::new(), + friendly_name: None, + description: None, + if_type: InterfaceType::try_from( + link_msg.header.link_layer_type as u32, + ) + .unwrap_or(InterfaceType::Unknown), + mac_addr: None, + ipv4: Vec::new(), + ipv6: Vec::new(), + flags: link_msg.header.flags, + transmit_speed: None, + receive_speed: None, + gateway: None, + }; + + for nla in link_msg.nlas { + match nla { + LinkNla::IfName(name) => { + interface.name = name; + } + LinkNla::Address(addr) => { + match addr.len() { + 6 => { + interface.mac_addr = Some(MacAddr::new( + addr.try_into().unwrap(), + )); + } + 4 => { + let ip = Ipv4Addr::from( + <[u8; 4]>::try_from(addr).unwrap(), + ); + interface.ipv4.push(Ipv4Net::new_with_netmask( + ip, + Ipv4Addr::UNSPECIFIED, + )); + } + _ => { + // unclear what these would be + } + } + } + _ => {} + } + } + ifaces.push(interface); + } + RtnlMessage::NewAddress(addr_msg) => { + println!("NewAddress: {:?}", addr_msg); + if let Some(interface) = + ifaces.iter_mut().find(|i| i.index == addr_msg.header.index) + { + for nla in addr_msg.nlas { + match nla { + AddressNla::Address(addr) => match addr.len() { + 4 => { + let ip = Ipv4Addr::from( + <[u8; 4]>::try_from(addr).unwrap(), + ); + interface.ipv4.push(Ipv4Net::new( + ip, + addr_msg.header.prefix_len, + )); + } + 16 => { + let ip = Ipv6Addr::from( + <[u8; 16]>::try_from(addr).unwrap(), + ); + interface.ipv6.push(Ipv6Net::new( + ip, + addr_msg.header.prefix_len, + )); + } + _ => { + // what else? + } + }, + _ => {} + } + } + } else { + eprintln!( + "found unknown interface with index: {}", + addr_msg.header.index + ); + } + } + _ => { + // not expecting other messages + } + } + } + _ => {} + } + offset += rx_packet.header.length as usize; + if offset == size || rx_packet.header.length == 0 { + offset = 0; + break; + } + } + } + } + + #[cfg(test)] + mod tests { + use super::*; + + #[test] + fn test_netlink_ifaddrs() { + let interfaces = unix_interfaces(); + dbg!(&interfaces); + assert!(!interfaces.is_empty()); + } + } } diff --git a/src/interface/unix.rs b/src/interface/unix.rs index e198b6d..6775286 100644 --- a/src/interface/unix.rs +++ b/src/interface/unix.rs @@ -1,10 +1,10 @@ use super::Interface; use super::MacAddr; use crate::gateway; +use crate::interface::InterfaceType; use crate::ip::{Ipv4Net, Ipv6Net}; use crate::sys; -use crate::interface::InterfaceType; use libc; use std::ffi::{CStr, CString}; use std::mem::{self, MaybeUninit}; @@ -12,9 +12,6 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::os::raw::c_char; use std::str::from_utf8_unchecked; -#[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(); @@ -128,7 +125,9 @@ pub fn interfaces() -> Vec { } #[cfg(any(target_os = "linux", target_os = "android"))] -fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option, Option) { +pub(super) fn sockaddr_to_network_addr( + sa: *const libc::sockaddr, +) -> (Option, Option) { use std::net::SocketAddr; unsafe { @@ -150,7 +149,7 @@ fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option, Opti let addr = sys::sockaddr_to_addr(mem::transmute(sa), mem::size_of::()); - match addr { + match dbg!(addr) { Ok(SocketAddr::V4(sa)) => (None, Some(IpAddr::V4(*sa.ip()))), Ok(SocketAddr::V6(sa)) => (None, Some(IpAddr::V6(*sa.ip()))), Err(_) => (None, None), @@ -199,7 +198,30 @@ fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option, Opti } } +#[cfg(not(target_os = "android"))] +pub fn unix_interfaces() -> Vec { + unix_interfaces_inner(libc::getifaddrs, libc::freeifaddrs) +} + +#[cfg(target_os = "android")] pub fn unix_interfaces() -> Vec { + // 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((getifaddrs, freeeifaddrs)) = android::get_libc_ifaddrs() { + return unix_interfaces_inner(getifaddrs, freeifaddrs); + } + + // If API < 24 (or we can't load libc for some other reason), we fallback to using netlink + android::netlink::unix_interfaces() +} + +pub fn unix_interfaces_inner( + getifaddrs: unsafe extern "C" fn(ifap: *mut *mut libc::ifaddrs) -> libc::c_int, + freeifaddrs: unsafe extern "C" fn(ifa: *mut libc::ifaddrs), +) -> Vec { let mut ifaces: Vec = vec![]; let mut addrs: MaybeUninit<*mut libc::ifaddrs> = MaybeUninit::uninit(); if unsafe { getifaddrs(addrs.as_mut_ptr()) } != 0 { @@ -212,6 +234,7 @@ pub fn unix_interfaces() -> Vec { 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 (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![]; @@ -308,17 +331,6 @@ pub fn unix_interfaces() -> Vec { 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::*; From 7b1545ebd80fb6313825f0d659b3b98babb900e5 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sun, 12 Mar 2023 00:44:07 +0100 Subject: [PATCH 3/8] remove stray println --- src/interface/android.rs | 1 - src/interface/unix.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/interface/android.rs b/src/interface/android.rs index 0798384..cc85a42 100644 --- a/src/interface/android.rs +++ b/src/interface/android.rs @@ -164,7 +164,6 @@ mod netlink { ifaces.push(interface); } RtnlMessage::NewAddress(addr_msg) => { - println!("NewAddress: {:?}", addr_msg); if let Some(interface) = ifaces.iter_mut().find(|i| i.index == addr_msg.header.index) { diff --git a/src/interface/unix.rs b/src/interface/unix.rs index 6775286..f615d27 100644 --- a/src/interface/unix.rs +++ b/src/interface/unix.rs @@ -149,7 +149,7 @@ pub(super) fn sockaddr_to_network_addr( let addr = sys::sockaddr_to_addr(mem::transmute(sa), mem::size_of::()); - match dbg!(addr) { + match addr { Ok(SocketAddr::V4(sa)) => (None, Some(IpAddr::V4(*sa.ip()))), Ok(SocketAddr::V6(sa)) => (None, Some(IpAddr::V6(*sa.ip()))), Err(_) => (None, None), From fba8877f4a6cc1e245d2adc416408eeb980ee645 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sun, 12 Mar 2023 13:29:20 +0100 Subject: [PATCH 4/8] cleanup netlink impl --- src/interface/android.rs | 340 ++++++++++++++++++++++++--------------- 1 file changed, 206 insertions(+), 134 deletions(-) diff --git a/src/interface/android.rs b/src/interface/android.rs index cc85a42..b8addde 100644 --- a/src/interface/android.rs +++ b/src/interface/android.rs @@ -56,166 +56,238 @@ mod netlink { LinkMessage, RtnlMessage, }; use netlink_sys::{protocols::NETLINK_ROUTE, Socket}; + use std::io; use std::net::{Ipv4Addr, Ipv6Addr}; use crate::interface::{Interface, InterfaceType, Ipv4Net, Ipv6Net, MacAddr}; pub fn unix_interfaces() -> Vec { - let socket = Socket::new(NETLINK_ROUTE).unwrap(); - let mut ifaces = Vec::new(); - enumerate_netlink( - &socket, - RtnlMessage::GetLink(LinkMessage::default()), - &mut ifaces, - ); - enumerate_netlink( - &socket, - RtnlMessage::GetAddress(AddressMessage::default()), - &mut ifaces, - ); - + if let Ok(socket) = Socket::new(NETLINK_ROUTE) { + if let Err(err) = enumerate_netlink( + &socket, + RtnlMessage::GetLink(LinkMessage::default()), + &mut ifaces, + handle_new_link, + ) { + eprintln!("unable to list interfaces: {:?}", err); + }; + if let Err(err) = enumerate_netlink( + &socket, + RtnlMessage::GetAddress(AddressMessage::default()), + &mut ifaces, + handle_new_addr, + ) { + eprintln!("unable to list addresses: {:?}", err); + } + } ifaces } - fn enumerate_netlink(socket: &Socket, msg: RtnlMessage, ifaces: &mut Vec) { - let mut packet = NetlinkMessage::new(NetlinkHeader::default(), NetlinkPayload::from(msg)); - packet.header.flags = NLM_F_DUMP | NLM_F_REQUEST; - packet.header.sequence_number = 1; - packet.finalize(); + fn handle_new_link(ifaces: &mut Vec, msg: RtnlMessage) -> io::Result<()> { + match msg { + RtnlMessage::NewLink(link_msg) => { + let mut interface: Interface = Interface { + index: link_msg.header.index, + name: String::new(), + friendly_name: None, + description: None, + if_type: InterfaceType::try_from(link_msg.header.link_layer_type as u32) + .unwrap_or(InterfaceType::Unknown), + mac_addr: None, + ipv4: Vec::new(), + ipv6: Vec::new(), + flags: link_msg.header.flags, + transmit_speed: None, + receive_speed: None, + gateway: None, + }; + + for nla in link_msg.nlas { + match nla { + LinkNla::IfName(name) => { + interface.name = name; + } + LinkNla::Address(addr) => { + match addr.len() { + 6 => { + interface.mac_addr = + Some(MacAddr::new(addr.try_into().unwrap())); + } + 4 => { + let ip = Ipv4Addr::from(<[u8; 4]>::try_from(addr).unwrap()); + interface + .ipv4 + .push(Ipv4Net::new_with_netmask(ip, Ipv4Addr::UNSPECIFIED)); + } + _ => { + // unclear what these would be + } + } + } + _ => {} + } + } + ifaces.push(interface); + } + _ => {} + } + + Ok(()) + } + + fn handle_new_addr(ifaces: &mut Vec, msg: RtnlMessage) -> io::Result<()> { + match msg { + RtnlMessage::NewAddress(addr_msg) => { + if let Some(interface) = + ifaces.iter_mut().find(|i| i.index == addr_msg.header.index) + { + for nla in addr_msg.nlas { + match nla { + AddressNla::Address(addr) => match addr.len() { + 4 => { + let ip = Ipv4Addr::from(<[u8; 4]>::try_from(addr).unwrap()); + interface + .ipv4 + .push(Ipv4Net::new(ip, addr_msg.header.prefix_len)); + } + 16 => { + let ip = Ipv6Addr::from(<[u8; 16]>::try_from(addr).unwrap()); + interface + .ipv6 + .push(Ipv6Net::new(ip, addr_msg.header.prefix_len)); + } + _ => { + // what else? + } + }, + _ => {} + } + } + } else { + eprintln!( + "found unknown interface with index: {}", + addr_msg.header.index + ); + } + } + _ => {} + } + + Ok(()) + } - let mut buf = vec![0; packet.header.length as usize]; + struct NetlinkIter<'a> { + socket: &'a Socket, + /// Buffer for received data. + buf: Vec, + /// Size of the data available in `buf`. + size: usize, + /// Offset into the data currently in `buf`. + offset: usize, + /// Are we don iterating? + done: bool, + } - // TODO: gracefully handle error - assert!(buf.len() == packet.buffer_len()); - packet.serialize(&mut buf[..]); + impl<'a> NetlinkIter<'a> { + fn new(socket: &'a Socket, msg: RtnlMessage) -> io::Result { + let mut packet = + NetlinkMessage::new(NetlinkHeader::default(), NetlinkPayload::from(msg)); + packet.header.flags = NLM_F_DUMP | NLM_F_REQUEST; + packet.header.sequence_number = 1; + packet.finalize(); - socket.send(&buf[..], 0).unwrap(); + let mut buf = vec![0; packet.header.length as usize]; + assert_eq!(buf.len(), packet.buffer_len()); + packet.serialize(&mut buf[..]); + socket.send(&buf[..], 0)?; - let mut receive_buffer = vec![0; 4096]; - let mut offset = 0; + Ok(NetlinkIter { + socket, + offset: 0, + size: 0, + buf: vec![0u8; 4096], + done: false, + }) + } + } - loop { - let size = socket.recv(&mut &mut receive_buffer[..], 0).unwrap(); + impl<'a> Iterator for NetlinkIter<'a> { + type Item = io::Result; - loop { - let bytes = &receive_buffer[offset..]; - let rx_packet: NetlinkMessage = - NetlinkMessage::deserialize(bytes).unwrap(); + fn next(&mut self) -> Option { + if self.done { + return None; + } - match rx_packet.payload { - NetlinkPayload::Done => { - return; - } - NetlinkPayload::Error(err) => { - eprintln!("Error: {:?}", err); - return; + while !self.done { + // Outer loop + if self.size == 0 { + match self.socket.recv(&mut &mut self.buf[..], 0) { + Ok(size) => { + self.size = size; + self.offset = 0; + } + Err(err) => { + self.done = true; + return Some(Err(err)); + } } - NetlinkPayload::InnerMessage(msg) => { - match msg { - RtnlMessage::NewLink(link_msg) => { - let mut interface: Interface = Interface { - index: link_msg.header.index, - name: String::new(), - friendly_name: None, - description: None, - if_type: InterfaceType::try_from( - link_msg.header.link_layer_type as u32, - ) - .unwrap_or(InterfaceType::Unknown), - mac_addr: None, - ipv4: Vec::new(), - ipv6: Vec::new(), - flags: link_msg.header.flags, - transmit_speed: None, - receive_speed: None, - gateway: None, - }; - - for nla in link_msg.nlas { - match nla { - LinkNla::IfName(name) => { - interface.name = name; - } - LinkNla::Address(addr) => { - match addr.len() { - 6 => { - interface.mac_addr = Some(MacAddr::new( - addr.try_into().unwrap(), - )); - } - 4 => { - let ip = Ipv4Addr::from( - <[u8; 4]>::try_from(addr).unwrap(), - ); - interface.ipv4.push(Ipv4Net::new_with_netmask( - ip, - Ipv4Addr::UNSPECIFIED, - )); - } - _ => { - // unclear what these would be - } - } - } - _ => {} - } - } - ifaces.push(interface); + } + + let bytes = &self.buf[self.offset..]; + match NetlinkMessage::::deserialize(bytes) { + Ok(packet) => { + self.offset += packet.header.length as usize; + if packet.header.length == 0 || self.offset == self.size { + // mark this message as fully read + self.size = 0; + } + match packet.payload { + NetlinkPayload::Done => { + self.done = true; + return None; } - RtnlMessage::NewAddress(addr_msg) => { - if let Some(interface) = - ifaces.iter_mut().find(|i| i.index == addr_msg.header.index) - { - for nla in addr_msg.nlas { - match nla { - AddressNla::Address(addr) => match addr.len() { - 4 => { - let ip = Ipv4Addr::from( - <[u8; 4]>::try_from(addr).unwrap(), - ); - interface.ipv4.push(Ipv4Net::new( - ip, - addr_msg.header.prefix_len, - )); - } - 16 => { - let ip = Ipv6Addr::from( - <[u8; 16]>::try_from(addr).unwrap(), - ); - interface.ipv6.push(Ipv6Net::new( - ip, - addr_msg.header.prefix_len, - )); - } - _ => { - // what else? - } - }, - _ => {} - } - } - } else { - eprintln!( - "found unknown interface with index: {}", - addr_msg.header.index - ); - } + NetlinkPayload::Error(err) => { + self.done = true; + return Some(Err(io::Error::new( + io::ErrorKind::Other, + err.to_string(), + ))); } + NetlinkPayload::InnerMessage(msg) => return Some(Ok(msg)), _ => { - // not expecting other messages + continue; } } } - _ => {} - } - offset += rx_packet.header.length as usize; - if offset == size || rx_packet.header.length == 0 { - offset = 0; - break; + Err(err) => { + self.done = true; + return Some(Err(io::Error::new(io::ErrorKind::Other, err.to_string()))); + } } } + + None + } + } + + fn enumerate_netlink( + socket: &Socket, + msg: RtnlMessage, + ifaces: &mut Vec, + cb: F, + ) -> io::Result<()> + where + F: Fn(&mut Vec, RtnlMessage) -> io::Result<()>, + { + let iter = NetlinkIter::new(socket, msg)?; + for msg in iter { + let msg = msg?; + cb(ifaces, msg)?; } + + Ok(()) } #[cfg(test)] From 64e265889aefec26d3351690c590289f892df0cf Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sun, 12 Mar 2023 13:33:49 +0100 Subject: [PATCH 5/8] simplify android impl, to only use netlink --- Cargo.toml | 3 --- src/interface/android.rs | 45 +++------------------------------------- src/interface/unix.rs | 26 +++++------------------ 3 files changed, 8 insertions(+), 66 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 13f4421..0fffd3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,9 +14,6 @@ license = "MIT" libc = "0.2" [target.'cfg(target_os = "android")'.dependencies] -# DL Open -dlopen = "0.1.8" -once_cell = "1.17.1" # netlink netlink-packet-core = "0.5" netlink-packet-route = "0.15" diff --git a/src/interface/android.rs b/src/interface/android.rs index b8addde..ab2084a 100644 --- a/src/interface/android.rs +++ b/src/interface/android.rs @@ -1,46 +1,7 @@ -use once_cell::sync::OnceCell; +use crate::interface::Interface; -pub fn get_libc_ifaddrs() -> Option<( - unsafe extern "C" fn(*mut *mut libc::ifaddrs) -> libc::c_int, - unsafe extern "C" fn(*mut libc::ifaddrs), -)> { - match (get_getifaddrs(), get_freeifaddrs()) { - (Some(a), Some(b)) => Some((a, b)), - _ => None, - } -} - -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 fn unix_interfaces() -> Vec { + netlink::unix_interfaces() } mod netlink { diff --git a/src/interface/unix.rs b/src/interface/unix.rs index f615d27..b91dcbe 100644 --- a/src/interface/unix.rs +++ b/src/interface/unix.rs @@ -198,33 +198,17 @@ fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option, Opti } } -#[cfg(not(target_os = "android"))] -pub fn unix_interfaces() -> Vec { - unix_interfaces_inner(libc::getifaddrs, libc::freeifaddrs) -} #[cfg(target_os = "android")] pub fn unix_interfaces() -> Vec { - // 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((getifaddrs, freeeifaddrs)) = android::get_libc_ifaddrs() { - return unix_interfaces_inner(getifaddrs, freeifaddrs); - } - - // If API < 24 (or we can't load libc for some other reason), we fallback to using netlink - android::netlink::unix_interfaces() + super::android::unix_interfaces() } -pub fn unix_interfaces_inner( - getifaddrs: unsafe extern "C" fn(ifap: *mut *mut libc::ifaddrs) -> libc::c_int, - freeifaddrs: unsafe extern "C" fn(ifa: *mut libc::ifaddrs), -) -> Vec { +#[cfg(not(target_os = "android"))] +pub fn unix_interfaces() -> Vec { let mut ifaces: Vec = vec![]; let mut addrs: MaybeUninit<*mut libc::ifaddrs> = MaybeUninit::uninit(); - if unsafe { getifaddrs(addrs.as_mut_ptr()) } != 0 { + if unsafe { libc::getifaddrs(addrs.as_mut_ptr()) } != 0 { return ifaces; } let addrs = unsafe { addrs.assume_init() }; @@ -320,7 +304,7 @@ pub fn unix_interfaces_inner( addr = addr_ref.ifa_next; } unsafe { - freeifaddrs(addrs); + libc::freeifaddrs(addrs); } for iface in &mut ifaces { let name = CString::new(iface.name.as_bytes()).unwrap(); From 7a257095bac009c4be0b93c2979801624fdd337b Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Mon, 13 Mar 2023 18:05:55 +0100 Subject: [PATCH 6/8] bring back dlopen --- Cargo.toml | 3 +++ src/interface/android.rs | 47 ++++++++++++++++++++++++++++++++++++---- src/interface/unix.rs | 20 +++++++++++++---- 3 files changed, 62 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0fffd3f..13f4421 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,9 @@ license = "MIT" libc = "0.2" [target.'cfg(target_os = "android")'.dependencies] +# DL Open +dlopen = "0.1.8" +once_cell = "1.17.1" # netlink netlink-packet-core = "0.5" netlink-packet-route = "0.15" diff --git a/src/interface/android.rs b/src/interface/android.rs index ab2084a..6f0535c 100644 --- a/src/interface/android.rs +++ b/src/interface/android.rs @@ -1,10 +1,49 @@ -use crate::interface::Interface; +use once_cell::sync::OnceCell; -pub fn unix_interfaces() -> Vec { - netlink::unix_interfaces() +pub fn get_libc_ifaddrs() -> Option<( + unsafe extern "C" fn(*mut *mut libc::ifaddrs) -> libc::c_int, + unsafe extern "C" fn(*mut libc::ifaddrs), +)> { + match (get_getifaddrs(), get_freeifaddrs()) { + (Some(a), Some(b)) => Some((a, b)), + _ => None, + } +} + +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")) } -mod netlink { +pub mod netlink { //! Netlink based getifaddrs. //! //! Based on the logic found in https://git.musl-libc.org/cgit/musl/tree/src/network/getifaddrs.c diff --git a/src/interface/unix.rs b/src/interface/unix.rs index b91dcbe..7a6a839 100644 --- a/src/interface/unix.rs +++ b/src/interface/unix.rs @@ -198,17 +198,29 @@ fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option, Opti } } - #[cfg(target_os = "android")] pub fn unix_interfaces() -> Vec { - super::android::unix_interfaces() + use super::android; + + if let Some((getifaddrs, freeifaddrs)) = android::get_libc_ifaddrs() { + return unix_interfaces_inner(getifaddrs, freeifaddrs); + } + + android::netlink::unix_interfaces() } #[cfg(not(target_os = "android"))] pub fn unix_interfaces() -> Vec { + unix_interfaces_inner(libc::getifaddrs, libc::freeifaddrs) +} + +fn unix_interfaces_inner( + getifaddrs: unsafe extern "C" fn(*mut *mut libc::ifaddrs) -> libc::c_int, + freeifaddrs: unsafe extern "C" fn(*mut libc::ifaddrs), +) -> 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() }; @@ -304,7 +316,7 @@ pub fn unix_interfaces() -> Vec { addr = addr_ref.ifa_next; } unsafe { - libc::freeifaddrs(addrs); + freeifaddrs(addrs); } for iface in &mut ifaces { let name = CString::new(iface.name.as_bytes()).unwrap(); From 8e8ebb974d91f75529145fb3acb25f04ecb55aa7 Mon Sep 17 00:00:00 2001 From: Floris Bruynooghe Date: Wed, 22 Mar 2023 16:46:40 +0100 Subject: [PATCH 7/8] Use dlopen2 as that's maintained --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 13f4421..32fe12d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,8 +15,8 @@ libc = "0.2" [target.'cfg(target_os = "android")'.dependencies] # DL Open -dlopen = "0.1.8" -once_cell = "1.17.1" +dlopen2 = "0.4" +once_cell = "1" # netlink netlink-packet-core = "0.5" netlink-packet-route = "0.15" From e649387dc0751286de4aac8ad9bffb5f806a2b62 Mon Sep 17 00:00:00 2001 From: Floris Bruynooghe Date: Wed, 22 Mar 2023 17:18:06 +0100 Subject: [PATCH 8/8] remove println --- src/interface/android.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/interface/android.rs b/src/interface/android.rs index 6f0535c..4012f84 100644 --- a/src/interface/android.rs +++ b/src/interface/android.rs @@ -13,7 +13,6 @@ pub fn get_libc_ifaddrs() -> Option<( 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),