mod misc;
mod sys;
use std::fmt;
use crate::misc::yes_or_no;
type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
InterfaceError(String),
SocketError(String),
ScanFailed(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum WifiSecurity {
Open,
Wep,
Wpa2PersonalPsk,
Wpa3PersonalSae,
Wpa2EnterpriseEap,
Wpa2EnterpriseEap256,
Wpa3EnterpriseEap256,
Wpa3EnterpriseSuiteBEap256,
Wpa3EnterpriseEap,
Wpa2EnterpriseEapFt,
Wpa2PersonalPskFt,
Wpa2PersonalPsk256,
Wpa3PersonalSaeFt,
WpaPersonalPsk,
WpaEnterpriseEap,
TunneledDirectLinkSetup,
Unknown,
Other(String),
}
#[derive(Debug, PartialEq, Eq, Default, Clone)]
pub struct Wifi {
pub mac: String,
pub ssid: String,
pub channel: u32,
pub signal_level: i32,
pub security: Vec<WifiSecurity>,
}
pub enum SignalStrength {
Unknown,
Weak,
Fair,
Good,
Excellent,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::SocketError(detail) => {
write!(f, "Error while creating socket: {}", detail)
}
Error::InterfaceError(detail) => {
write!(f, "Interface error: {}", detail)
}
Error::ScanFailed(detail) => {
write!(f, "Scan Failed: {}", detail)
}
}
}
}
impl fmt::Display for WifiSecurity {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
WifiSecurity::Open => write!(f, "Open"),
WifiSecurity::Other(sec) => write!(f, "{}", sec),
WifiSecurity::TunneledDirectLinkSetup => write!(f, "Tunneled Direct Link Setup"),
WifiSecurity::Unknown => write!(f, "Unknown"),
WifiSecurity::Wep => write!(f, "WEP"),
WifiSecurity::Wpa2EnterpriseEap => write!(f, "WPA2-Enterprise (EAP)"),
WifiSecurity::Wpa2EnterpriseEapFt => write!(f, "WPA2-Enterprise (EAP-FT)"),
WifiSecurity::Wpa2PersonalPsk => write!(f, "WPA2-Personal (PSK)"),
WifiSecurity::Wpa2PersonalPskFt => write!(f, "WPA2-Personal (PSK-FT)"),
WifiSecurity::Wpa3EnterpriseEap256 => write!(f, "WPA3-Enterprise (EAP-256)"),
WifiSecurity::Wpa3EnterpriseSuiteBEap256 => {
write!(f, "WPA3-Enterprise (Suite B EAP-256)")
}
WifiSecurity::Wpa2PersonalPsk256 => write!(f, "WPA2-Personal (PSK-256)"),
WifiSecurity::Wpa3PersonalSae => write!(f, "WPA3-Personal (SAE)"),
WifiSecurity::Wpa3PersonalSaeFt => write!(f, "WPA3-Personal (SAE-FT)"),
WifiSecurity::WpaEnterpriseEap => write!(f, "WPA-Enterprise"),
WifiSecurity::WpaPersonalPsk => write!(f, "WPA-Personal"),
WifiSecurity::Wpa2EnterpriseEap256 => write!(f, "WPA2-Enterprise (EPA-256)"),
WifiSecurity::Wpa3EnterpriseEap => write!(f, "WPA3-Enterprise (EAP)"),
}
}
}
impl fmt::Display for Wifi {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"BSSID: {} | SSID: {} | Channel: {} | RSSI: {} dBm | Security: {} | Hidden: {}",
self.mac,
self.ssid,
self.channel,
self.signal_level,
self.security
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>()
.join(", "),
yes_or_no(self.is_hidden())
)
}
}
impl Wifi {
pub fn is_open(&self) -> bool {
self.security.len() == 1 && self.security[0] == WifiSecurity::Open
}
pub fn is_wpa3(&self) -> bool {
self.security.iter().any(|s| {
matches!(
s,
WifiSecurity::Wpa3EnterpriseEap256
| WifiSecurity::Wpa3EnterpriseSuiteBEap256
| WifiSecurity::Wpa3PersonalSae
| WifiSecurity::Wpa3PersonalSaeFt
| WifiSecurity::Wpa3EnterpriseEap
)
})
}
pub fn is_wpa2(&self) -> bool {
self.security.iter().any(|s| {
matches!(
s,
WifiSecurity::Wpa2EnterpriseEap
| WifiSecurity::Wpa2EnterpriseEapFt
| WifiSecurity::Wpa2PersonalPsk
| WifiSecurity::Wpa2PersonalPskFt
| WifiSecurity::Wpa2PersonalPsk256
| WifiSecurity::Wpa2EnterpriseEap256
)
})
}
pub fn is_enterprise(&self) -> bool {
self.security.iter().any(|s| {
matches!(
s,
WifiSecurity::WpaEnterpriseEap
| WifiSecurity::Wpa2EnterpriseEap
| WifiSecurity::Wpa2EnterpriseEapFt
| WifiSecurity::Wpa3EnterpriseEap256
| WifiSecurity::Wpa3EnterpriseSuiteBEap256
| WifiSecurity::Wpa3EnterpriseEap
| WifiSecurity::Wpa2EnterpriseEap256
)
})
}
pub fn is_personal(&self) -> bool {
self.security.iter().any(|s| {
matches!(
s,
WifiSecurity::WpaPersonalPsk
| WifiSecurity::Wpa2PersonalPsk
| WifiSecurity::Wpa2PersonalPskFt
| WifiSecurity::Wpa3PersonalSae
| WifiSecurity::Wpa3PersonalSaeFt
| WifiSecurity::Wpa2PersonalPsk256
)
})
}
pub fn readable_signal(&self) -> SignalStrength {
match self.signal_level {
0 => SignalStrength::Unknown,
-50..=0 => SignalStrength::Excellent,
-70..=-61 => SignalStrength::Good,
-80..=-71 => SignalStrength::Fair,
_ => SignalStrength::Weak,
}
}
pub fn is_hidden(&self) -> bool {
self.ssid.is_empty()
}
pub fn get_frequency(&self) -> u32 {
match self.channel {
1..=13 => 2407 + self.channel * 5, 14 => 2484, 36..=165 => 5000 + self.channel * 5, 167..=233 => 5950 + (self.channel - 1) * 5, _ => 0, }
}
}
impl std::error::Error for Error {}
pub trait WlanScanner {
fn scan(&mut self) -> Result<Vec<Wifi>>;
}
pub fn scan() -> Result<Vec<Wifi>> {
#[cfg(target_os = "macos")]
{
let mut scanner = sys::macos::ScanMac;
scanner.scan()
}
#[cfg(target_os = "linux")]
{
let mut scanner = sys::linux::ScanLinux;
scanner.scan()
}
#[cfg(target_os = "windows")]
{
let mut scanner = sys::windows::ScanWindows;
scanner.scan()
}
#[cfg(target_os = "openbsd")]
{
let mut scanner = sys::openbsd::ScanOpenBsd;
scanner.scan()
}
#[cfg(target_os = "freebsd")]
{
let mut scanner = sys::freebsd::ScanFreeBsd;
scanner.scan()
}
}