diff --git a/src/connectivity/mod.rs b/src/connectivity/mod.rs index 27e4b15..98edea6 100644 --- a/src/connectivity/mod.rs +++ b/src/connectivity/mod.rs @@ -7,14 +7,20 @@ use std::{fmt, io}; pub trait Network: fmt::Debug { /// 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; + fn disconnect(&self) -> Result; } -#[derive(Debug)] -pub enum NetworkType { - WEP, - WPA, - WPA2, +// #[derive(Debug)] +// pub enum NetworkType { +// WEP, +// WPA, +// WPA2, +// } + +#[derive(Debug, Clone)] +pub struct Config<'a> { + pub interface: Option<&'a str>, } #[derive(Debug)] @@ -23,8 +29,10 @@ pub enum NetworkError { SsidNotFound, OsNotSupported, IpAssignFailed, + AddNetworkProfileFailed, IoError(io::Error), FailedToConnect(String), + FailedToDisconnect(String), } impl From for NetworkError { diff --git a/src/connectivity/profile_network.rs b/src/connectivity/profile_network.rs index dab5ea0..4a7e959 100644 --- a/src/connectivity/profile_network.rs +++ b/src/connectivity/profile_network.rs @@ -1,4 +1,4 @@ -use connectivity::{providers::Machine, Network, NetworkError}; +use connectivity::{providers::Machine, Config, Network, NetworkError}; #[derive(Debug)] pub struct ProfileNetwork { @@ -7,19 +7,22 @@ pub struct ProfileNetwork { /// Profile Network handler responsible to connect to a wireless network. impl ProfileNetwork { - pub fn new(name: &str) -> Result { + pub fn new(name: &str, config: Option) -> Result { if !(cfg!(target_os = "linux") || cfg!(target_os = "windows")) { 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 { handler: Box::new(handler), }) } - pub fn connect(&self, password: &str) -> bool { + pub fn connect(&self, password: &str) -> Result { self.handler.connect(password) } } diff --git a/src/connectivity/providers/linux.rs b/src/connectivity/providers/linux.rs index ee892af..0a7c9aa 100644 --- a/src/connectivity/providers/linux.rs +++ b/src/connectivity/providers/linux.rs @@ -1,128 +1,50 @@ -extern crate tempfile; - -use self::tempfile::NamedTempFile; -use connectivity::{Network, NetworkError, NetworkType}; -use std::process::{Command, Output}; -use std::{ - fs::File, io::{Error, Read, Write}, -}; +use connectivity::{Network, NetworkError}; +use std::process::Command; #[derive(Debug)] pub struct Linux { pub name: String, - pub network_type: NetworkType, + interface: String, } impl Linux { - pub fn new(name: &str) -> Result { - match Linux::check_if_web_or_wpa(name.into()) { - Ok(t) => match t { - NetworkType::WEP => Ok(Linux { - name: name.into(), - network_type: NetworkType::WEP, - }), - _ => Ok(Linux { - name: name.into(), - network_type: NetworkType::WPA, - }), - }, - Err(err) => Err(err), + pub fn new(name: &str, interface: Option<&str>) -> Self { + Linux { + name: name.into(), + interface: interface.unwrap_or("wlan0").into(), } } - - /// Detects the network type of a given network. - fn check_if_web_or_wpa(name: String) -> Result { - 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 { - 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 { - 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 { - let mut temp_file = NamedTempFile::new()?; - write!(temp_file, "{}", content)?; - - Ok(temp_file) - } } impl Network for Linux { - fn connect(&self, password: &str) -> bool { - match self.network_type { - NetworkType::WEP => self - .connect_to_wep_network(password) - .map_err(|_err| false) - .unwrap() - .status - .success(), - _ => self - .connect_to_wpa_network(password) - .map_err(|_err| false) - .unwrap() - .status - .success(), - } + fn connect(&self, password: &str) -> Result { + let output = Command::new("nmcli") + .args(&[ + "d", + "wifi", + "connect", + &self.name, + "password", + &password, + "ifname", + &self.interface, + ]) + .output() + .map_err(|err| NetworkError::FailedToConnect(format!("{}", err)))?; + + Ok(String::from_utf8_lossy(&output.stdout) + .as_ref() + .contains("successfully activated")) + } + + fn disconnect(&self) -> Result { + 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")) } } diff --git a/src/connectivity/providers/windows.rs b/src/connectivity/providers/windows.rs index ca6b41e..fc4e304 100644 --- a/src/connectivity/providers/windows.rs +++ b/src/connectivity/providers/windows.rs @@ -1,5 +1,5 @@ use connectivity::handlers::NetworkXmlProfileHandler; -use connectivity::Network; +use connectivity::{Network, NetworkError}; use std::process::Command; @@ -11,14 +11,14 @@ pub(crate) struct Windows { impl Windows { #[cfg(target_os = "windows")] - pub fn new(name: &str) -> Result { - Ok(Windows { + pub fn new(name: &str, interface: Option<&str>) -> Self { + Windows { name: String::from(name), 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(); handler.content = handler .content @@ -26,12 +26,12 @@ impl Windows { .replace("{password}", password); // Write details to new xml file - if let Err(_) = handler.to_file(&self.output_xml_path).map_err(|_err| false) { - return false; - } + let _ = handler + .to_file(&self.output_xml_path) + .map_err(|err| NetworkError::IoError(err))?; // Add the network profile - if let Err(_) = Command::new("netsh") + Command::new("netsh") .args(&[ "wlan", "add", @@ -39,27 +39,34 @@ impl Windows { &format!("filename={}", self.output_xml_path), ]) .output() - { - return false; - } + .map_err(|_| NetworkError::AddNetworkProfileFailed)?; - true + Ok(()) } } impl Network for Windows { - fn connect(&self, password: &str) -> bool { - if self.add_profile(password) == false { - return false; - } + fn connect(&self, password: &str) -> Result { + self.add_profile(password)?; let output = Command::new("netsh") .args(&["wlan", "connect", &format!("name={}", self.name)]) - .output(); + .output() + .map_err(|err| NetworkError::FailedToConnect(format!("{}", err)))?; - match output { - Ok(res) => res.status.success(), - Err(_) => false, - } + Ok(String::from_utf8_lossy(&output.stdout) + .as_ref() + .contains("successfully activated")) + } + + fn disconnect(&self) -> Result { + 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")) } } diff --git a/src/lib.rs b/src/lib.rs index 6896143..d741abf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] mod connectivity; -pub use connectivity::{profile_network::ProfileNetwork as WiFi, *}; +pub use connectivity::{profile_network::ProfileNetwork as WiFi, Config}; #[cfg(test)] mod tests { @@ -9,7 +9,12 @@ mod tests { #[test] fn connect_to_wifi_failed() { - let wifi = WiFi::new("hello").unwrap(); - assert_eq!(wifi.connect("password"), false); + let config = Some(Config { + interface: Some(String::from("wlo1")), + }); + + let wifi = WiFi::new("hello", config).unwrap(); + + assert_eq!(wifi.connect("password").unwrap(), false); } } diff --git a/src/main.rs b/src/main.rs index 60be885..17e5fa3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,24 @@ 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> { - let wifi = WiFi::new("AndroidAPSD")?; - println!("{}", wifi.connect("belm4235")); + let config = Some(Config { + 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(()) }