Skip to content

Commit aade615

Browse files
committed
feat(ios): improve interface type detection
1 parent 0174b8e commit aade615

File tree

4 files changed

+179
-0
lines changed

4 files changed

+179
-0
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ objc2-core-foundation = "0.3"
4141
objc2-system-configuration = { version = "0.3", features = ["SCNetworkConfiguration"] }
4242
plist = "1.8"
4343

44+
[target.'cfg(target_os = "ios")'.dependencies]
45+
dispatch2 = "0.3"
46+
block2 = "0.6"
47+
4448
[dev-dependencies]
4549
serde_json = "1.0"
4650

src/os/ios/interface.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use crate::{interface::interface::Interface, os::unix::interface::unix_interface
55
pub fn interfaces() -> Vec<Interface> {
66
let mut ifaces: Vec<Interface> = unix_interfaces();
77

8+
let nw_iface_map = super::network::nw_interface_map();
9+
810
#[cfg(feature = "gateway")]
911
let gateway_map = crate::os::darwin::route::get_gateway_map();
1012

@@ -21,6 +23,10 @@ pub fn interfaces() -> Vec<Interface> {
2123
iface.if_type = name_type;
2224
}
2325

26+
if let Some(nw_iface) = nw_iface_map.get(&iface.name) {
27+
iface.if_type = nw_iface.if_type;
28+
}
29+
2430
#[cfg(feature = "gateway")]
2531
{
2632
if let Some(gateway) = gateway_map.get(&iface.index) {

src/os/ios/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
pub mod interface;
2+
pub mod network;

src/os/ios/network.rs

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
//! Minimal Network.framework interface enumeration
2+
3+
#![allow(non_camel_case_types)]
4+
5+
use std::collections::HashMap;
6+
use std::ffi::CStr;
7+
use std::os::raw::{c_char, c_void};
8+
use std::sync::{Arc, Condvar, Mutex};
9+
use std::time::Duration;
10+
11+
use block2::RcBlock;
12+
use dispatch2::{DispatchQueue, DispatchQueueGlobalPriority, DispatchRetained, GlobalQueueIdentifier};
13+
14+
use crate::interface::types::InterfaceType;
15+
16+
#[derive(Debug, Clone)]
17+
pub(crate) struct NWInterface {
18+
pub bsd_name: String,
19+
pub if_type: InterfaceType,
20+
#[allow(dead_code)]
21+
pub index: u32,
22+
}
23+
24+
// Link to Network.framework
25+
#[link(name = "Network", kind = "framework")]
26+
unsafe extern "C" {}
27+
28+
// Network.framework (minimal) FFI types
29+
type nw_path_monitor_t = *mut c_void;
30+
type nw_path_t = *mut c_void;
31+
type nw_interface_t = *mut c_void;
32+
33+
#[allow(dead_code)]
34+
#[repr(C)]
35+
#[derive(Clone, Copy, Debug)]
36+
enum nw_interface_type_t {
37+
other = 0,
38+
wifi = 1,
39+
cellular = 2,
40+
wired = 3,
41+
loopback = 4,
42+
}
43+
44+
unsafe extern "C" {
45+
fn nw_path_monitor_create() -> nw_path_monitor_t;
46+
fn nw_path_monitor_set_queue(monitor: nw_path_monitor_t, queue: *mut c_void);
47+
fn nw_path_monitor_set_update_handler(monitor: nw_path_monitor_t, handler: *mut c_void);
48+
fn nw_path_monitor_start(monitor: nw_path_monitor_t);
49+
fn nw_path_monitor_cancel(monitor: nw_path_monitor_t);
50+
51+
fn nw_path_enumerate_interfaces(path: nw_path_t, enumerate_block: *mut c_void);
52+
53+
fn nw_interface_get_name(interface: nw_interface_t) -> *const c_char;
54+
fn nw_interface_get_type(interface: nw_interface_t) -> nw_interface_type_t;
55+
fn nw_interface_get_index(interface: nw_interface_t) -> u32;
56+
57+
fn nw_release(obj: *mut c_void);
58+
}
59+
60+
fn map_type(t: nw_interface_type_t) -> InterfaceType {
61+
match t {
62+
nw_interface_type_t::wifi => InterfaceType::Wireless80211,
63+
nw_interface_type_t::cellular => InterfaceType::Wwanpp,
64+
nw_interface_type_t::wired => InterfaceType::Ethernet,
65+
nw_interface_type_t::loopback => InterfaceType::Loopback,
66+
nw_interface_type_t::other => InterfaceType::Unknown,
67+
}
68+
}
69+
70+
type EnumBlock = dyn Fn(*mut c_void) -> u8 + 'static;
71+
72+
/// Enumerate interfaces from Network.framework (NWPath/NWInterface)
73+
pub fn nfw_interfaces() -> Vec<NWInterface> {
74+
let shared: Arc<(Mutex<Option<Vec<NWInterface>>>, Condvar)> =
75+
Arc::new((Mutex::new(None), Condvar::new()));
76+
let shared2 = Arc::clone(&shared);
77+
78+
let queue = DispatchQueue::global_queue(GlobalQueueIdentifier::Priority(
79+
DispatchQueueGlobalPriority::Default,
80+
));
81+
82+
type UpdateHandler = dyn Fn(*mut c_void) + 'static;
83+
84+
let blk: RcBlock<UpdateHandler> = RcBlock::new(move |path_ptr: *mut c_void| {
85+
let path = path_ptr as nw_path_t;
86+
87+
let acc: Arc<Mutex<Vec<NWInterface>>> = Arc::new(Mutex::new(Vec::new()));
88+
let acc2 = Arc::clone(&acc);
89+
90+
let enum_blk: RcBlock<EnumBlock> = RcBlock::new(move |iface_ptr: *mut c_void| -> u8 {
91+
let iface = iface_ptr as nw_interface_t;
92+
if iface.is_null() {
93+
return 1;
94+
}
95+
96+
let name_ptr = unsafe { nw_interface_get_name(iface) };
97+
if name_ptr.is_null() {
98+
return 1;
99+
}
100+
101+
let name = unsafe { CStr::from_ptr(name_ptr) }
102+
.to_string_lossy()
103+
.into_owned();
104+
105+
let ty = unsafe { nw_interface_get_type(iface) };
106+
let idx = unsafe { nw_interface_get_index(iface) };
107+
108+
acc2.lock().unwrap().push(NWInterface {
109+
bsd_name: name,
110+
if_type: map_type(ty),
111+
index: idx,
112+
});
113+
114+
1
115+
});
116+
117+
let enum_ptr: *mut c_void = RcBlock::<EnumBlock>::as_ptr(&enum_blk) as *mut c_void;
118+
unsafe { nw_path_enumerate_interfaces(path, enum_ptr) };
119+
120+
// Take only the first callback result and notify
121+
let v = std::mem::take(&mut *acc.lock().unwrap());
122+
let (lock, cv) = &*shared2;
123+
let mut g = lock.lock().unwrap();
124+
if g.is_none() {
125+
*g = Some(v);
126+
cv.notify_one();
127+
}
128+
129+
// note: enum_blk lives until the end of this closure
130+
});
131+
132+
unsafe {
133+
let monitor = nw_path_monitor_create();
134+
if monitor.is_null() {
135+
return Vec::new();
136+
}
137+
138+
let q_nn = DispatchRetained::<DispatchQueue>::as_ptr(&queue);
139+
let q_ptr: *mut c_void = q_nn.as_ptr() as *mut c_void;
140+
141+
let blk_ptr: *mut c_void = RcBlock::<UpdateHandler>::as_ptr(&blk) as *mut c_void;
142+
143+
nw_path_monitor_set_queue(monitor, q_ptr);
144+
nw_path_monitor_set_update_handler(monitor, blk_ptr);
145+
nw_path_monitor_start(monitor);
146+
147+
let (lock, cv) = &*shared;
148+
149+
let guard = lock.lock().unwrap();
150+
if guard.is_none() {
151+
let _ = cv.wait_timeout(guard, Duration::from_millis(500)).unwrap();
152+
}
153+
154+
nw_path_monitor_cancel(monitor);
155+
nw_release(monitor as *mut c_void);
156+
}
157+
158+
shared.0.lock().unwrap().take().unwrap_or_default()
159+
}
160+
161+
pub fn nw_interface_map() -> HashMap<String, NWInterface> {
162+
let mut map = HashMap::new();
163+
let ifaces = nfw_interfaces();
164+
for iface in ifaces {
165+
map.insert(iface.bsd_name.clone(), iface);
166+
}
167+
map
168+
}

0 commit comments

Comments
 (0)