use crate::StackError;
use arrayvec::ArrayString;
use super::constants::{
AuthType, WifiChannel, MAX_HOST_NAME_LEN, MAX_PSK_KEY_LEN, MAX_S802_PASSWORD_LEN,
MAX_S802_USERNAME_LEN, MAX_SSID_LEN, MAX_WEP_KEY_LEN,
};
#[cfg(feature = "wep")]
use super::constants::WepKeyIndex;
use core::net::Ipv4Addr;
const DEFAULT_AP_IP: u32 = 0xC0A80101;
pub type HostName = ArrayString<MAX_HOST_NAME_LEN>;
pub type Ssid = ArrayString<MAX_SSID_LEN>;
pub type WpaKey = ArrayString<MAX_PSK_KEY_LEN>;
pub type WepKey = ArrayString<MAX_WEP_KEY_LEN>;
pub type S8Username = ArrayString<MAX_S802_USERNAME_LEN>;
pub type S8Password = ArrayString<MAX_S802_PASSWORD_LEN>;
#[repr(u8)]
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Credentials {
Open = 1,
WpaPSK(WpaKey) = 2,
#[cfg(feature = "wep")]
Wep(WepKey, WepKeyIndex) = 3,
S802_1X(S8Username, S8Password) = 4,
}
pub struct ProvisioningInfo {
pub ssid: Ssid,
pub key: Credentials,
pub(crate) status: bool,
}
#[derive(Debug, PartialEq, Eq)]
pub struct AccessPoint<'a> {
pub ssid: &'a Ssid,
pub key: Credentials,
pub channel: WifiChannel,
pub ssid_hidden: bool,
pub ip: Ipv4Addr,
}
impl From<Credentials> for AuthType {
fn from(cred: Credentials) -> Self {
match cred {
Credentials::Open => Self::Open,
Credentials::WpaPSK(_) => Self::WpaPSK,
#[cfg(feature = "wep")]
Credentials::Wep(_, _) => Self::WEP,
Credentials::S802_1X(_, _) => Self::S802_1X,
}
}
}
impl From<Credentials> for u8 {
fn from(val: Credentials) -> Self {
match val {
Credentials::Open => 1,
Credentials::WpaPSK(_) => 2,
#[cfg(feature = "wep")]
Credentials::Wep(_, _) => 3,
Credentials::S802_1X(_, _) => 4,
}
}
}
impl Credentials {
pub fn key_len(&self) -> usize {
match self {
Credentials::Open => 0,
#[cfg(feature = "wep")]
Credentials::Wep(key, _) => key.len(),
Credentials::WpaPSK(key) => key.len(),
Credentials::S802_1X(_, key) => key.len(),
}
}
}
impl<'a> AccessPoint<'a> {
pub fn new(
ssid: &'a Ssid,
key: Credentials,
channel: WifiChannel,
ssid_hidden: bool,
ip: Ipv4Addr,
) -> Result<Self, StackError> {
let octets = ip.octets();
let auth = <Credentials as Into<AuthType>>::into(key);
if !((1..100).contains(&octets[3])) {
return Err(StackError::InvalidParameters);
}
if auth == AuthType::S802_1X {
return Err(StackError::InvalidParameters);
}
Ok(Self {
ssid,
key,
channel,
ssid_hidden,
ip: Ipv4Addr::from(octets),
})
}
pub fn open(ssid: &'a Ssid) -> Self {
Self {
ssid,
key: Credentials::Open,
channel: WifiChannel::Channel1,
ssid_hidden: false,
ip: Ipv4Addr::from_bits(DEFAULT_AP_IP),
}
}
#[cfg(feature = "wep")]
pub fn wep(ssid: &'a Ssid, key: &'a WepKey, key_index: WepKeyIndex) -> Self {
Self {
ssid,
key: Credentials::Wep(*key, key_index),
channel: WifiChannel::Channel1,
ssid_hidden: false,
ip: Ipv4Addr::from_bits(DEFAULT_AP_IP),
}
}
pub fn wpa(ssid: &'a Ssid, key: &'a WpaKey) -> Self {
Self {
ssid,
key: Credentials::WpaPSK(*key),
channel: WifiChannel::Channel1,
ssid_hidden: false,
ip: Ipv4Addr::from_bits(DEFAULT_AP_IP),
}
}
pub fn set_ip(&mut self, ip: Ipv4Addr) -> Result<(), StackError> {
let octets = ip.octets();
if !((1..100).contains(&octets[3])) {
return Err(StackError::InvalidParameters);
}
self.ip = Ipv4Addr::from(octets);
Ok(())
}
pub fn set_channel(&mut self, channel: WifiChannel) {
self.channel = channel;
}
pub fn set_ssid_hidden(&mut self, hidden: bool) {
self.ssid_hidden = hidden;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ap_set_channel() {
let ssid = Ssid::from("test").unwrap();
let mut ap = AccessPoint::open(&ssid);
assert_eq!(ap.channel, WifiChannel::Channel1);
ap.set_channel(WifiChannel::Channel2);
assert_eq!(ap.channel, WifiChannel::Channel2);
}
#[test]
fn test_ap_set_ip_fail() {
let ssid = Ssid::from("test").unwrap();
let psk = WpaKey::from("test_key").unwrap();
let mut ap = AccessPoint::wpa(&ssid, &psk);
let ip = Ipv4Addr::new(192, 168, 1, 100);
assert_eq!(ap.ip, Ipv4Addr::from_bits(DEFAULT_AP_IP));
let result = ap.set_ip(ip);
assert_eq!(result, Err(StackError::InvalidParameters))
}
#[test]
fn test_ap_set_ip_success() {
let ssid = Ssid::from("test").unwrap();
let psk = WpaKey::from("test_key").unwrap();
let mut ap = AccessPoint::wpa(&ssid, &psk);
let ip = Ipv4Addr::new(192, 168, 1, 1);
assert_eq!(ap.ip, Ipv4Addr::from_bits(DEFAULT_AP_IP));
let result = ap.set_ip(ip);
assert!(result.is_ok());
}
#[test]
fn test_ap_config_fail() {
let ssid = Ssid::from("test").unwrap();
let psk = WpaKey::from("test_key").unwrap();
let ap = AccessPoint::new(
&ssid,
Credentials::WpaPSK(psk),
WifiChannel::Channel1,
false,
Ipv4Addr::new(192, 168, 1, 100),
);
assert_eq!(ap, Err(StackError::InvalidParameters));
}
#[test]
fn test_ap_config_success() {
let ssid = Ssid::from("test").unwrap();
let psk = WpaKey::from("test_key").unwrap();
let ap = AccessPoint::new(
&ssid,
Credentials::WpaPSK(psk),
WifiChannel::Channel1,
false,
Ipv4Addr::new(192, 168, 1, 1),
);
assert!(ap.is_ok());
}
#[test]
fn test_ap_config_enterprise_fail() {
let ssid = Ssid::from("test").unwrap();
let username = S8Username::from("username").unwrap();
let password = S8Password::from("password").unwrap();
let ap = AccessPoint::new(
&ssid,
Credentials::S802_1X(username, password),
WifiChannel::Channel1,
false,
Ipv4Addr::new(192, 168, 1, 1),
);
assert_eq!(ap, Err(StackError::InvalidParameters));
}
#[test]
fn test_ssid_visibility() {
let ssid = Ssid::from("test").unwrap();
let mut ap = AccessPoint::open(&ssid);
assert_eq!(ap.ssid_hidden, false);
ap.set_ssid_hidden(true);
assert_eq!(ap.ssid_hidden, true);
}
}