├── src ├── connectivity │ ├── stubs │ │ ├── mod.rs │ │ └── windows_wifi_profile.rs │ ├── handlers │ │ ├── mod.rs │ │ └── xml_profile_handler.rs │ ├── providers │ │ ├── mod.rs │ │ ├── osx.rs │ │ ├── linux.rs │ │ └── windows.rs │ └── mod.rs ├── hotspot │ ├── providers │ │ ├── osx.rs │ │ ├── mod.rs │ │ ├── windows.rs │ │ └── linux.rs │ └── mod.rs ├── lib.rs ├── main.rs └── platforms │ ├── mod.rs │ ├── osx.rs │ ├── linux.rs │ └── windows.rs ├── .gitignore ├── examples ├── hotspot │ ├── Cargo.toml │ └── src │ │ └── main.rs └── wifi-cli │ ├── Cargo.toml │ └── src │ └── main.rs ├── tests └── mod.rs ├── .travis.yml ├── Cargo.toml └── README.md /src/connectivity/stubs/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod windows_wifi_profile; 2 | -------------------------------------------------------------------------------- /src/connectivity/handlers/mod.rs: -------------------------------------------------------------------------------- 1 | mod xml_profile_handler; 2 | pub(crate) use self::xml_profile_handler::NetworkXmlProfileHandler; 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | **/target/* 3 | **/*.rs.bk 4 | Cargo.lock 5 | *.xml 6 | .env 7 | rls 8 | .vscode 9 | .idea 10 | **/.DS_Store 11 | crossbeam-channel -------------------------------------------------------------------------------- /src/connectivity/providers/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_os = "linux")] 2 | mod linux; 3 | #[cfg(target_os = "macos")] 4 | mod osx; 5 | #[cfg(target_os = "windows")] 6 | mod windows; 7 | -------------------------------------------------------------------------------- /examples/hotspot/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wifi-connect" 3 | version = "0.1.0" 4 | edition = "2018" 5 | authors = ["Tochukwu Nkemdilim "] 6 | 7 | [dependencies] 8 | wifi-rs = { path = "../../../wifi-rs" } -------------------------------------------------------------------------------- /src/hotspot/providers/osx.rs: -------------------------------------------------------------------------------- 1 | use crate::{hotspot::WifiHotspot, platforms::WiFi}; 2 | 3 | /// Configuration for a wireless hotspot. 4 | #[allow(dead_code)] 5 | pub struct HotspotConfig; 6 | 7 | /// Wireless hotspot functionality for a wifi interface. 8 | impl WifiHotspot for WiFi {} 9 | -------------------------------------------------------------------------------- /examples/wifi-cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wifi-cli" 3 | version = "0.1.0" 4 | edition = "2018" 5 | authors = ["Tochukwu Nkemdilim "] 6 | 7 | [dependencies] 8 | clap = { version = "2.31.2", features = ["yaml"] } 9 | wifi-rs = { path = "../../../wifi-rs" } -------------------------------------------------------------------------------- /tests/mod.rs: -------------------------------------------------------------------------------- 1 | fn fib(n: usize) -> u32 { 2 | let mut first = 1; 3 | let mut second = 1; 4 | 5 | for _ in 2..n { 6 | let temp = first; 7 | first = second; 8 | second += temp 9 | } 10 | 11 | return second; 12 | } 13 | 14 | #[test] 15 | fn basic() { 16 | assert_eq!(fib(10), 55); 17 | } 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | os: 4 | - linux 5 | - osx 6 | - windows 7 | 8 | rust: 9 | - nightly 10 | - beta 11 | - stable 12 | 13 | matrix: 14 | allow_failures: 15 | - rust: nightly 16 | 17 | script: 18 | - cargo build 19 | - cargo test 20 | 21 | notifications: 22 | email: 23 | on_success: never 24 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod connectivity; 2 | mod hotspot; 3 | mod platforms; 4 | 5 | /// Pre-requisite module for `Connectivity`, `Hotspot` functionality. 6 | pub mod prelude { 7 | pub use crate::connectivity::*; 8 | pub use crate::hotspot::providers::prelude::*; 9 | pub use crate::hotspot::*; 10 | pub use crate::platforms::Config; 11 | } 12 | 13 | pub use crate::platforms::WiFi; 14 | -------------------------------------------------------------------------------- /src/hotspot/providers/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_os = "windows")] 2 | mod windows; 3 | 4 | #[cfg(target_os = "linux")] 5 | mod linux; 6 | 7 | #[cfg(target_os = "macos")] 8 | mod osx; 9 | 10 | pub mod prelude { 11 | #[cfg(target_os = "linux")] 12 | pub use super::linux::*; 13 | #[cfg(target_os = "macos")] 14 | pub use super::osx::*; 15 | #[cfg(target_os = "windows")] 16 | pub use super::windows::*; 17 | } 18 | -------------------------------------------------------------------------------- /examples/hotspot/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use wifi_rs::{prelude::*, WiFi}; 3 | 4 | fn main() -> Result<(), io::Error> { 5 | let config = Some(Config { 6 | interface: Some("wlo1"), 7 | }); 8 | 9 | let mut wifi = WiFi::new(config); 10 | let config = HotspotConfig::new(Some(HotspotBand::Bg), Some(Channel::One)); 11 | 12 | wifi.create_hotspot("test-hotspot", "password", Some(&config)); 13 | 14 | Ok(()) 15 | } 16 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wifi-rs" 3 | version = "0.2.4" 4 | edition = "2018" 5 | authors = ["Tochukwu Nkemdilim "] 6 | description = "Interface with and manage Wireless Network (WiFi)" 7 | license = "MIT" 8 | keywords = ["wifi", "network", "wireless"] 9 | categories = ["api-bindings", "network-programming"] 10 | repository = "https://github.com/toksdotdev/wifi-rs" 11 | readme = "README.md" 12 | 13 | [target.'cfg(windows)'.dependencies] 14 | tempfile = "3.0.2" 15 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod connectivity; 2 | mod hotspot; 3 | mod platforms; 4 | 5 | use crate::connectivity::{Connectivity, WifiConnectionError}; 6 | use crate::platforms::{Config, WiFi}; 7 | 8 | fn main() -> Result<(), WifiConnectionError> { 9 | let config = Some(Config { 10 | interface: Some("wlo1"), 11 | }); 12 | 13 | let mut wifi = WiFi::new(config); 14 | 15 | match wifi.connect("CSIS_MH", "") { 16 | Ok(result) => println!( 17 | "{}", 18 | if result == true { 19 | "Connection Successfull." 20 | } else { 21 | "Invalid password." 22 | } 23 | ), 24 | Err(err) => println!("The following error occurred: {:?}", err), 25 | } 26 | 27 | Ok(()) 28 | } 29 | -------------------------------------------------------------------------------- /src/connectivity/handlers/xml_profile_handler.rs: -------------------------------------------------------------------------------- 1 | use crate::connectivity::stubs::windows_wifi_profile; 2 | use std::{io, io::Write}; 3 | use tempfile::NamedTempFile; 4 | 5 | /// A netowork XML handler for windows, responsible creating 6 | /// disposable xml profiles files. 7 | pub(crate) struct NetworkXmlProfileHandler { 8 | pub content: String, 9 | } 10 | 11 | impl NetworkXmlProfileHandler { 12 | /// Create a new xml profile handler for windows. 13 | pub fn new() -> Self { 14 | NetworkXmlProfileHandler { 15 | content: NetworkXmlProfileHandler::read_from_stub(), 16 | } 17 | } 18 | 19 | /// Generate a sample templated xml profile. 20 | pub fn read_from_stub() -> String { 21 | windows_wifi_profile::get_wifi_profile() 22 | } 23 | 24 | /// Recreate the file and dump the processed contents to it 25 | pub fn write_to_temp_file(&mut self) -> Result { 26 | let mut temp_file = NamedTempFile::new()?; 27 | write!(temp_file, "{}", self.content)?; 28 | 29 | Ok(temp_file) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/connectivity/stubs/windows_wifi_profile.rs: -------------------------------------------------------------------------------- 1 | pub fn get_wifi_profile() -> String { 2 | String::from(r#" 3 | 4 | {SSID} 5 | 6 | 7 | {SSID} 8 | 9 | 10 | ESS 11 | auto 12 | 13 | 14 | 15 | WPA2PSK 16 | AES 17 | false 18 | 19 | 20 | passPhrase 21 | false 22 | {password} 23 | 24 | 25 | 26 | 27 | false 28 | 29 | "#, 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /src/connectivity/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_os = "windows")] 2 | mod handlers; 3 | mod providers; 4 | #[cfg(target_os = "windows")] 5 | mod stubs; 6 | 7 | use crate::platforms::WifiError; 8 | use std::{fmt, io}; 9 | 10 | /// Wireless network connectivity functionality. 11 | pub trait Connectivity: fmt::Debug { 12 | /// Makes an attempt to connect to a selected wireless network with password specified. 13 | fn connect(&mut self, ssid: &str, password: &str) -> Result; 14 | 15 | /// Disconnects from a wireless network currently connected to. 16 | fn disconnect(&self) -> Result; 17 | } 18 | 19 | /// Error that occurs when attempting to connect to a wireless network. 20 | #[derive(Debug)] 21 | pub enum WifiConnectionError { 22 | /// Adding the newtork profile failed. 23 | #[cfg(target_os = "windows")] 24 | AddNetworkProfileFailed, 25 | /// Failed to connect to wireless network. 26 | FailedToConnect(String), 27 | /// Failed to disconnect from wireless network. Try turning the wireless interface down. 28 | FailedToDisconnect(String), 29 | /// A wireless error occurred. 30 | Other { kind: WifiError }, 31 | // SsidNotFound, 32 | } 33 | 34 | impl From for WifiConnectionError { 35 | fn from(error: io::Error) -> Self { 36 | WifiConnectionError::Other { 37 | kind: WifiError::IoError(error), 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/platforms/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_os = "linux")] 2 | mod linux; 3 | #[cfg(target_os = "macos")] 4 | mod osx; 5 | #[cfg(target_os = "windows")] 6 | mod windows; 7 | 8 | #[cfg(target_os = "linux")] 9 | pub use self::linux::{Connection, Linux as WiFi}; 10 | #[cfg(target_os = "macos")] 11 | pub use self::osx::{Connection, Osx as WiFi}; 12 | #[cfg(target_os = "windows")] 13 | pub use self::windows::{Connection, Windows as WiFi}; 14 | 15 | use std::{fmt, io}; 16 | 17 | /// Configuration for a wifi network. 18 | #[derive(Debug, Clone)] 19 | pub struct Config<'a> { 20 | /// The interface the wifi module is situated. 21 | pub interface: Option<&'a str>, 22 | } 23 | 24 | #[derive(Debug)] 25 | pub enum WifiError { 26 | // The specified wifi is currently disabled. Try switching it on. 27 | WifiDisabled, 28 | /// The wifi interface interface failed to switch on. 29 | #[cfg(target_os = "windows")] 30 | InterfaceFailedToOn, 31 | /// IO Error occurred. 32 | IoError(io::Error), 33 | } 34 | 35 | /// Wifi interface for an operating system. 36 | /// This provides basic functionalities for wifi interface. 37 | pub trait WifiInterface: fmt::Debug { 38 | /// Check if the wifi interface on host machine is enabled. 39 | fn is_wifi_enabled() -> Result { 40 | unimplemented!(); 41 | } 42 | 43 | /// Turn on the wifi interface of host machine. 44 | fn turn_on() -> Result<(), WifiError> { 45 | unimplemented!(); 46 | } 47 | 48 | /// Turn off the wifi interface of host machine. 49 | fn turn_off() -> Result<(), WifiError> { 50 | unimplemented!(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/platforms/osx.rs: -------------------------------------------------------------------------------- 1 | use crate::platforms::{Config, WifiError, WifiInterface}; 2 | use std::process::Command; 3 | 4 | #[derive(Debug)] 5 | pub struct Connection { 6 | pub(crate) ssid: String, 7 | } 8 | 9 | /// Wireless network interface for mac operating system. 10 | #[derive(Debug)] 11 | pub struct Osx { 12 | pub(crate) connection: Option, 13 | pub(crate) interface: String, 14 | } 15 | 16 | impl Osx { 17 | pub fn new(config: Option) -> Self { 18 | Osx { 19 | connection: None, 20 | interface: config.map_or("en0".to_string(), |cfg| { 21 | cfg.interface.unwrap_or("en0").to_string() 22 | }), 23 | } 24 | } 25 | } 26 | 27 | /// Wifi interface for osx operating system. 28 | /// This provides basic functionalities for wifi interface. 29 | impl WifiInterface for Osx { 30 | fn is_wifi_enabled() -> Result { 31 | let output = Command::new("networksetup") 32 | .args(&["radio", "wifi"]) 33 | .output() 34 | .map_err(|err| WifiError::IoError(err))?; 35 | 36 | Ok(String::from_utf8_lossy(&output.stdout).contains("enabled")) 37 | } 38 | 39 | /// Turn on the wireless network adapter. 40 | fn turn_on() -> Result<(), WifiError> { 41 | Command::new("networksetup") 42 | .args(&["-setairportpower", "en0", "on"]) 43 | .output() 44 | .map_err(|err| WifiError::IoError(err))?; 45 | 46 | Ok(()) 47 | } 48 | 49 | /// Turn off the wireless adapter. 50 | fn turn_off() -> Result<(), WifiError> { 51 | Command::new("networksetup") 52 | .args(&["-setairportpower", "en0", "off"]) 53 | .output() 54 | .map_err(|err| WifiError::IoError(err))?; 55 | 56 | Ok(()) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /examples/wifi-cli/src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::{App, Arg}; 2 | use std::io; 3 | use wifi_rs::prelude::*; 4 | use wifi_rs::WiFi; 5 | 6 | fn main() -> Result<(), io::Error> { 7 | let matches = App::new("Wi-Fi") 8 | .version("0.0.1") 9 | .author("Tochukwu Nkemdilim") 10 | .about("Connect to a Wi-Fi network 🎉") 11 | .arg( 12 | Arg::with_name("ssid") 13 | .short("s") 14 | .long("ssid") 15 | .multiple(false) 16 | .required(true) 17 | .takes_value(true) 18 | .help("SSID of wireless network."), 19 | ) 20 | .arg( 21 | Arg::with_name("password") 22 | .short("p") 23 | .long("password") 24 | .multiple(false) 25 | .required(true) 26 | .takes_value(true) 27 | .help("Password of the wireless network."), 28 | ) 29 | .arg( 30 | Arg::with_name("interface") 31 | .short("i") 32 | .long("interface") 33 | .multiple(false) 34 | .default_value("wlan0") 35 | .takes_value(true) 36 | .help("Wireless interface to connect through."), 37 | ) 38 | .get_matches(); 39 | 40 | // Get Password 41 | let password = matches.value_of("password").unwrap(); 42 | 43 | // Get SSID 44 | let ssid = matches.value_of("ssid").unwrap(); 45 | 46 | // Get Wireless Interface 47 | let interface = matches.value_of("interface").unwrap(); 48 | 49 | let config = Some(Config { 50 | interface: Some(interface), 51 | }); 52 | 53 | // let wifi = WiFi::new(ssid)?; 54 | let mut wifi = WiFi::new(config); 55 | println!("Connection Status: {:?}", wifi.connect(ssid, password)); 56 | 57 | Ok(()) 58 | } 59 | -------------------------------------------------------------------------------- /src/connectivity/providers/osx.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | connectivity::{Connectivity, WifiConnectionError}, 3 | platforms::{Connection, WiFi, WifiError, WifiInterface}, 4 | }; 5 | use std::process::Command; 6 | 7 | /// Wireless network connectivity functionality. 8 | impl Connectivity for WiFi { 9 | /// Attempts to connect to a wireless network with a given SSID and password. 10 | fn connect(&mut self, ssid: &str, password: &str) -> Result { 11 | if !WiFi::is_wifi_enabled().map_err(|err| WifiConnectionError::Other { kind: err })? { 12 | return Err(WifiConnectionError::Other { 13 | kind: WifiError::WifiDisabled, 14 | }); 15 | } 16 | 17 | let output = Command::new("networksetup") 18 | .args(&["-setairportnetwork", &self.interface, &ssid, &password]) 19 | .output() 20 | .map_err(|err| WifiConnectionError::FailedToConnect(format!("{}", err)))?; 21 | 22 | if String::from_utf8_lossy(&output.stdout).as_ref() != "" { 23 | return Ok(false); 24 | } 25 | 26 | self.connection = Some(Connection { 27 | ssid: String::from(ssid), 28 | }); 29 | 30 | Ok(true) 31 | } 32 | 33 | /// Attempts to disconnect from a wireless network currently connected to. 34 | fn disconnect(&self) -> Result { 35 | let output = Command::new("networksetup") 36 | .args(&[ 37 | "-removepreferredwirelessnetwork", 38 | &*self.interface, 39 | &*self.connection.as_ref().unwrap().ssid, 40 | ]) 41 | .output() 42 | .map_err(|err| WifiConnectionError::FailedToDisconnect(format!("{}", err)))?; 43 | 44 | Ok(String::from_utf8_lossy(&output.stdout) 45 | .as_ref() 46 | .contains("disconnect")) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/platforms/linux.rs: -------------------------------------------------------------------------------- 1 | use crate::platforms::{Config, WifiError, WifiInterface}; 2 | use std::process::Command; 3 | 4 | #[derive(Debug)] 5 | pub struct Connection { 6 | // pub(crate) ssid: String, 7 | } 8 | 9 | /// Wireless network interface for linux operating system. 10 | #[derive(Debug)] 11 | pub struct Linux { 12 | pub(crate) connection: Option, 13 | pub(crate) interface: String, 14 | } 15 | 16 | impl Linux { 17 | pub fn new(config: Option) -> Self { 18 | Linux { 19 | connection: None, 20 | interface: config.map_or("wlan0".to_string(), |cfg| { 21 | cfg.interface.unwrap_or("wlan0").to_string() 22 | }), 23 | } 24 | } 25 | } 26 | 27 | /// Wifi interface for linux operating system. 28 | /// This provides basic functionalities for wifi interface. 29 | impl WifiInterface for Linux { 30 | /// Check if wireless network adapter is enabled. 31 | fn is_wifi_enabled() -> Result { 32 | let output = Command::new("nmcli") 33 | .args(&["radio", "wifi"]) 34 | .output() 35 | .map_err(|err| WifiError::IoError(err))?; 36 | 37 | Ok(String::from_utf8_lossy(&output.stdout) 38 | .replace(" ", "") 39 | .replace("\n", "") 40 | .contains("enabled")) 41 | } 42 | 43 | /// Turn on the wireless network adapter. 44 | fn turn_on() -> Result<(), WifiError> { 45 | Command::new("nmcli") 46 | .args(&["radio", "wifi", "on"]) 47 | .output() 48 | .map_err(|err| WifiError::IoError(err))?; 49 | 50 | Ok(()) 51 | } 52 | 53 | /// Turn off the wireless network adapter. 54 | fn turn_off() -> Result<(), WifiError> { 55 | Command::new("nmcli") 56 | .args(&["radio", "wifi", "off"]) 57 | .output() 58 | .map_err(|err| WifiError::IoError(err))?; 59 | 60 | Ok(()) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/hotspot/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod providers; 2 | 3 | use self::providers::prelude::HotspotConfig; 4 | use crate::platforms::{WifiError, WifiInterface}; 5 | use std::fmt; 6 | 7 | /// Error that might occur when interacting managing wireless hotspot. 8 | #[derive(Debug)] 9 | pub enum WifiHotspotError { 10 | /// Failed to ceate wireless hotspot.s 11 | #[cfg(any(target_os = "windows", target_os = "linux"))] 12 | CreationFailed, 13 | 14 | /// Failed to stop wireless hotspot service. Try turning off 15 | /// the wireless interface via ```wifi.turn_off()```. 16 | #[cfg(target_os = "linux")] 17 | FailedToStop(std::io::Error), 18 | 19 | /// A wireless interface error occurred. 20 | Other { kind: WifiError }, 21 | } 22 | 23 | /// Wireless hotspot functionality for a wifi interface. 24 | pub trait WifiHotspot: fmt::Debug + WifiInterface { 25 | /// Creates wireless hotspot service for host machine. This only creates the wifi network, 26 | /// and isn't responsible for initiating the serving of the wifi network process. 27 | /// To begin serving the hotspot, use ```start_hotspot()```. 28 | fn create_hotspot( 29 | &mut self, 30 | ssid: &str, 31 | password: &str, 32 | configuration: Option<&HotspotConfig>, 33 | ) -> Result { 34 | let _a = ssid; 35 | let _b = password; 36 | let _c = configuration; 37 | 38 | unimplemented!(); 39 | } 40 | 41 | /// Start serving publicly an already created wireless hotspot. 42 | fn start_hotspot() -> Result { 43 | unimplemented!(); 44 | } 45 | 46 | /// Stop serving a wireless network. 47 | /// **NOTE: All users connected will automatically be disconnected.** 48 | fn stop_hotspot(&mut self) -> Result { 49 | unimplemented!(); 50 | } 51 | } 52 | 53 | impl From for WifiHotspotError { 54 | fn from(error: WifiError) -> Self { 55 | WifiHotspotError::Other { kind: error } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/connectivity/providers/linux.rs: -------------------------------------------------------------------------------- 1 | use crate::connectivity::{Connectivity, WifiConnectionError}; 2 | use crate::platforms::{Connection, WiFi, WifiError, WifiInterface}; 3 | use std::process::Command; 4 | 5 | /// Wireless network connectivity functionality. 6 | impl Connectivity for WiFi { 7 | /// Attempts to connect to a wireless network with a given SSID and password. 8 | fn connect(&mut self, ssid: &str, password: &str) -> Result { 9 | if !WiFi::is_wifi_enabled().map_err(|err| WifiConnectionError::Other { kind: err })? { 10 | return Err(WifiConnectionError::Other { 11 | kind: WifiError::WifiDisabled, 12 | }); 13 | } 14 | 15 | let output = Command::new("nmcli") 16 | .args(&[ 17 | "d", 18 | "wifi", 19 | "connect", 20 | ssid, 21 | "password", 22 | &password, 23 | "ifname", 24 | &self.interface, 25 | ]) 26 | .output() 27 | .map_err(|err| WifiConnectionError::FailedToConnect(format!("{}", err)))?; 28 | 29 | if !String::from_utf8_lossy(&output.stdout) 30 | .as_ref() 31 | .contains("successfully activated") 32 | { 33 | return Ok(false); 34 | } 35 | 36 | self.connection = Some(Connection { 37 | // ssid: String::from(ssid), 38 | }); 39 | 40 | Ok(true) 41 | } 42 | 43 | /// Attempts to disconnect from a wireless network currently connected to. 44 | fn disconnect(&self) -> Result { 45 | let output = Command::new("nmcli") 46 | .args(&["d", "disconnect", "ifname", &self.interface]) 47 | .output() 48 | .map_err(|err| WifiConnectionError::FailedToDisconnect(format!("{}", err)))?; 49 | 50 | Ok(String::from_utf8_lossy(&output.stdout) 51 | .as_ref() 52 | .contains("disconnect")) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/platforms/windows.rs: -------------------------------------------------------------------------------- 1 | use crate::platforms::{Config, WifiError, WifiInterface}; 2 | use std::process::Command; 3 | 4 | const WINDOWS_INTERFACE: &'static str = "Wireless Network Connection"; 5 | 6 | #[derive(Debug)] 7 | pub struct Connection { 8 | pub(crate) ssid: String, 9 | } 10 | 11 | /// Wireless network interface for windows operating system. 12 | #[derive(Debug)] 13 | pub struct Windows { 14 | pub(crate) connection: Option, 15 | pub(crate) interface: String, 16 | } 17 | 18 | impl Windows { 19 | pub fn new(config: Option) -> Self { 20 | Windows { 21 | connection: None, 22 | interface: config.map_or("wlan0".to_string(), |cfg| { 23 | cfg.interface.unwrap_or("wlan0").to_string() 24 | }), 25 | } 26 | } 27 | } 28 | 29 | /// Wifi interface for windows operating system. 30 | /// This provides basic functionalities for wifi interface. 31 | impl WifiInterface for Windows { 32 | /// Check if wireless network adapter is enabled. 33 | fn is_wifi_enabled() -> Result { 34 | let output = Command::new("netsh") 35 | .args(&[ 36 | "wlan", 37 | "show", 38 | "interface", 39 | &format!("name= \"{}\"", WINDOWS_INTERFACE), 40 | ]) 41 | .output() 42 | .map_err(|err| WifiError::IoError(err))?; 43 | 44 | Ok(!String::from_utf8_lossy(&output.stdout).contains("There is no wireless interface")) 45 | } 46 | 47 | /// Turn on the wireless network adapter. 48 | fn turn_on() -> Result<(), WifiError> { 49 | Command::new("netsh") 50 | .args(&[ 51 | "interface", 52 | "set", 53 | "interface", 54 | &format!("name= \"{}\"", WINDOWS_INTERFACE), 55 | "ENABLED", 56 | ]) 57 | .output() 58 | .map_err(|err| WifiError::IoError(err))?; 59 | 60 | Ok(()) 61 | } 62 | 63 | /// Turn off the wireless network adapter. 64 | fn turn_off() -> Result<(), WifiError> { 65 | let _output = Command::new("netsh") 66 | .args(&[ 67 | "interface", 68 | "set", 69 | "interface", 70 | &format!("name= \"{}\"", WINDOWS_INTERFACE), 71 | "DISABLED", 72 | ]) 73 | .output() 74 | .map_err(|err| WifiError::IoError(err))?; 75 | 76 | Ok(()) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WiFi-rs 2 | 3 | A rust crate to interface and manage Wi-Fi networks. 4 | 5 | This is a command-line counterpart of managing networks instead of via a GUI. 6 | 7 | ## Features 8 | 9 | - Connect to a WiFi (`Windows`, `Linux`, `MacOS`). 10 | - Disconnect from a WiFi network (`Windows`, `Linux`, `MacOS`). 11 | - Create hotspot (`Windows`, `Linux`). 12 | 13 | ## Currently supported network types 14 | 15 | Note that only **open**, **WEP** and **WPA-PSK** networks are supported at the moment. 16 | 17 | > It is also supposed that IP configuration is obtained via DHCP. 18 | 19 | ## Supported Operating Systems 20 | 21 | - Windows 22 | - Linux 23 | - MacOS 24 | 25 | ## Example 26 | 27 | ```Rust 28 | use wifi_rs::{prelude::*, WiFi}; 29 | 30 | fn main() -> Result<(), WifiConnectionError> { 31 | let config = Some(Config { 32 | interface: Some("wlo1"), 33 | }); 34 | 35 | let mut wifi = WiFi::new(config); 36 | 37 | match wifi.connect("AndroidAPSD22", "belm4235") { 38 | Ok(result) => println!( 39 | "{}", 40 | if result == true { 41 | "Connection Successful." 42 | } else { 43 | "Invalid password." 44 | } 45 | ), 46 | Err(err) => println!("The following error occurred: {:?}", err), 47 | } 48 | 49 | Ok(()) 50 | } 51 | ``` 52 | 53 | ## Todos 54 | 55 | ### Windows 56 | 57 | - [x] Support for Windows. 58 | - [x] Bundle windows profile sample as literals. 59 | - [x] Add hotspot functionality. 60 | - [x] Use `tempfile` crate on windows to generate windows profile temporary file. 61 | - [x] Fix the implementation for `is_wifi_enabled` for windows. 62 | - [x] Add implementation for WifiInterface trait. 63 | - [ ] Add get network type feature. 64 | 65 | ### Linux 66 | 67 | - [x] Support for linux. 68 | - [x] Add disconnect feature. 69 | - [x] Add hotspot functionality. 70 | - [ ] Add get network type feature. 71 | 72 | ### MacOS 73 | 74 | - [x] Add support for MacOS. 75 | - [ ] Add hotspot functionality. 76 | - [ ] Add get network type feature. 77 | 78 | ### General 79 | 80 | - [x] Return detailed error messages. 81 | - [x] Write documentation. 82 | - [x] Update `wifi-CLI` with recent updates. 83 | 84 | ## Contribution 85 | 86 | Any feature you feel is missing, why not send in a Pull Request, and let's help make this project better. Or if there are any bugs, kindly create an issue, so we could work together towards fixing it. 87 | 88 | ## Support 89 | 90 | Love this project, please show some love by starring the project 😃. 91 | -------------------------------------------------------------------------------- /src/connectivity/providers/windows.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | connectivity::{handlers::NetworkXmlProfileHandler, Connectivity, WifiConnectionError}, 3 | platforms::{Connection, WiFi, WifiError, WifiInterface}, 4 | }; 5 | use std::process::Command; 6 | 7 | impl WiFi { 8 | /// Add the wireless network profile of network to connect to, 9 | /// (this is specific to windows operating system). 10 | fn add_profile(ssid: &str, password: &str) -> Result<(), WifiConnectionError> { 11 | let mut handler = NetworkXmlProfileHandler::new(); 12 | handler.content = handler 13 | .content 14 | .replace("{SSID}", ssid) 15 | .replace("{password}", password); 16 | 17 | let temp_file = handler.write_to_temp_file()?; 18 | 19 | Command::new("netsh") 20 | .args(&[ 21 | "wlan", 22 | "add", 23 | "profile", 24 | &format!("filename={}", temp_file.path().to_str().unwrap()), 25 | ]) 26 | .output() 27 | .map_err(|_| WifiConnectionError::AddNetworkProfileFailed)?; 28 | 29 | Ok(()) 30 | } 31 | } 32 | 33 | /// Wireless network connectivity functionality. 34 | impl Connectivity for WiFi { 35 | /// Attempts to connect to a wireless network with a given SSID and password. 36 | fn connect(&mut self, ssid: &str, password: &str) -> Result { 37 | if !WiFi::is_wifi_enabled().map_err(|err| WifiConnectionError::Other { kind: err })? { 38 | return Err(WifiConnectionError::Other { 39 | kind: WifiError::WifiDisabled, 40 | }); 41 | } 42 | 43 | Self::add_profile(ssid, password)?; 44 | 45 | let output = Command::new("netsh") 46 | .args(&["wlan", "connect", &format!("name={}", ssid)]) 47 | .output() 48 | .map_err(|err| WifiConnectionError::FailedToConnect(format!("{}", err)))?; 49 | 50 | if !String::from_utf8_lossy(&output.stdout) 51 | .as_ref() 52 | .contains("completed successfully") 53 | { 54 | return Ok(false); 55 | } 56 | 57 | self.connection = Some(Connection { 58 | ssid: String::from(ssid), 59 | }); 60 | 61 | Ok(true) 62 | } 63 | 64 | /// Attempts to disconnect from a wireless network currently connected to. 65 | fn disconnect(&self) -> Result { 66 | let output = Command::new("netsh") 67 | .args(&["wlan", "disconnect"]) 68 | .output() 69 | .map_err(|err| WifiConnectionError::FailedToDisconnect(format!("{}", err)))?; 70 | 71 | Ok(String::from_utf8_lossy(&output.stdout) 72 | .as_ref() 73 | .contains("disconnect")) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/hotspot/providers/windows.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | hotspot::{WifiHotspot, WifiHotspotError}, 3 | platforms::{WiFi, WifiError, WifiInterface}, 4 | }; 5 | use std::process::Command; 6 | 7 | /// Configuration for a wireless hotspot. 8 | pub struct HotspotConfig; 9 | 10 | impl WiFi { 11 | /// Attempts to turn on a wireless networ if down. 12 | fn try_turn_on_network_if_down() -> Result<(), WifiError> { 13 | if !Self::is_wifi_enabled()? { 14 | Self::turn_on().map_err(|err| WifiError::InterfaceFailedToOn)?; 15 | } 16 | 17 | Ok(()) 18 | } 19 | } 20 | 21 | /// Wireless hotspot functionality for a wifi interface. 22 | impl WifiHotspot for WiFi { 23 | /// Creates wireless hotspot service for host machine. This only creats the wifi network, 24 | /// and isn't responsible for initiating the serving of the wifi network process. 25 | /// To begin serving the hotspot, use ```start_hotspot()```. 26 | fn create_hotspot( 27 | &mut self, 28 | ssid: &str, 29 | password: &str, 30 | configuration: Option<&HotspotConfig>, 31 | ) -> Result { 32 | let output = Command::new("netsh") 33 | .args(&[ 34 | "wlan", 35 | "set", 36 | "hostednetwork", 37 | "mode = allow", 38 | &format!("ssid={}", ssid), 39 | &format!("key={}", password), 40 | ]) 41 | .output() 42 | .map_err(|_err| WifiHotspotError::CreationFailed)?; 43 | 44 | if !String::from_utf8_lossy(&output.stdout) 45 | .as_ref() 46 | .contains("successfully changed") 47 | {} 48 | 49 | Self::try_turn_on_network_if_down()?; 50 | 51 | let output = Command::new("netsh") 52 | .args(&["wlan", "start", "hostednetwork"]) 53 | .output() 54 | .map_err(|err| WifiHotspotError::CreationFailed)?; 55 | 56 | Ok(String::from_utf8_lossy(&output.stdout) 57 | .as_ref() 58 | .contains("hosted network started")) 59 | } 60 | 61 | /// Start serving publicly an already created wireless hotspot. 62 | fn start_hotspot() -> Result { 63 | Self::try_turn_on_network_if_down()?; 64 | 65 | let output = Command::new("netsh") 66 | .args(&["wlan", "start", "hostednetwork"]) 67 | .output() 68 | .map_err(|err| WifiHotspotError::CreationFailed)?; 69 | 70 | Ok(String::from_utf8_lossy(&output.stdout) 71 | .as_ref() 72 | .contains("hosted network started")) 73 | } 74 | 75 | /// Stop serving a wireless network. 76 | /// 77 | /// **NOTE: All users connected will automatically be disconnected.** 78 | fn stop_hotspot(&mut self) -> Result { 79 | let output = Command::new("netsh") 80 | .args(&["wlan", "stop", "hostednetwork"]) 81 | .output() 82 | .map_err(|err| WifiHotspotError::CreationFailed)?; 83 | 84 | return Ok(String::from_utf8_lossy(&output.stdout) 85 | .as_ref() 86 | .contains("hosted network stopped")); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/hotspot/providers/linux.rs: -------------------------------------------------------------------------------- 1 | use crate::hotspot::{WifiHotspot, WifiHotspotError}; 2 | use crate::platforms::WiFi; 3 | use std::fmt; 4 | use std::process::Command; 5 | 6 | /// Name of the group upon which the hotspot would be created. 7 | /// This name must be fixed, in order to still interact with 8 | /// the same hotspot previosuly created. 9 | const HOTSPOT_GROUP: &'static str = "Hotspot"; 10 | 11 | /// Configuration for a wireless hotspot. 12 | pub struct HotspotConfig { 13 | /// The band tor broadcast network on. 14 | band: Option, 15 | /// The channel to broadcast network on. 16 | channel: Option, 17 | } 18 | 19 | /// function to create a new config object 20 | #[allow(dead_code)] 21 | impl HotspotConfig { 22 | pub fn new(band: Option, channel: Option) -> HotspotConfig { 23 | HotspotConfig { band, channel } 24 | } 25 | } 26 | 27 | #[allow(dead_code)] 28 | #[derive(Debug)] 29 | /// Band type of wireless hotspot. 30 | pub enum HotspotBand { 31 | /// Band `A` 32 | A, 33 | /// Band `BG` 34 | Bg, 35 | } 36 | 37 | /// Channel to broadcast wireless hotspot on. 38 | #[allow(dead_code)] 39 | #[derive(Debug, Clone, Copy)] 40 | pub enum Channel { 41 | /// Channel 1 42 | One = 1, 43 | /// Channel 2 44 | Two = 2, 45 | /// Channel 3 46 | Three = 3, 47 | /// Channel 4 48 | Four = 4, 49 | /// Channel 5 50 | Five = 5, 51 | /// Channel 6 52 | Six = 6, 53 | } 54 | 55 | /// Wireless hotspot functionality for a wifi interface. 56 | impl WifiHotspot for WiFi { 57 | /// Creates wireless hotspot service for host machine. This only creats the wifi network, 58 | /// and isn't responsible for initiating the serving of the wifi network process. 59 | /// To begin serving the hotspot, use ```start_hotspot()```. 60 | fn create_hotspot( 61 | &mut self, 62 | ssid: &str, 63 | password: &str, 64 | configuration: Option<&HotspotConfig>, 65 | ) -> Result { 66 | let mut command = vec![ 67 | "device".to_string(), 68 | "wifi".to_string(), 69 | "hotspot".to_string(), 70 | "ifname".to_string(), 71 | self.interface.to_string(), 72 | "con-name".to_string(), 73 | HOTSPOT_GROUP.to_string(), 74 | "ssid".to_string(), 75 | ssid.to_string(), 76 | "password".to_string(), 77 | password.to_string(), 78 | ]; 79 | 80 | let mut cmd = generate_command_param_from_config(configuration); 81 | command.append(&mut cmd); 82 | 83 | let output = Command::new("nmcli") 84 | .args(&command) 85 | .output() 86 | .map_err(|_err| WifiHotspotError::CreationFailed)?; 87 | 88 | Ok(String::from_utf8_lossy(&output.stdout) 89 | .as_ref() 90 | .contains("successfully activated")) 91 | } 92 | 93 | /// Start serving publicly an already created wireless hotspot. 94 | fn start_hotspot() -> Result { 95 | let output = Command::new("nmcli") 96 | .args(&["con", "up", HOTSPOT_GROUP]) 97 | .output() 98 | .map_err(|err| WifiHotspotError::FailedToStop(err))?; 99 | 100 | Ok(String::from_utf8_lossy(&output.stdout) 101 | .as_ref() 102 | .contains("Connection successfully activated")) 103 | } 104 | 105 | /// Stop serving a wireless network. 106 | /// 107 | /// **NOTE: All users connected will automatically be disconnected.** 108 | fn stop_hotspot(&mut self) -> Result { 109 | let output = Command::new("nmcli") 110 | .args(&["con", "down", HOTSPOT_GROUP]) 111 | .output() 112 | .map_err(|err| WifiHotspotError::FailedToStop(err))?; 113 | 114 | Ok(String::from_utf8_lossy(&output.stdout) 115 | .as_ref() 116 | .contains("Connection 'Hotspot' successfully deactivated")) 117 | } 118 | } 119 | 120 | /// Generate vector values from a given config paramenters. 121 | fn generate_command_param_from_config(configuration: Option<&HotspotConfig>) -> Vec { 122 | let mut command = vec![]; 123 | 124 | if let Some(ref config) = configuration { 125 | if let Some(ref band) = config.band { 126 | let band = format!("{}", band); 127 | let mut a = vec!["band".to_string(), band]; 128 | command.append(&mut a); 129 | } 130 | 131 | if let Some(channel) = config.channel { 132 | let channel = format!("{}", channel as u8); 133 | command.append(&mut vec!["channel".to_string(), channel]); 134 | } 135 | }; 136 | 137 | command 138 | } 139 | 140 | impl fmt::Display for HotspotBand { 141 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 142 | write!( 143 | f, 144 | "{}", 145 | match self { 146 | HotspotBand::A => "a", 147 | HotspotBand::Bg => "bg", 148 | } 149 | ) 150 | } 151 | } 152 | --------------------------------------------------------------------------------