feat: try to dynamically load libc.so on Android

main
dignifiedquire 1 year ago
parent 8f22d88792
commit a8bed97314
  1. 4
      Cargo.toml
  2. 64
      src/interface/android.rs
  3. 14
      src/interface/linux.rs
  4. 70
      src/interface/mod.rs
  5. 138
      src/interface/unix.rs

@ -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"

@ -0,0 +1,64 @@
use once_cell::sync::OnceCell;
fn load_symbol<T>(sym: &'static str) -> Option<T> {
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::<T>(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<unsafe extern "C" fn(*mut *mut libc::ifaddrs) -> libc::c_int> {
static INSTANCE: OnceCell<
Option<unsafe extern "C" fn(*mut *mut libc::ifaddrs) -> libc::c_int>,
> = OnceCell::new();
*INSTANCE.get_or_init(|| load_symbol("getifaddrs"))
}
fn get_freeifaddrs() -> Option<unsafe extern "C" fn(*mut libc::ifaddrs)> {
static INSTANCE: OnceCell<Option<unsafe extern "C" fn(*mut libc::ifaddrs)>> = 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!()
}

@ -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::<u32>() {
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<u64> {
@ -33,14 +33,14 @@ pub fn get_interface_speed(if_name: String) -> Option<u64> {
Ok(if_speed) => {
// Convert Mbps to bps
return Some(if_speed * 1000000);
},
}
Err(_) => {
return None;
}
}
},
}
Err(_) => {
return None;
}
};
};
}

@ -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<String>,
pub friendly_name: Option<String>,
/// Description of the network interface
pub description: Option<String>,
/// Interface Type
@ -99,7 +127,7 @@ pub struct Interface {
/// Get default Network Interface
pub fn get_default_interface() -> Result<Interface, String> {
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<Interface, String> {
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<Interface, String> {
/// Get default Network Interface index
pub fn get_default_interface_index() -> Option<u32> {
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<u32> {
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<u32> {
/// Get default Network Interface name
pub fn get_default_interface_name() -> Option<String> {
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<String> {
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

@ -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<Interface> {
let mut interfaces: Vec<Interface> = 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<Interface> {
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<Interface> {
let type_map = macos::get_if_type_map();
let mut interfaces: Vec<Interface> = 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<Interface> {
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<Interface> {
use super::linux;
let mut interfaces: Vec<Interface> = 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<Interface> {
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<MacAddr>, Opti
(Some(mac), None)
} else {
let addr = sys::sockaddr_to_addr(
mem::transmute(sa),
mem::size_of::<libc::sockaddr_storage>(),
);
let addr =
sys::sockaddr_to_addr(mem::transmute(sa), mem::size_of::<libc::sockaddr_storage>());
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<MacAddr>, 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<MacAddr>, Option<IpAddr>) {
use crate::bpf;
use std::net::SocketAddr;
@ -180,10 +187,8 @@ fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option<MacAddr>, Opti
(Some(mac), None)
} else {
let addr = sys::sockaddr_to_addr(
mem::transmute(sa),
mem::size_of::<libc::sockaddr_storage>(),
);
let addr =
sys::sockaddr_to_addr(mem::transmute(sa), mem::size_of::<libc::sockaddr_storage>());
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<MacAddr>, Opti
pub fn unix_interfaces() -> Vec<Interface> {
let mut ifaces: Vec<Interface> = 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<Ipv4Net> = vec![];
@ -215,33 +220,29 @@ pub fn unix_interfaces() -> Vec<Interface> {
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<Interface> {
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<Interface> {
}
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::*;

Loading…
Cancel
Save