Chore(Stage): Initial Commit
This commit is contained in:
+11
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
**/target/*
|
||||||
|
**/*.rs.bk
|
||||||
|
Cargo.lock
|
||||||
|
*.xml
|
||||||
|
.env
|
||||||
|
rls
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
**/.DS_Store
|
||||||
|
crossbeam-channel
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
[package]
|
||||||
|
name = "wifi-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Tochukwu Nkemdilim <nkemdilimtochukwu@gmail.com>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
mod xml_profile_handler;
|
||||||
|
|
||||||
|
pub(crate) use self::xml_profile_handler::NetworkXmlProfileHandler;
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
use std::fs;
|
||||||
|
use std::io;
|
||||||
|
use connectivity::stubs::windows_wifi_profile;
|
||||||
|
|
||||||
|
pub(crate) struct NetworkXmlProfileHandler {
|
||||||
|
pub(crate) content: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetworkXmlProfileHandler {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
NetworkXmlProfileHandler {
|
||||||
|
content: NetworkXmlProfileHandler::read_from_stub(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_from_stub() -> String {
|
||||||
|
windows_wifi_profile::get_wifi_profile()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Recreate the file and dump the processed contents to it
|
||||||
|
pub fn to_file(&mut self, file_path: &str) -> Result<(), io::Error> {
|
||||||
|
Ok(fs::write(&file_path, self.content.as_bytes())?)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
pub mod profile_network;
|
||||||
|
mod handlers;
|
||||||
|
mod stubs;
|
||||||
|
mod providers;
|
||||||
|
|
||||||
|
use std::{fmt, io};
|
||||||
|
use std::string::FromUtf8Error;
|
||||||
|
|
||||||
|
pub trait Network {
|
||||||
|
/// Makes an attempt to connect to a selected wireless network with password specified.
|
||||||
|
fn connect(&self, password: &str) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Improve upon this.
|
||||||
|
impl fmt::Debug for Network {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "Network")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum NetworkType {
|
||||||
|
WEP,
|
||||||
|
WPA,
|
||||||
|
WPA2,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum NetworkTypeParseError {
|
||||||
|
FromUtf8Error(FromUtf8Error),
|
||||||
|
IoError(io::Error),
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
use connectivity::providers::{Machine};
|
||||||
|
use connectivity::Network;
|
||||||
|
use std::io::{Error, ErrorKind};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ProfileNetwork {
|
||||||
|
handler: Box<Network>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Profile Network handler responsible to connect to a wireless network.
|
||||||
|
impl ProfileNetwork {
|
||||||
|
pub fn new(name: &str) -> Result<Self, Error> {
|
||||||
|
if !(cfg!(target_os = "linux") || cfg!(target_os = "windows")) {
|
||||||
|
return Err(Error::new(
|
||||||
|
ErrorKind::Other,
|
||||||
|
"The Specified OS is not supported",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let handler = Machine::new(name)?;
|
||||||
|
return Ok(ProfileNetwork {
|
||||||
|
handler: Box::new(handler),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn connect(&self, password: &str) -> bool {
|
||||||
|
self.handler.connect(password)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
use connectivity::{Network, NetworkType, NetworkTypeParseError};
|
||||||
|
use std::io;
|
||||||
|
use std::io::{Error, ErrorKind};
|
||||||
|
use std::process::{Command, Output};
|
||||||
|
|
||||||
|
pub struct Linux {
|
||||||
|
pub name: String,
|
||||||
|
pub network_type: NetworkType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Linux {
|
||||||
|
pub fn new(name: String) -> Result<Self, io::Error> {
|
||||||
|
match Linux::check_if_web_or_wpa(name.clone()) {
|
||||||
|
Ok(t) => match t {
|
||||||
|
NetworkType::WEP => Ok(Linux {
|
||||||
|
name: name.clone(),
|
||||||
|
network_type: NetworkType::WEP,
|
||||||
|
}),
|
||||||
|
_ => Ok(Linux {
|
||||||
|
name: name.clone(),
|
||||||
|
network_type: NetworkType::WPA,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Err(_) => Err(Error::new(ErrorKind::Other, "Failed to parse")), // use the NetworkTypeParseError::IoError here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Detects the network type of a given network.
|
||||||
|
fn check_if_web_or_wpa(name: String) -> Result<NetworkType, NetworkTypeParseError> {
|
||||||
|
Command::new("nmcli")
|
||||||
|
.args(&[
|
||||||
|
"con",
|
||||||
|
"list",
|
||||||
|
"id",
|
||||||
|
"\"",
|
||||||
|
&name,
|
||||||
|
"\"",
|
||||||
|
"|",
|
||||||
|
"awk",
|
||||||
|
"'/key-mgmt/ {{ print $2 }}'",
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.map_err(|err| NetworkTypeParseError::IoError(err))
|
||||||
|
.and_then(|output| {
|
||||||
|
String::from_utf8(output.stdout)
|
||||||
|
.map_err(|err| NetworkTypeParseError::FromUtf8Error(err))
|
||||||
|
.and_then(|result| match result.as_ref() {
|
||||||
|
"wpa-psk" => Ok(NetworkType::WPA),
|
||||||
|
_ => Ok(NetworkType::WEP),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn connect_to_wep_network(&self, password: &str) -> Result<Output, io::Error> {
|
||||||
|
Command::new("iwconfig")
|
||||||
|
.args(&["wlan0", "essid", &self.name, "key", password])
|
||||||
|
.output()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn connect_to_wpa_network(&self, password: &str) -> Result<Output, io::Error> {
|
||||||
|
// Dynamically generate differennt version of file (if running sync)
|
||||||
|
Command::new("wpa_passphrase")
|
||||||
|
.args(&[&self.name, password, "wpa.conf"])
|
||||||
|
.output()?;
|
||||||
|
|
||||||
|
Ok(Command::new("wpa_supplicant")
|
||||||
|
.args(&["-Dwext", "-i", "wlan0", "-c/root/wpa.conf"])
|
||||||
|
.output()?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
mod linux;
|
||||||
|
mod windows;
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub(crate) use self::windows::Windows as Machine;
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub(crate) use self::linux::Linux as Machine;
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
use connectivity::handlers::NetworkXmlProfileHandler;
|
||||||
|
use connectivity::Network;
|
||||||
|
use std::io;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
const OUTPUT_XML_FILE_PATH: &str = "output.xml";
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub(crate) struct Windows {
|
||||||
|
name: String,
|
||||||
|
pub output_xml_path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Windows {
|
||||||
|
pub fn new(name: &str) -> Result<Self, io::Error> {
|
||||||
|
let profile_file_name = format!("netsh wlan add profile filename=\"{}\"", name);
|
||||||
|
|
||||||
|
Command::new("cmd")
|
||||||
|
.args(&["/C", &profile_file_name[..]])
|
||||||
|
.output()?;
|
||||||
|
|
||||||
|
Ok(Windows {
|
||||||
|
name: String::from(name),
|
||||||
|
output_xml_path: OUTPUT_XML_FILE_PATH.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Network for Windows {
|
||||||
|
fn connect(&self, password: &str) -> bool {
|
||||||
|
{
|
||||||
|
let mut handler = NetworkXmlProfileHandler::new();
|
||||||
|
|
||||||
|
handler.content = handler
|
||||||
|
.content
|
||||||
|
.replace("{SSID}", &self.name)
|
||||||
|
.replace("{password}", password);
|
||||||
|
|
||||||
|
// Write details to new xml file
|
||||||
|
if let Err(err) = handler.to_file(&self.output_xml_path).map_err(|_err| false) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = Command::new("netsh")
|
||||||
|
.args(&["wlan", "connect", &format!("name = \"{}\"", self.name)])
|
||||||
|
.output();
|
||||||
|
|
||||||
|
match output {
|
||||||
|
Ok(res) => res.status.success(),
|
||||||
|
Err(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub(crate) mod windows_wifi_profile;
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
pub fn get_wifi_profile() -> String {
|
||||||
|
String::from(
|
||||||
|
r#"
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1">
|
||||||
|
<name>{SSID}</name>
|
||||||
|
<SSIDConfig>
|
||||||
|
<SSID>
|
||||||
|
<name>{SSID}</name>
|
||||||
|
</SSID>
|
||||||
|
</SSIDConfig>
|
||||||
|
<connectionType>ESS</connectionType>
|
||||||
|
<connectionMode>auto</connectionMode>
|
||||||
|
<MSM>
|
||||||
|
<security>
|
||||||
|
<authEncryption>
|
||||||
|
<authentication>WPA2PSK</authentication>
|
||||||
|
<encryption>AES</encryption>
|
||||||
|
<useOneX>false</useOneX>
|
||||||
|
</authEncryption>
|
||||||
|
<sharedKey>
|
||||||
|
<keyType>passPhrase</keyType>
|
||||||
|
<protected>false</protected>
|
||||||
|
<keyMaterial>{password}</keyMaterial>
|
||||||
|
</sharedKey>
|
||||||
|
</security>
|
||||||
|
</MSM>
|
||||||
|
<MacRandomization
|
||||||
|
xmlns="http://www.microsoft.com/networking/WLAN/profile/v3">
|
||||||
|
<enableRandomization>false</enableRandomization>
|
||||||
|
</MacRandomization>
|
||||||
|
</WLANProfile>
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
mod connectivity;
|
||||||
|
pub use connectivity::profile_network::ProfileNetwork as WiFi;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn connect_to_wifi_failed() {
|
||||||
|
let wifi = WiFi::new("hello").unwrap();
|
||||||
|
assert_eq!(wifi.connect("password"), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user