Feat(Linux): Integrate Linux Connectivity Feature

This commit is contained in:
Tochukwu Nkemdilim
2018-07-02 18:35:43 +01:00
parent bfd9385844
commit c46bf7f494
6 changed files with 112 additions and 152 deletions
+14 -6
View File
@@ -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 {
+7 -4
View File
@@ -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)
} }
} }
+36 -114
View File
@@ -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"))
} }
} }
+29 -22
View File
@@ -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
View File
@@ -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
View File
@@ -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(())
} }