Skip to content

Commit 7e89532

Browse files
authored
Merge pull request #22 from dignifiedquire/feat-android
feat: Android compatibility
2 parents 07a3fce + 584b96f commit 7e89532

File tree

4 files changed

+339
-2
lines changed

4 files changed

+339
-2
lines changed

Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ license = "MIT"
1313
[dependencies]
1414
libc = "0.2"
1515

16+
[target.'cfg(target_os = "android")'.dependencies]
17+
# DL Open
18+
dlopen2 = "0.4"
19+
once_cell = "1"
20+
# netlink
21+
netlink-packet-core = "0.5"
22+
netlink-packet-route = "0.15"
23+
netlink-sys = "0.8"
24+
1625
[target.'cfg(windows)'.dependencies]
1726
memalloc = "0.1.0"
1827

src/interface/android.rs

Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
use once_cell::sync::OnceCell;
2+
3+
pub fn get_libc_ifaddrs() -> Option<(
4+
unsafe extern "C" fn(*mut *mut libc::ifaddrs) -> libc::c_int,
5+
unsafe extern "C" fn(*mut libc::ifaddrs),
6+
)> {
7+
match (get_getifaddrs(), get_freeifaddrs()) {
8+
(Some(a), Some(b)) => Some((a, b)),
9+
_ => None,
10+
}
11+
}
12+
13+
fn load_symbol<T>(sym: &'static str) -> Option<T> {
14+
const LIB_NAME: &str = "libc.so";
15+
16+
match dlopen::raw::Library::open(LIB_NAME) {
17+
Ok(lib) => match unsafe { lib.symbol::<T>(sym) } {
18+
Ok(val) => Some(val),
19+
Err(err) => {
20+
eprintln!("failed to load symbol {} from {}: {:?}", sym, LIB_NAME, err);
21+
None
22+
}
23+
},
24+
Err(err) => {
25+
eprintln!("failed to load {}: {:?}", LIB_NAME, err);
26+
None
27+
}
28+
}
29+
}
30+
31+
fn get_getifaddrs() -> Option<unsafe extern "C" fn(*mut *mut libc::ifaddrs) -> libc::c_int> {
32+
static INSTANCE: OnceCell<
33+
Option<unsafe extern "C" fn(*mut *mut libc::ifaddrs) -> libc::c_int>,
34+
> = OnceCell::new();
35+
36+
*INSTANCE.get_or_init(|| load_symbol("getifaddrs"))
37+
}
38+
39+
fn get_freeifaddrs() -> Option<unsafe extern "C" fn(*mut libc::ifaddrs)> {
40+
static INSTANCE: OnceCell<Option<unsafe extern "C" fn(*mut libc::ifaddrs)>> = OnceCell::new();
41+
42+
*INSTANCE.get_or_init(|| load_symbol("freeifaddrs"))
43+
}
44+
45+
pub mod netlink {
46+
//! Netlink based getifaddrs.
47+
//!
48+
//! Based on the logic found in https://git.musl-libc.org/cgit/musl/tree/src/network/getifaddrs.c
49+
50+
use netlink_packet_core::{
51+
NetlinkHeader, NetlinkMessage, NetlinkPayload, NLM_F_DUMP, NLM_F_REQUEST,
52+
};
53+
use netlink_packet_route::{
54+
rtnl::address::nlas::Nla as AddressNla, rtnl::link::nlas::Nla as LinkNla, AddressMessage,
55+
LinkMessage, RtnlMessage,
56+
};
57+
use netlink_sys::{protocols::NETLINK_ROUTE, Socket};
58+
use std::io;
59+
use std::net::{Ipv4Addr, Ipv6Addr};
60+
61+
use crate::interface::{Interface, InterfaceType, Ipv4Net, Ipv6Net, MacAddr};
62+
63+
pub fn unix_interfaces() -> Vec<Interface> {
64+
let mut ifaces = Vec::new();
65+
if let Ok(socket) = Socket::new(NETLINK_ROUTE) {
66+
if let Err(err) = enumerate_netlink(
67+
&socket,
68+
RtnlMessage::GetLink(LinkMessage::default()),
69+
&mut ifaces,
70+
handle_new_link,
71+
) {
72+
eprintln!("unable to list interfaces: {:?}", err);
73+
};
74+
if let Err(err) = enumerate_netlink(
75+
&socket,
76+
RtnlMessage::GetAddress(AddressMessage::default()),
77+
&mut ifaces,
78+
handle_new_addr,
79+
) {
80+
eprintln!("unable to list addresses: {:?}", err);
81+
}
82+
}
83+
ifaces
84+
}
85+
86+
fn handle_new_link(ifaces: &mut Vec<Interface>, msg: RtnlMessage) -> io::Result<()> {
87+
match msg {
88+
RtnlMessage::NewLink(link_msg) => {
89+
let mut interface: Interface = Interface {
90+
index: link_msg.header.index,
91+
name: String::new(),
92+
friendly_name: None,
93+
description: None,
94+
if_type: InterfaceType::try_from(link_msg.header.link_layer_type as u32)
95+
.unwrap_or(InterfaceType::Unknown),
96+
mac_addr: None,
97+
ipv4: Vec::new(),
98+
ipv6: Vec::new(),
99+
flags: link_msg.header.flags,
100+
transmit_speed: None,
101+
receive_speed: None,
102+
gateway: None,
103+
};
104+
105+
for nla in link_msg.nlas {
106+
match nla {
107+
LinkNla::IfName(name) => {
108+
interface.name = name;
109+
}
110+
LinkNla::Address(addr) => {
111+
match addr.len() {
112+
6 => {
113+
interface.mac_addr =
114+
Some(MacAddr::new(addr.try_into().unwrap()));
115+
}
116+
4 => {
117+
let ip = Ipv4Addr::from(<[u8; 4]>::try_from(addr).unwrap());
118+
interface
119+
.ipv4
120+
.push(Ipv4Net::new_with_netmask(ip, Ipv4Addr::UNSPECIFIED));
121+
}
122+
_ => {
123+
// unclear what these would be
124+
}
125+
}
126+
}
127+
_ => {}
128+
}
129+
}
130+
ifaces.push(interface);
131+
}
132+
_ => {}
133+
}
134+
135+
Ok(())
136+
}
137+
138+
fn handle_new_addr(ifaces: &mut Vec<Interface>, msg: RtnlMessage) -> io::Result<()> {
139+
match msg {
140+
RtnlMessage::NewAddress(addr_msg) => {
141+
if let Some(interface) =
142+
ifaces.iter_mut().find(|i| i.index == addr_msg.header.index)
143+
{
144+
for nla in addr_msg.nlas {
145+
match nla {
146+
AddressNla::Address(addr) => match addr.len() {
147+
4 => {
148+
let ip = Ipv4Addr::from(<[u8; 4]>::try_from(addr).unwrap());
149+
interface
150+
.ipv4
151+
.push(Ipv4Net::new(ip, addr_msg.header.prefix_len));
152+
}
153+
16 => {
154+
let ip = Ipv6Addr::from(<[u8; 16]>::try_from(addr).unwrap());
155+
interface
156+
.ipv6
157+
.push(Ipv6Net::new(ip, addr_msg.header.prefix_len));
158+
}
159+
_ => {
160+
// what else?
161+
}
162+
},
163+
_ => {}
164+
}
165+
}
166+
} else {
167+
eprintln!(
168+
"found unknown interface with index: {}",
169+
addr_msg.header.index
170+
);
171+
}
172+
}
173+
_ => {}
174+
}
175+
176+
Ok(())
177+
}
178+
179+
struct NetlinkIter<'a> {
180+
socket: &'a Socket,
181+
/// Buffer for received data.
182+
buf: Vec<u8>,
183+
/// Size of the data available in `buf`.
184+
size: usize,
185+
/// Offset into the data currently in `buf`.
186+
offset: usize,
187+
/// Are we don iterating?
188+
done: bool,
189+
}
190+
191+
impl<'a> NetlinkIter<'a> {
192+
fn new(socket: &'a Socket, msg: RtnlMessage) -> io::Result<Self> {
193+
let mut packet =
194+
NetlinkMessage::new(NetlinkHeader::default(), NetlinkPayload::from(msg));
195+
packet.header.flags = NLM_F_DUMP | NLM_F_REQUEST;
196+
packet.header.sequence_number = 1;
197+
packet.finalize();
198+
199+
let mut buf = vec![0; packet.header.length as usize];
200+
assert_eq!(buf.len(), packet.buffer_len());
201+
packet.serialize(&mut buf[..]);
202+
socket.send(&buf[..], 0)?;
203+
204+
Ok(NetlinkIter {
205+
socket,
206+
offset: 0,
207+
size: 0,
208+
buf: vec![0u8; 4096],
209+
done: false,
210+
})
211+
}
212+
}
213+
214+
impl<'a> Iterator for NetlinkIter<'a> {
215+
type Item = io::Result<RtnlMessage>;
216+
217+
fn next(&mut self) -> Option<Self::Item> {
218+
if self.done {
219+
return None;
220+
}
221+
222+
while !self.done {
223+
// Outer loop
224+
if self.size == 0 {
225+
match self.socket.recv(&mut &mut self.buf[..], 0) {
226+
Ok(size) => {
227+
self.size = size;
228+
self.offset = 0;
229+
}
230+
Err(err) => {
231+
self.done = true;
232+
return Some(Err(err));
233+
}
234+
}
235+
}
236+
237+
let bytes = &self.buf[self.offset..];
238+
match NetlinkMessage::<RtnlMessage>::deserialize(bytes) {
239+
Ok(packet) => {
240+
self.offset += packet.header.length as usize;
241+
if packet.header.length == 0 || self.offset == self.size {
242+
// mark this message as fully read
243+
self.size = 0;
244+
}
245+
match packet.payload {
246+
NetlinkPayload::Done => {
247+
self.done = true;
248+
return None;
249+
}
250+
NetlinkPayload::Error(err) => {
251+
self.done = true;
252+
return Some(Err(io::Error::new(
253+
io::ErrorKind::Other,
254+
err.to_string(),
255+
)));
256+
}
257+
NetlinkPayload::InnerMessage(msg) => return Some(Ok(msg)),
258+
_ => {
259+
continue;
260+
}
261+
}
262+
}
263+
Err(err) => {
264+
self.done = true;
265+
return Some(Err(io::Error::new(io::ErrorKind::Other, err.to_string())));
266+
}
267+
}
268+
}
269+
270+
None
271+
}
272+
}
273+
274+
fn enumerate_netlink<F>(
275+
socket: &Socket,
276+
msg: RtnlMessage,
277+
ifaces: &mut Vec<Interface>,
278+
cb: F,
279+
) -> io::Result<()>
280+
where
281+
F: Fn(&mut Vec<Interface>, RtnlMessage) -> io::Result<()>,
282+
{
283+
let iter = NetlinkIter::new(socket, msg)?;
284+
for msg in iter {
285+
let msg = msg?;
286+
cb(ifaces, msg)?;
287+
}
288+
289+
Ok(())
290+
}
291+
292+
#[cfg(test)]
293+
mod tests {
294+
use super::*;
295+
296+
#[test]
297+
fn test_netlink_ifaddrs() {
298+
let interfaces = unix_interfaces();
299+
dbg!(&interfaces);
300+
assert!(!interfaces.is_empty());
301+
}
302+
}
303+
}

