Feat(Linux): Integrate Linux Connectivity Feature
This commit is contained in:
+14
-6
@@ -7,14 +7,20 @@ use std::{fmt, io};
|
|||||||
|
|
||||||
pub trait Network: fmt::Debug {
|
pub trait Network: fmt::Debug {
|
||||||
/// Makes an attempt to connect to a selected wireless network with password specified.
|
/// Makes an attempt to connect to a selected wireless network with password specified.
|
||||||
fn connect(&self, password: &str) -> bool;
|
fn connect(&self, password: &str) -> Result<bool, NetworkError>;
|
||||||
|
fn disconnect(&self) -> Result<bool, NetworkError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
// #[derive(Debug)]
|
||||||
pub enum NetworkType {
|
// pub enum NetworkType {
|
||||||
WEP,
|
// WEP,
|
||||||
WPA,
|
// WPA,
|
||||||
WPA2,
|
// WPA2,
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Config<'a> {
|
||||||
|
pub interface: Option<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -23,8 +29,10 @@ pub enum NetworkError {
|
|||||||
SsidNotFound,
|
SsidNotFound,
|
||||||
OsNotSupported,
|
OsNotSupported,
|
||||||
IpAssignFailed,
|
IpAssignFailed,
|
||||||
|
AddNetworkProfileFailed,
|
||||||
IoError(io::Error),
|
IoError(io::Error),
|
||||||
FailedToConnect(String),
|
FailedToConnect(String),
|
||||||
|
FailedToDisconnect(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<io::Error> for NetworkError {
|
impl From<io::Error> for NetworkError {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use connectivity::{providers::Machine, Network, NetworkError};
|
use connectivity::{providers::Machine, Config, Network, NetworkError};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ProfileNetwork {
|
pub struct ProfileNetwork {
|
||||||
@@ -7,19 +7,22 @@ pub struct ProfileNetwork {
|
|||||||
|
|
||||||
/// Profile Network handler responsible to connect to a wireless network.
|
/// Profile Network handler responsible to connect to a wireless network.
|
||||||
impl ProfileNetwork {
|
impl ProfileNetwork {
|
||||||
pub fn new(name: &str) -> Result<Self, NetworkError> {
|
pub fn new(name: &str, config: Option<Config>) -> Result<Self, NetworkError> {
|
||||||
if !(cfg!(target_os = "linux") || cfg!(target_os = "windows")) {
|
if !(cfg!(target_os = "linux") || cfg!(target_os = "windows")) {
|
||||||
return Err(NetworkError::OsNotSupported);
|
return Err(NetworkError::OsNotSupported);
|
||||||
}
|
}
|
||||||
|
|
||||||
let handler = Machine::new(name)?;
|
let handler = Machine::new(
|
||||||
|
name,
|
||||||
|
config.map_or(None, |cfg| cfg.interface.map_or(None, |x| Some(&x))),
|
||||||
|
);
|
||||||
|
|
||||||
Ok(ProfileNetwork {
|
Ok(ProfileNetwork {
|
||||||
handler: Box::new(handler),
|
handler: Box::new(handler),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn connect(&self, password: &str) -> bool {
|
pub fn connect(&self, password: &str) -> Result<bool, NetworkError> {
|
||||||
self.handler.connect(password)
|
self.handler.connect(password)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,128 +1,50 @@
|
|||||||
extern crate tempfile;
|
use connectivity::{Network, NetworkError};
|
||||||
|
use std::process::Command;
|
||||||
use self::tempfile::NamedTempFile;
|
|
||||||
use connectivity::{Network, NetworkError, NetworkType};
|
|
||||||
use std::process::{Command, Output};
|
|
||||||
use std::{
|
|
||||||
fs::File, io::{Error, Read, Write},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Linux {
|
pub struct Linux {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub network_type: NetworkType,
|
interface: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Linux {
|
impl Linux {
|
||||||
pub fn new(name: &str) -> Result<Self, NetworkError> {
|
pub fn new(name: &str, interface: Option<&str>) -> Self {
|
||||||
match Linux::check_if_web_or_wpa(name.into()) {
|
Linux {
|
||||||
Ok(t) => match t {
|
name: name.into(),
|
||||||
NetworkType::WEP => Ok(Linux {
|
interface: interface.unwrap_or("wlan0").into(),
|
||||||
name: name.into(),
|
|
||||||
network_type: NetworkType::WEP,
|
|
||||||
}),
|
|
||||||
_ => Ok(Linux {
|
|
||||||
name: name.into(),
|
|
||||||
network_type: NetworkType::WPA,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Err(err) => Err(err),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Detects the network type of a given network.
|
|
||||||
fn check_if_web_or_wpa(name: String) -> Result<NetworkType, NetworkError> {
|
|
||||||
Command::new("nmcli")
|
|
||||||
.args(&[
|
|
||||||
"-t",
|
|
||||||
"-f",
|
|
||||||
"802-11-wireless-security.key-mgmt",
|
|
||||||
"con",
|
|
||||||
"show",
|
|
||||||
&name,
|
|
||||||
])
|
|
||||||
.output()
|
|
||||||
.map_err(|err| NetworkError::IoError(err))
|
|
||||||
.and_then(|output| {
|
|
||||||
let output = String::from_utf8_lossy(&output.stdout);
|
|
||||||
let psk = {
|
|
||||||
let mut split = output.split(':');
|
|
||||||
let _ = split.next();
|
|
||||||
match split.next() {
|
|
||||||
Some(x) => x,
|
|
||||||
None => return Err(NetworkError::SsidNotFound),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match psk.trim() {
|
|
||||||
"wpa-psk" => Ok(NetworkType::WPA),
|
|
||||||
_ => Ok(NetworkType::WEP),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn connect_to_wep_network(&self, password: &str) -> Result<Output, NetworkError> {
|
|
||||||
Ok(Command::new("iwconfig")
|
|
||||||
.args(&["wlan0", "essid", &self.name, "key", password])
|
|
||||||
.output()
|
|
||||||
.map_err(|err| NetworkError::FailedToConnect(format!("{:?}", err)))?)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn connect_to_wpa_network(&self, password: &str) -> Result<Output, NetworkError> {
|
|
||||||
let passphrase = Command::new("wpa_passphrase")
|
|
||||||
.args(&[&self.name, password])
|
|
||||||
.output()?;
|
|
||||||
|
|
||||||
let wpa_conf_file = self.create_conf_file(&String::from_utf8_lossy(&passphrase.stdout))?;
|
|
||||||
|
|
||||||
let mut file = File::open(wpa_conf_file.path())?;
|
|
||||||
let mut new_content = String::new();
|
|
||||||
file.read_to_string(&mut new_content)?;
|
|
||||||
|
|
||||||
Command::new("wpa_supplicant")
|
|
||||||
.args(&[
|
|
||||||
"-D",
|
|
||||||
"wext",
|
|
||||||
"-B",
|
|
||||||
"-i",
|
|
||||||
"wlo1",
|
|
||||||
wpa_conf_file.path().to_str().unwrap(),
|
|
||||||
])
|
|
||||||
.output()
|
|
||||||
.map_err(|err| NetworkError::FailedToConnect(format!("{:?}", err)))?;
|
|
||||||
|
|
||||||
let a = Command::new("dhclient").args(&["wlo1"]).output()?;
|
|
||||||
println!("{:?}", String::from_utf8_lossy(&a.stdout));
|
|
||||||
|
|
||||||
Ok(Command::new("dhclient")
|
|
||||||
.args(&["wlo1"])
|
|
||||||
.output()
|
|
||||||
.map_err(|_| NetworkError::IpAssignFailed)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_conf_file(&self, content: &str) -> Result<NamedTempFile, Error> {
|
|
||||||
let mut temp_file = NamedTempFile::new()?;
|
|
||||||
write!(temp_file, "{}", content)?;
|
|
||||||
|
|
||||||
Ok(temp_file)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Network for Linux {
|
impl Network for Linux {
|
||||||
fn connect(&self, password: &str) -> bool {
|
fn connect(&self, password: &str) -> Result<bool, NetworkError> {
|
||||||
match self.network_type {
|
let output = Command::new("nmcli")
|
||||||
NetworkType::WEP => self
|
.args(&[
|
||||||
.connect_to_wep_network(password)
|
"d",
|
||||||
.map_err(|_err| false)
|
"wifi",
|
||||||
.unwrap()
|
"connect",
|
||||||
.status
|
&self.name,
|
||||||
.success(),
|
"password",
|
||||||
_ => self
|
&password,
|
||||||
.connect_to_wpa_network(password)
|
"ifname",
|
||||||
.map_err(|_err| false)
|
&self.interface,
|
||||||
.unwrap()
|
])
|
||||||
.status
|
.output()
|
||||||
.success(),
|
.map_err(|err| NetworkError::FailedToConnect(format!("{}", err)))?;
|
||||||
}
|
|
||||||
|
Ok(String::from_utf8_lossy(&output.stdout)
|
||||||
|
.as_ref()
|
||||||
|
.contains("successfully activated"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disconnect(&self) -> Result<bool, NetworkError> {
|
||||||
|
let output = Command::new("nmcli")
|
||||||
|
.args(&["d", "disconnect", "ifname", &self.interface])
|
||||||
|
.output()
|
||||||
|
.map_err(|err| NetworkError::FailedToDisconnect(format!("{}", err)))?;
|
||||||
|
|
||||||
|
Ok(String::from_utf8_lossy(&output.stdout)
|
||||||
|
.as_ref()
|
||||||
|
.contains("disconnect"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use connectivity::handlers::NetworkXmlProfileHandler;
|
use connectivity::handlers::NetworkXmlProfileHandler;
|
||||||
use connectivity::Network;
|
use connectivity::{Network, NetworkError};
|
||||||
|
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
@@ -11,14 +11,14 @@ pub(crate) struct Windows {
|
|||||||
|
|
||||||
impl Windows {
|
impl Windows {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub fn new(name: &str) -> Result<Self, io::Error> {
|
pub fn new(name: &str, interface: Option<&str>) -> Self {
|
||||||
Ok(Windows {
|
Windows {
|
||||||
name: String::from(name),
|
name: String::from(name),
|
||||||
output_xml_path: OUTPUT_XML_FILE_PATH.into(),
|
output_xml_path: OUTPUT_XML_FILE_PATH.into(),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add_profile(&self, password: &str) -> bool {
|
pub(crate) fn add_profile(&self, password: &str) -> Result<(), NetworkError> {
|
||||||
let mut handler = NetworkXmlProfileHandler::new();
|
let mut handler = NetworkXmlProfileHandler::new();
|
||||||
handler.content = handler
|
handler.content = handler
|
||||||
.content
|
.content
|
||||||
@@ -26,12 +26,12 @@ impl Windows {
|
|||||||
.replace("{password}", password);
|
.replace("{password}", password);
|
||||||
|
|
||||||
// Write details to new xml file
|
// Write details to new xml file
|
||||||
if let Err(_) = handler.to_file(&self.output_xml_path).map_err(|_err| false) {
|
let _ = handler
|
||||||
return false;
|
.to_file(&self.output_xml_path)
|
||||||
}
|
.map_err(|err| NetworkError::IoError(err))?;
|
||||||
|
|
||||||
// Add the network profile
|
// Add the network profile
|
||||||
if let Err(_) = Command::new("netsh")
|
Command::new("netsh")
|
||||||
.args(&[
|
.args(&[
|
||||||
"wlan",
|
"wlan",
|
||||||
"add",
|
"add",
|
||||||
@@ -39,27 +39,34 @@ impl Windows {
|
|||||||
&format!("filename={}", self.output_xml_path),
|
&format!("filename={}", self.output_xml_path),
|
||||||
])
|
])
|
||||||
.output()
|
.output()
|
||||||
{
|
.map_err(|_| NetworkError::AddNetworkProfileFailed)?;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Network for Windows {
|
impl Network for Windows {
|
||||||
fn connect(&self, password: &str) -> bool {
|
fn connect(&self, password: &str) -> Result<bool, NetworkError> {
|
||||||
if self.add_profile(password) == false {
|
self.add_profile(password)?;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let output = Command::new("netsh")
|
let output = Command::new("netsh")
|
||||||
.args(&["wlan", "connect", &format!("name={}", self.name)])
|
.args(&["wlan", "connect", &format!("name={}", self.name)])
|
||||||
.output();
|
.output()
|
||||||
|
.map_err(|err| NetworkError::FailedToConnect(format!("{}", err)))?;
|
||||||
|
|
||||||
match output {
|
Ok(String::from_utf8_lossy(&output.stdout)
|
||||||
Ok(res) => res.status.success(),
|
.as_ref()
|
||||||
Err(_) => false,
|
.contains("successfully activated"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn disconnect(&self) -> Result<bool, NetworkError> {
|
||||||
|
let output = Command::new("netsh")
|
||||||
|
.args(&["wlan", "disconnect"])
|
||||||
|
.output()
|
||||||
|
.map_err(|err| NetworkError::FailedToDisconnect(format!("{}", err)))?;
|
||||||
|
|
||||||
|
Ok(String::from_utf8_lossy(&output.stdout)
|
||||||
|
.as_ref()
|
||||||
|
.contains("disconnect"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-3
@@ -1,7 +1,7 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
mod connectivity;
|
mod connectivity;
|
||||||
pub use connectivity::{profile_network::ProfileNetwork as WiFi, *};
|
pub use connectivity::{profile_network::ProfileNetwork as WiFi, Config};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
@@ -9,7 +9,12 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn connect_to_wifi_failed() {
|
fn connect_to_wifi_failed() {
|
||||||
let wifi = WiFi::new("hello").unwrap();
|
let config = Some(Config {
|
||||||
assert_eq!(wifi.connect("password"), false);
|
interface: Some(String::from("wlo1")),
|
||||||
|
});
|
||||||
|
|
||||||
|
let wifi = WiFi::new("hello", config).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(wifi.connect("password").unwrap(), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-3
@@ -1,9 +1,24 @@
|
|||||||
mod connectivity;
|
mod connectivity;
|
||||||
pub use connectivity::{profile_network::ProfileNetwork as WiFi, NetworkError};
|
pub use connectivity::{profile_network::ProfileNetwork as WiFi, Config, NetworkError};
|
||||||
|
|
||||||
fn main() -> Result<(), NetworkError> {
|
fn main() -> Result<(), NetworkError> {
|
||||||
let wifi = WiFi::new("AndroidAPSD")?;
|
let config = Some(Config {
|
||||||
println!("{}", wifi.connect("belm4235"));
|
interface: Some("wlo1"),
|
||||||
|
});
|
||||||
|
|
||||||
|
let wifi = WiFi::new("AndroidAPSD", config)?;
|
||||||
|
|
||||||
|
match wifi.connect("belm4235") {
|
||||||
|
Ok(result) => println!(
|
||||||
|
"{}",
|
||||||
|
if result == true {
|
||||||
|
"Connection Successfull."
|
||||||
|
} else {
|
||||||
|
"Invalid password."
|
||||||
|
}
|
||||||
|
),
|
||||||
|
Err(err) => println!("The following error occurred: {:?}", err),
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user