rbroadlink/network/
wireless_connection.rs

1use packed_struct::prelude::PackedStruct;
2
3use crate::network::util::checksum;
4
5/// WirelessConnection represents the credentials for connecting to a wireless
6/// network.
7#[derive(Debug)]
8pub enum WirelessConnection<'a> {
9    /// None represents a network with no security
10    None(&'a str),
11
12    /// WEP represents a network with WEP security key.
13    WEP(&'a str, &'a str),
14
15    /// WPA1 represents a network with a WPA v1 security key.
16    WPA1(&'a str, &'a str),
17
18    /// WPA represents a network with a WPA v2 security key.
19    WPA2(&'a str, &'a str),
20
21    /// WPA represents a network with both WPA v1 and WPA v2 support.
22    WPA(&'a str, &'a str),
23}
24
25/// WirelessConnectionMessage represents a message used for instructing a device
26/// to connect to a specified wireless network.
27#[derive(PackedStruct, Debug)]
28#[packed_struct(bit_numbering = "msb0", endian = "lsb", size_bytes = "136")]
29pub struct WirelessConnectionMessage {
30    /// The message's checksum for verification purposes.
31    #[packed_field(bytes = "32:33")]
32    checksum: u16,
33
34    /// A magic constant for this message. Always 0x14
35    #[packed_field(bytes = "38")]
36    magic_constant: i8,
37
38    /// The SSID of the network.
39    #[packed_field(bytes = "68:99")]
40    ssid: [u8; 32],
41
42    /// The password of the network, if needed.
43    #[packed_field(bytes = "100:131")]
44    password: [u8; 32],
45
46    /// Length of the SSID
47    #[packed_field(bytes = "132")]
48    ssid_length: u8,
49
50    /// Length of the password
51    #[packed_field(bytes = "133")]
52    password_length: u8,
53
54    /// The security mode to use. 0 -> None, 1 -> WEP, 2 -> WPA1, 3 -> WPA2, 4 -> WPA
55    #[packed_field(bytes = "134")]
56    security_mode: u8,
57}
58
59impl WirelessConnection<'_> {
60    /// Pack a WirelessCOnnection into its network transport format.
61    pub fn to_message(&self) -> Result<WirelessConnectionMessage, String> {
62        let empty_pass = "";
63        let (ssid, pass, security_mode) = match self {
64            WirelessConnection::None(ssid) => (ssid, &empty_pass, 0),
65            WirelessConnection::WEP(ssid, pass) => (ssid, pass, 1),
66            WirelessConnection::WPA1(ssid, pass) => (ssid, pass, 2),
67            WirelessConnection::WPA2(ssid, pass) => (ssid, pass, 3),
68            WirelessConnection::WPA(ssid, pass) => (ssid, pass, 4),
69        };
70
71        // Ensure that the fields aren't too long
72        if ssid.len() > 32 {
73            return Err("Could not use provided SSID! SSID longer than 32 characters.".into());
74        }
75
76        // Copy over the strings into their fixed buffers
77        let mut ssid_fixed = [0u8; 32];
78        let mut pass_fixed = [0u8; 32];
79        for (i, c) in ssid.as_bytes().iter().enumerate() {
80            ssid_fixed[i] = *c;
81        }
82        for (i, c) in pass.as_bytes().iter().enumerate() {
83            pass_fixed[i] = *c;
84        }
85
86        // Construct the message
87        let mut msg = WirelessConnectionMessage {
88            // We will need to recalculate this after creating the message
89            checksum: 0,
90
91            // This is always 0x14
92            magic_constant: 0x14,
93
94            // Grab info from connection
95            ssid: ssid_fixed,
96            password: pass_fixed,
97            ssid_length: u8::try_from(ssid.len()).map_err(|e| {
98                format!(
99                    "Could not use provided SSID! SSID is too long (max 32 characters). {}",
100                    e
101                )
102            })?,
103            password_length: u8::try_from(pass.len()).map_err(|e| {
104                format!(
105                    "Could not use provided password! Password is too long (max 32 characters). {}",
106                    e
107                )
108            })?,
109
110            security_mode: security_mode,
111        };
112
113        // Add the checksum into the msg
114        msg.checksum = checksum(
115            &msg.pack()
116                .map_err(|e| format!("Could not pack WirelessConnectionMessage! {}", e))?,
117        );
118
119        // Return the newly created message
120        return Ok(msg);
121    }
122}