src/interface/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ use self::windows::*;
3333
#[cfg(any(target_os = "linux", target_os = "android"))]
3434
mod linux;
3535

36+
#[cfg(target_os = "android")]
37+
mod android;
38+
3639
#[cfg(any(target_os = "macos", target_os = "ios"))]
3740
mod macos;
3841

src/interface/unix.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use super::Interface;
22
use super::MacAddr;
33
use crate::gateway;
4+
use crate::interface::InterfaceType;
45
use crate::ip::{Ipv4Net, Ipv6Net};
56
use crate::sys;
67

@@ -125,7 +126,9 @@ pub fn interfaces() -> Vec<Interface> {
125126
}
126127

127128
#[cfg(any(target_os = "linux", target_os = "android"))]
128-
fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option<MacAddr>, Option<IpAddr>) {
129+
pub(super) fn sockaddr_to_network_addr(
130+
sa: *const libc::sockaddr,
131+
) -> (Option<MacAddr>, Option<IpAddr>) {
129132
use std::net::SocketAddr;
130133

131134
unsafe {
@@ -196,10 +199,29 @@ fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option<MacAddr>, Opti
196199
}
197200
}
198201

202+
#[cfg(target_os = "android")]
203+
pub fn unix_interfaces() -> Vec<Interface> {
204+
use super::android;
205+
206+
if let Some((getifaddrs, freeifaddrs)) = android::get_libc_ifaddrs() {
207+
return unix_interfaces_inner(getifaddrs, freeifaddrs);
208+
}
209+
210+
android::netlink::unix_interfaces()
211+
}
212+
213+
#[cfg(not(target_os = "android"))]
199214
pub fn unix_interfaces() -> Vec<Interface> {
215+
unix_interfaces_inner(libc::getifaddrs, libc::freeifaddrs)
216+
}
217+
218+
fn unix_interfaces_inner(
219+
getifaddrs: unsafe extern "C" fn(*mut *mut libc::ifaddrs) -> libc::c_int,
220+
freeifaddrs: unsafe extern "C" fn(*mut libc::ifaddrs),
221+
) -> Vec<Interface> {
200222
let mut ifaces: Vec<Interface> = vec![];
201223
let mut addrs: MaybeUninit<*mut libc::ifaddrs> = MaybeUninit::uninit();
202-
if unsafe { libc::getifaddrs(addrs.as_mut_ptr()) } != 0 {
224+
if unsafe { getifaddrs(addrs.as_mut_ptr()) } != 0 {
203225
return ifaces;
204226
}
205227
let addrs = unsafe { addrs.assume_init() };

0 commit comments

Comments
 (0)