2021-12-26 13:55:29 +01:00
|
|
|
// shell.exec("{networksetup,airport}") goes brrrr
|
|
|
|
|
|
|
|
use crate::connectors::types::{Connector, Network, NetworkInterface};
|
|
|
|
use std::process::Command;
|
|
|
|
extern crate plist;
|
|
|
|
use serde::{Deserialize};
|
|
|
|
use regex::{Regex};
|
|
|
|
|
|
|
|
#[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 output = Command::new("/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport")
|
|
|
|
.args(&args)
|
|
|
|
.arg("--xml")
|
|
|
|
.output()
|
|
|
|
.expect("calling airport failed");
|
|
|
|
if !output.status.success() {
|
|
|
|
panic!("airport returned {:} status", output.status);
|
|
|
|
}
|
|
|
|
Ok(output.stdout)
|
|
|
|
}
|
|
|
|
|
2022-01-02 22:15:23 +01:00
|
|
|
fn call_networksetup(&self, args: Vec<&str>) -> Result<Vec<u8>, String> {
|
2021-12-26 13:55:29 +01:00
|
|
|
let output = Command::new("/usr/sbin/networksetup")
|
2022-01-02 22:15:23 +01:00
|
|
|
.args(&args)
|
2021-12-26 13:55:29 +01:00
|
|
|
.output()
|
|
|
|
.expect("calling networksetup failed");
|
2022-01-02 22:15:23 +01:00
|
|
|
match output.status.success() {
|
|
|
|
true => Ok(output.stdout),
|
|
|
|
false => Err(match output.status.code() {
|
|
|
|
Some(code) => format!("networksetup exited with {:}: {:?}", code, output.stdout),
|
|
|
|
None => format!("networksetup exited by signal"),
|
|
|
|
}),
|
2021-12-26 13:55:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Connector for MacOSConnector {
|
2022-01-02 22:15:23 +01:00
|
|
|
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),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2021-12-26 13:55:29 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn list_network_interfaces(&self) -> Result<Vec<NetworkInterface>, String> {
|
|
|
|
// order returns both names
|
|
|
|
let output = String::from_utf8(
|
2022-01-02 22:15:23 +01:00
|
|
|
self.call_networksetup(vec!["-listnetworkserviceorder"])
|
2021-12-26 13:55:29 +01:00
|
|
|
.expect("networksetup idk what actually"))
|
|
|
|
.expect("networksetup returned non-utf8 chars???");
|
2022-01-02 22:15:23 +01:00
|
|
|
if output.len() == 0 {
|
|
|
|
return Err("networksetup returned nothing. Is the Wi-Fi turned on?".to_string());
|
|
|
|
}
|
2021-12-26 13:55:29 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|