etcnet, networkmanager connectors
This commit is contained in:
parent
c3732ba4bc
commit
77379b9a2e
5
Cargo.lock
generated
5
Cargo.lock
generated
|
@ -342,6 +342,7 @@ dependencies = [
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -758,9 +759,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.73"
|
version = "1.0.74"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5"
|
checksum = "ee2bb9cd061c5865d345bb02ca49fcef1391741b672b54a0bf7b679badec3142"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa 1.0.1",
|
"itoa 1.0.1",
|
||||||
"ryu",
|
"ryu",
|
||||||
|
|
|
@ -12,4 +12,5 @@ plist = { version = "1.3.1" }
|
||||||
regex = { version = "1.5.4" }
|
regex = { version = "1.5.4" }
|
||||||
reqwest = { version = "0.11", features = ["json"] }
|
reqwest = { version = "0.11", features = ["json"] }
|
||||||
serde = { version = "1.0.132", features = ["derive"] }
|
serde = { version = "1.0.132", features = ["derive"] }
|
||||||
|
serde_json = "1.0.74"
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
|
33
src/connectors/etcnet.rs
Normal file
33
src/connectors/etcnet.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
use crate::connectors::types::{ListsNetworkInterfaces, NetworkInterface};
|
||||||
|
use crate::connectors::utils::call_exec;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde_json;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct ENInterface {
|
||||||
|
ifname: String,
|
||||||
|
flags: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ENInterface> for NetworkInterface {
|
||||||
|
fn from(inf: ENInterface) -> Self {
|
||||||
|
NetworkInterface {
|
||||||
|
enabled: inf.flags.contains(&String::from("UP")),
|
||||||
|
machine_name: inf.ifname.clone(),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EtcNetConnector {}
|
||||||
|
|
||||||
|
impl ListsNetworkInterfaces for EtcNetConnector {
|
||||||
|
fn list_network_interfaces(&self) -> Result<Vec<NetworkInterface>, String> {
|
||||||
|
let output = call_exec("ip", "ip", vec!["--json", "address"]).expect("calling ip failed");
|
||||||
|
let ifaces: Vec<ENInterface> = serde_json::from_slice(&output).expect("ip returned shit");
|
||||||
|
Ok(ifaces
|
||||||
|
.into_iter()
|
||||||
|
.map(|inf| NetworkInterface::from(inf))
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,12 @@
|
||||||
// shell.exec("{networksetup,airport}") goes brrrr
|
// shell.exec("{networksetup,airport}") goes brrrr
|
||||||
|
|
||||||
use crate::connectors::types::{Connector, Network, NetworkInterface};
|
use crate::connectors::types::{
|
||||||
use std::process::Command;
|
Connects, ListsNetworkInterfaces, ListsNetworks, Network, NetworkInterface,
|
||||||
extern crate plist;
|
};
|
||||||
use serde::{Deserialize};
|
use crate::connectors::utils::call_exec;
|
||||||
use regex::{Regex};
|
use plist;
|
||||||
|
use regex::Regex;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
|
@ -32,34 +34,26 @@ pub struct MacOSConnector {}
|
||||||
|
|
||||||
impl MacOSConnector {
|
impl MacOSConnector {
|
||||||
fn call_airport(&self, args: Vec<&str>) -> Result<Vec<u8>, String> {
|
fn call_airport(&self, args: Vec<&str>) -> Result<Vec<u8>, String> {
|
||||||
let output = Command::new("/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport")
|
let mut args = args.clone();
|
||||||
.args(&args)
|
args.push("--xml");
|
||||||
.arg("--xml")
|
call_exec(
|
||||||
.output()
|
"airport",
|
||||||
.expect("calling airport failed");
|
"/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport",
|
||||||
if !output.status.success() {
|
args)
|
||||||
panic!("airport returned {:} status", output.status);
|
|
||||||
}
|
|
||||||
Ok(output.stdout)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_networksetup(&self, args: Vec<&str>) -> Result<Vec<u8>, String> {
|
fn call_networksetup(&self, args: Vec<&str>) -> Result<Vec<u8>, String> {
|
||||||
let output = Command::new("/usr/sbin/networksetup")
|
call_exec("networksetup", "/usr/sbin/networksetup", args)
|
||||||
.args(&args)
|
|
||||||
.output()
|
|
||||||
.expect("calling networksetup failed");
|
|
||||||
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"),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Connector for MacOSConnector {
|
impl Connects for MacOSConnector {
|
||||||
fn connect_to_network(&self, iface: &NetworkInterface, net: &Network, psk: Option<&str>) -> Result<bool, String> {
|
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];
|
let mut args = vec!["-setairportnetwork", &iface.machine_name, &net.ssid];
|
||||||
if let Some(p) = psk {
|
if let Some(p) = psk {
|
||||||
args.push(p);
|
args.push(p);
|
||||||
|
@ -69,30 +63,39 @@ impl Connector for MacOSConnector {
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListsNetworks for MacOSConnector {
|
||||||
fn list_networks(&self, _iface: &NetworkInterface) -> Result<Vec<Network>, String> {
|
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
|
// this should use the specific network interface, but airport won't let us specify it
|
||||||
let output = self.call_airport(vec!["-s"])
|
let output = self
|
||||||
|
.call_airport(vec!["-s"])
|
||||||
.expect("airport idk what actually");
|
.expect("airport idk what actually");
|
||||||
let res: Vec<AirportNetwork> = plist::from_bytes(&output).expect("airport returned shit");
|
let res: Vec<AirportNetwork> = plist::from_bytes(&output).expect("airport returned shit");
|
||||||
let networks = res.into_iter().map(|net| Network::from(net)).collect();
|
let networks = res.into_iter().map(|net| Network::from(net)).collect();
|
||||||
Ok(networks)
|
Ok(networks)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListsNetworkInterfaces for MacOSConnector {
|
||||||
fn list_network_interfaces(&self) -> Result<Vec<NetworkInterface>, String> {
|
fn list_network_interfaces(&self) -> Result<Vec<NetworkInterface>, String> {
|
||||||
// order returns both names
|
// order returns both names
|
||||||
let output = String::from_utf8(
|
let output = String::from_utf8(
|
||||||
self.call_networksetup(vec!["-listnetworkserviceorder"])
|
self.call_networksetup(vec!["-listnetworkserviceorder"])
|
||||||
.expect("networksetup idk what actually"))
|
.expect("networksetup idk what actually"),
|
||||||
|
)
|
||||||
.expect("networksetup returned non-utf8 chars???");
|
.expect("networksetup returned non-utf8 chars???");
|
||||||
if output.len() == 0 {
|
if output.len() == 0 {
|
||||||
return Err("networksetup returned nothing. Is the Wi-Fi turned on?".to_string());
|
return Err("networksetup returned nothing. Is the Wi-Fi turned on?".to_string());
|
||||||
}
|
}
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref RE: Regex = Regex::new(r"(?x)
|
static ref RE: Regex = Regex::new(
|
||||||
|
r"(?x)
|
||||||
\((?P<order>\d+|\*)\)\ (?P<human>[^\n]+)\n
|
\((?P<order>\d+|\*)\)\ (?P<human>[^\n]+)\n
|
||||||
\(Hardware\ Port:\ .+?,\ Device:\ (?P<unix>[a-z]+\d+)\)
|
\(Hardware\ Port:\ .+?,\ Device:\ (?P<unix>[a-z]+\d+)\)
|
||||||
").unwrap();
|
"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
let mut ifaces: Vec<NetworkInterface> = vec![];
|
let mut ifaces: Vec<NetworkInterface> = vec![];
|
||||||
for iface in RE.captures_iter(&output) {
|
for iface in RE.captures_iter(&output) {
|
||||||
|
|
|
@ -1,2 +1,5 @@
|
||||||
|
pub mod etcnet;
|
||||||
pub mod macos;
|
pub mod macos;
|
||||||
|
pub mod networkmanager;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
pub mod utils;
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
use crate::connectors::types::{
|
||||||
|
Connects, ListsNetworkInterfaces, ListsNetworks, Network, NetworkInterface,
|
||||||
|
};
|
||||||
|
use crate::connectors::utils::call_exec;
|
||||||
|
use std::borrow::BorrowMut;
|
||||||
|
use std::str::Split;
|
||||||
|
|
||||||
|
pub struct NetworkManagerConnector {}
|
||||||
|
|
||||||
|
impl NetworkManagerConnector {
|
||||||
|
fn call_nmcli(&self, add_args: Vec<&str>) -> Result<Vec<u8>, String> {
|
||||||
|
let args = vec!["-t", "-m", "tabular", "-c", "no", "-o", "-e", "yes"]
|
||||||
|
.into_iter()
|
||||||
|
.chain(add_args.into_iter())
|
||||||
|
.collect();
|
||||||
|
call_exec("networkmanager", "nmcli", args)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_nmcli(&self, add_args: Vec<&str>) -> Result<Vec<Vec<String>>, String> {
|
||||||
|
let output = self
|
||||||
|
.call_nmcli(add_args)
|
||||||
|
.expect("calling networkmanager failed");
|
||||||
|
let res = String::from_utf8(output).expect("networkmanager returned non-utf8");
|
||||||
|
let lines: Vec<&str> = res.split("\n").filter(|ln| !ln.is_empty()).collect();
|
||||||
|
let lines_with_fields: Vec<Split<&str>> = lines.into_iter().map(|line| line.split(":")).collect();
|
||||||
|
let mut parsed: Vec<Vec<String>> = vec![];
|
||||||
|
for old_fields_immut in lines_with_fields.clone() {
|
||||||
|
let mut old_fields = old_fields_immut;
|
||||||
|
let mut new_fields: Vec<String> = vec![];
|
||||||
|
let mut continuation = false;
|
||||||
|
loop {
|
||||||
|
match old_fields.next() {
|
||||||
|
Some(field) => {
|
||||||
|
let mut new_field = match continuation {
|
||||||
|
true => new_fields.pop().expect("new field").to_string(),
|
||||||
|
false => "".to_string(),
|
||||||
|
};
|
||||||
|
if continuation {
|
||||||
|
new_field.push(':');
|
||||||
|
}
|
||||||
|
new_field.push_str(match field.strip_suffix("\\") {
|
||||||
|
Some(stripped) => stripped,
|
||||||
|
None => field,
|
||||||
|
});
|
||||||
|
new_fields.push(new_field);
|
||||||
|
continuation = field.ends_with("\\");
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
let parsed_line = new_fields;
|
||||||
|
parsed.push(parsed_line);
|
||||||
|
break
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(parsed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListsNetworkInterfaces for NetworkManagerConnector {
|
||||||
|
fn list_network_interfaces(&self) -> Result<Vec<NetworkInterface>, String> {
|
||||||
|
let iface_list = match self.get_nmcli(
|
||||||
|
vec!["-f", "DEVICE,TYPE,STATE,CONNECTION", "device"],
|
||||||
|
) {
|
||||||
|
Ok(list) => list,
|
||||||
|
Err(err) => return Err(err),
|
||||||
|
};
|
||||||
|
let ifaces = iface_list.into_iter().map(|ifa| NetworkInterface {
|
||||||
|
machine_name: ifa[0].clone(),
|
||||||
|
enabled: ifa[2] != "unavailable",
|
||||||
|
..Default::default()
|
||||||
|
}).collect();
|
||||||
|
Ok(ifaces)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListsNetworks for NetworkManagerConnector {
|
||||||
|
fn list_networks(&self, iface: &NetworkInterface) -> Result<Vec<Network>, String> {
|
||||||
|
let net_list = match self.get_nmcli(vec![
|
||||||
|
"-f", "SSID,BSSID,CHAN,SIGNAL,WPA-FLAGS,IN-USE",
|
||||||
|
"device", "wifi", "list", "ifname", &iface.machine_name],
|
||||||
|
) {
|
||||||
|
Ok(list) => list,
|
||||||
|
Err(err) => return Err(err),
|
||||||
|
};
|
||||||
|
let nets = net_list.into_iter().map(|net| Network {
|
||||||
|
ssid: net[0].to_string(),
|
||||||
|
bssid: Some(net[1].to_string()),
|
||||||
|
channel: Some(net[2].parse::<u8>().unwrap()),
|
||||||
|
rssi: Some(net[3].parse::<i8>().unwrap()),
|
||||||
|
..Default::default()
|
||||||
|
}).collect();
|
||||||
|
Ok(nets)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,18 @@
|
||||||
pub trait Connector {
|
pub trait Connects {
|
||||||
fn connect_to_network(&self, iface: &NetworkInterface, net: &Network, psk: Option<&str>) -> Result<bool, String>;
|
fn connect_to_network(
|
||||||
|
&self,
|
||||||
|
iface: &NetworkInterface,
|
||||||
|
net: &Network,
|
||||||
|
psk: Option<&str>,
|
||||||
|
) -> Result<bool, String>;
|
||||||
|
}
|
||||||
|
pub trait ListsNetworks {
|
||||||
fn list_networks(&self, iface: &NetworkInterface) -> Result<Vec<Network>, String>;
|
fn list_networks(&self, iface: &NetworkInterface) -> Result<Vec<Network>, String>;
|
||||||
|
}
|
||||||
|
pub trait ListsNetworkInterfaces {
|
||||||
fn list_network_interfaces(&self) -> Result<Vec<NetworkInterface>, String>;
|
fn list_network_interfaces(&self) -> Result<Vec<NetworkInterface>, String>;
|
||||||
}
|
}
|
||||||
|
pub trait Connector: Connects + ListsNetworks + ListsNetworkInterfaces {}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Network {
|
pub struct Network {
|
||||||
|
|
15
src/connectors/utils.rs
Normal file
15
src/connectors/utils.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
pub fn call_exec(exec_name: &str, exec_path: &str, args: Vec<&str>) -> Result<Vec<u8>, String> {
|
||||||
|
let output = Command::new(exec_path)
|
||||||
|
.args(&args)
|
||||||
|
.output()
|
||||||
|
.expect(&format!("calling {} failed smh", exec_name));
|
||||||
|
match output.status.success() {
|
||||||
|
true => Ok(output.stdout),
|
||||||
|
false => Err(match output.status.code() {
|
||||||
|
Some(code) => format!("{} exited with {:}: {:?}", exec_name, code, output.stdout),
|
||||||
|
None => format!("{} exited by signal", exec_name),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
26
src/main.rs
26
src/main.rs
|
@ -2,34 +2,46 @@
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
mod connection_check;
|
mod connection_check;
|
||||||
mod connectors;
|
mod connectors;
|
||||||
use connectors::types::{Connector};
|
use crate::connectors::types::{Connects, ListsNetworkInterfaces, ListsNetworks};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
// let ip4 = connection_check::ipv4_info().await?;
|
// let ip4 = connection_check::ipv4_info().await?;
|
||||||
// println!("{:?}", ip4);
|
// println!("{:?}", ip4);
|
||||||
|
|
||||||
let connector = connectors::macos::MacOSConnector {};
|
let connector = connectors::networkmanager::NetworkManagerConnector {};
|
||||||
|
|
||||||
let ifaces = connector.list_network_interfaces().expect("network interface listing errored");
|
let ifaces = connector
|
||||||
|
.list_network_interfaces()
|
||||||
|
.expect("network interface listing errored");
|
||||||
|
|
||||||
for iface in &ifaces {
|
for iface in &ifaces {
|
||||||
println!("{:?}", iface);
|
println!("{:?}", iface);
|
||||||
}
|
}
|
||||||
|
|
||||||
let iface = ifaces.into_iter().find(|f| f.human_name == Some("Wi-Fi".to_string()))
|
let iface = ifaces
|
||||||
|
.into_iter()
|
||||||
|
.find(|f| f.machine_name == "wlp2s0")
|
||||||
.expect("Wi-Fi interface not found");
|
.expect("Wi-Fi interface not found");
|
||||||
|
|
||||||
let networks = connector.list_networks(&iface).expect("network listing errored");
|
let networks = connector
|
||||||
|
.list_networks(&iface)
|
||||||
|
.expect("network listing errored");
|
||||||
|
|
||||||
for net in &networks {
|
for net in &networks {
|
||||||
println!("{:?}", net);
|
println!("{:?}", net);
|
||||||
}
|
}
|
||||||
|
|
||||||
let network = networks.into_iter().find(|net| net.ssid == "test")
|
/*
|
||||||
|
let network = networks
|
||||||
|
.into_iter()
|
||||||
|
.find(|net| net.ssid == "test")
|
||||||
.expect("network not found");
|
.expect("network not found");
|
||||||
|
|
||||||
connector.connect_to_network(&iface, &network, None).expect("could not connect to network");
|
connector
|
||||||
|
.connect_to_network(&iface, &network, None)
|
||||||
|
.expect("could not connect to network");
|
||||||
|
*/
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue