librefi_rs/src/connectors/macos.rs

111 lines
3.4 KiB
Rust

// shell.exec("{networksetup,airport}") goes brrrr
use crate::connectors::types::{
Connects, ListsNetworkInterfaces, ListsNetworks, Network, NetworkInterface,
};
use crate::connectors::utils::call_exec;
use plist;
use regex::Regex;
use serde::Deserialize;
#[derive(Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub struct AirportNetwork {
#[serde(rename = "SSID_STR")]
ssid: String,
bssid: String,
channel: u8,
rssi: i8,
}
impl From<AirportNetwork> for Network {
fn from(net: AirportNetwork) -> Network {
Network {
ssid: net.ssid.clone(),
bssid: Some(net.bssid.clone()),
channel: Some(net.channel.clone()),
rssi: Some(net.rssi.clone()),
..Default::default()
}
}
}
pub struct MacOSConnector {}
impl MacOSConnector {
fn call_airport(&self, args: Vec<&str>) -> Result<Vec<u8>, String> {
let mut args = args.clone();
args.push("--xml");
call_exec(
"airport",
"/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport",
args)
}
fn call_networksetup(&self, args: Vec<&str>) -> Result<Vec<u8>, String> {
call_exec("networksetup", "/usr/sbin/networksetup", args)
}
}
impl Connects for MacOSConnector {
fn connect_to_network(
&self,
iface: &NetworkInterface,
net: &Network,
psk: Option<&str>,
) -> Result<bool, String> {
let mut args = vec!["-setairportnetwork", &iface.machine_name, &net.ssid];
if let Some(p) = psk {
args.push(p);
}
match self.call_networksetup(args) {
Ok(_) => Ok(true),
Err(e) => Err(e),
}
}
}
impl ListsNetworks for MacOSConnector {
fn list_networks(&self, _iface: &NetworkInterface) -> Result<Vec<Network>, String> {
// this should use the specific network interface, but airport won't let us specify it
let output = self
.call_airport(vec!["-s"])
.expect("airport idk what actually");
let res: Vec<AirportNetwork> = plist::from_bytes(&output).expect("airport returned shit");
let networks = res.into_iter().map(|net| Network::from(net)).collect();
Ok(networks)
}
}
impl ListsNetworkInterfaces for MacOSConnector {
fn list_network_interfaces(&self) -> Result<Vec<NetworkInterface>, String> {
// order returns both names
let output = String::from_utf8(
self.call_networksetup(vec!["-listnetworkserviceorder"])
.expect("networksetup idk what actually"),
)
.expect("networksetup returned non-utf8 chars???");
if output.len() == 0 {
return Err("networksetup returned nothing. Is the Wi-Fi turned on?".to_string());
}
lazy_static! {
static ref RE: Regex = Regex::new(
r"(?x)
\((?P<order>\d+|\*)\)\ (?P<human>[^\n]+)\n
\(Hardware\ Port:\ .+?,\ Device:\ (?P<unix>[a-z]+\d+)\)
"
)
.unwrap();
}
let mut ifaces: Vec<NetworkInterface> = vec![];
for iface in RE.captures_iter(&output) {
ifaces.push(NetworkInterface {
enabled: &iface["order"] != "*",
machine_name: iface["unix"].to_string(),
human_name: Some(iface["human"].to_string()),
});
}
Ok(ifaces)
}
}