Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ objc2-core-foundation = "0.3"
objc2-system-configuration = { version = "0.3", features = ["SCNetworkConfiguration"] }
plist = "1.8"

[target.'cfg(target_os = "ios")'.dependencies]
dispatch2 = "0.3"
block2 = "0.6"

[dev-dependencies]
serde_json = "1.0"

Expand Down
6 changes: 6 additions & 0 deletions src/os/ios/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use crate::{interface::interface::Interface, os::unix::interface::unix_interface
pub fn interfaces() -> Vec<Interface> {
let mut ifaces: Vec<Interface> = unix_interfaces();

let nw_iface_map = super::network::nw_interface_map();

#[cfg(feature = "gateway")]
let gateway_map = crate::os::darwin::route::get_gateway_map();

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

if let Some(nw_iface) = nw_iface_map.get(&iface.name) {
iface.if_type = nw_iface.if_type;
}

#[cfg(feature = "gateway")]
{
if let Some(gateway) = gateway_map.get(&iface.index) {
Expand Down
1 change: 1 addition & 0 deletions src/os/ios/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod interface;
pub mod network;
170 changes: 170 additions & 0 deletions src/os/ios/network.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
//! Minimal Network.framework interface enumeration

#![allow(non_camel_case_types)]

use std::collections::HashMap;
use std::ffi::CStr;
use std::os::raw::{c_char, c_void};
use std::sync::{Arc, Condvar, Mutex};
use std::time::Duration;

use block2::RcBlock;
use dispatch2::{
DispatchQueue, DispatchQueueGlobalPriority, DispatchRetained, GlobalQueueIdentifier,
};

use crate::interface::types::InterfaceType;

#[derive(Debug, Clone)]
pub(crate) struct NWInterface {
pub bsd_name: String,
pub if_type: InterfaceType,
#[allow(dead_code)]
pub index: u32,
}

// Link to Network.framework
#[link(name = "Network", kind = "framework")]
unsafe extern "C" {}

// Network.framework (minimal) FFI types
type nw_path_monitor_t = *mut c_void;
type nw_path_t = *mut c_void;
type nw_interface_t = *mut c_void;

#[allow(dead_code)]
#[repr(C)]
#[derive(Clone, Copy, Debug)]
enum nw_interface_type_t {
other = 0,
wifi = 1,
cellular = 2,
wired = 3,
loopback = 4,
}

unsafe extern "C" {
fn nw_path_monitor_create() -> nw_path_monitor_t;
fn nw_path_monitor_set_queue(monitor: nw_path_monitor_t, queue: *mut c_void);
fn nw_path_monitor_set_update_handler(monitor: nw_path_monitor_t, handler: *mut c_void);
fn nw_path_monitor_start(monitor: nw_path_monitor_t);
fn nw_path_monitor_cancel(monitor: nw_path_monitor_t);

fn nw_path_enumerate_interfaces(path: nw_path_t, enumerate_block: *mut c_void);

fn nw_interface_get_name(interface: nw_interface_t) -> *const c_char;
fn nw_interface_get_type(interface: nw_interface_t) -> nw_interface_type_t;
fn nw_interface_get_index(interface: nw_interface_t) -> u32;

fn nw_release(obj: *mut c_void);
}

fn map_type(t: nw_interface_type_t) -> InterfaceType {
match t {
nw_interface_type_t::wifi => InterfaceType::Wireless80211,
nw_interface_type_t::cellular => InterfaceType::Wwanpp,
nw_interface_type_t::wired => InterfaceType::Ethernet,
nw_interface_type_t::loopback => InterfaceType::Loopback,
nw_interface_type_t::other => InterfaceType::Unknown,
}
}

type EnumBlock = dyn Fn(*mut c_void) -> u8 + 'static;

/// Enumerate interfaces from Network.framework (NWPath/NWInterface)
pub fn nfw_interfaces() -> Vec<NWInterface> {
let shared: Arc<(Mutex<Option<Vec<NWInterface>>>, Condvar)> =
Arc::new((Mutex::new(None), Condvar::new()));
let shared2 = Arc::clone(&shared);

let queue = DispatchQueue::global_queue(GlobalQueueIdentifier::Priority(
DispatchQueueGlobalPriority::Default,
));

type UpdateHandler = dyn Fn(*mut c_void) + 'static;

let blk: RcBlock<UpdateHandler> = RcBlock::new(move |path_ptr: *mut c_void| {
let path = path_ptr as nw_path_t;

let acc: Arc<Mutex<Vec<NWInterface>>> = Arc::new(Mutex::new(Vec::new()));
let acc2 = Arc::clone(&acc);

let enum_blk: RcBlock<EnumBlock> = RcBlock::new(move |iface_ptr: *mut c_void| -> u8 {
let iface = iface_ptr as nw_interface_t;
if iface.is_null() {
return 1;
}

let name_ptr = unsafe { nw_interface_get_name(iface) };
if name_ptr.is_null() {
return 1;
}

let name = unsafe { CStr::from_ptr(name_ptr) }
.to_string_lossy()
.into_owned();

let ty = unsafe { nw_interface_get_type(iface) };
let idx = unsafe { nw_interface_get_index(iface) };

acc2.lock().unwrap().push(NWInterface {
bsd_name: name,
if_type: map_type(ty),
index: idx,
});

1
});

let enum_ptr: *mut c_void = RcBlock::<EnumBlock>::as_ptr(&enum_blk) as *mut c_void;
unsafe { nw_path_enumerate_interfaces(path, enum_ptr) };

// Take only the first callback result and notify
let v = std::mem::take(&mut *acc.lock().unwrap());
let (lock, cv) = &*shared2;
let mut g = lock.lock().unwrap();
if g.is_none() {
*g = Some(v);
cv.notify_one();
}

// note: enum_blk lives until the end of this closure
});

unsafe {
let monitor = nw_path_monitor_create();
if monitor.is_null() {
return Vec::new();
}

let q_nn = DispatchRetained::<DispatchQueue>::as_ptr(&queue);
let q_ptr: *mut c_void = q_nn.as_ptr() as *mut c_void;

let blk_ptr: *mut c_void = RcBlock::<UpdateHandler>::as_ptr(&blk) as *mut c_void;

nw_path_monitor_set_queue(monitor, q_ptr);
nw_path_monitor_set_update_handler(monitor, blk_ptr);
nw_path_monitor_start(monitor);

let (lock, cv) = &*shared;

let guard = lock.lock().unwrap();
if guard.is_none() {
let _ = cv.wait_timeout(guard, Duration::from_millis(500)).unwrap();
}

nw_path_monitor_cancel(monitor);
nw_release(monitor as *mut c_void);
}

shared.0.lock().unwrap().take().unwrap_or_default()
}

pub fn nw_interface_map() -> HashMap<String, NWInterface> {
let mut map = HashMap::new();
let ifaces = nfw_interfaces();
for iface in ifaces {
map.insert(iface.bsd_name.clone(), iface);
}
map
}