mqttrust/encoding/v4/
connect.rs

1use super::{decoder::*, encoder::*, *};
2
3/// Protocol version.
4///
5/// Sent in [`Connect`] packet.
6///
7/// [`Connect`]: struct.Connect.html
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum Protocol {
10    /// [MQTT 3.1.1] is the most commonly implemented version.
11    ///
12    /// [MQTT 3.1.1]: https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html
13    /// [MQTT 5]: https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html
14    MQTT311,
15    /// MQIsdp, aka SCADA are pre-standardisation names of MQTT. It should mostly conform to MQTT
16    /// 3.1.1, but you should watch out for implementation discrepancies.
17    MQIsdp,
18}
19impl Protocol {
20    pub(crate) fn new(name: &str, level: u8) -> Result<Protocol, Error> {
21        match (name, level) {
22            ("MQIsdp", 3) => Ok(Protocol::MQIsdp),
23            ("MQTT", 4) => Ok(Protocol::MQTT311),
24            _ => Err(Error::InvalidProtocol(name.into(), level)),
25        }
26    }
27    pub(crate) fn from_buffer(buf: &[u8], offset: &mut usize) -> Result<Self, Error> {
28        let protocol_name = read_str(buf, offset)?;
29        let protocol_level = buf[*offset];
30        *offset += 1;
31
32        Protocol::new(protocol_name, protocol_level)
33    }
34    pub(crate) fn to_buffer(&self, buf: &mut [u8], offset: &mut usize) -> Result<usize, Error> {
35        match self {
36            Protocol::MQTT311 => {
37                let slice = &[0u8, 4, b'M', b'Q', b'T', b'T', 4];
38                for &byte in slice {
39                    write_u8(buf, offset, byte)?;
40                }
41                Ok(slice.len())
42            }
43            Protocol::MQIsdp => {
44                let slice = &[0u8, 4, b'M', b'Q', b'i', b's', b'd', b'p', 4];
45                for &byte in slice {
46                    write_u8(buf, offset, byte)?;
47                }
48                Ok(slice.len())
49            }
50        }
51    }
52}
53
54/// Message that the server should publish when the client disconnects.
55///
56/// Sent by the client in the [Connect] packet. [MQTT 3.1.3.3].
57///
58/// [Connect]: struct.Connect.html
59/// [MQTT 3.1.3.3]: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718031
60#[derive(Debug, Clone, PartialEq)]
61pub struct LastWill<'a> {
62    pub topic: &'a str,
63    pub message: &'a [u8],
64    pub qos: QoS,
65    pub retain: bool,
66}
67
68/// Sucess value of a [Connack] packet.
69///
70/// See [MQTT 3.2.2.3] for interpretations.
71///
72/// [Connack]: struct.Connack.html
73/// [MQTT 3.2.2.3]: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718035
74#[derive(Debug, Clone, Copy, PartialEq)]
75#[cfg_attr(feature = "defmt-impl", derive(defmt::Format))]
76pub enum ConnectReturnCode {
77    Accepted,
78    RefusedProtocolVersion,
79    RefusedIdentifierRejected,
80    ServerUnavailable,
81    BadUsernamePassword,
82    NotAuthorized,
83}
84impl ConnectReturnCode {
85    fn as_u8(&self) -> u8 {
86        match *self {
87            ConnectReturnCode::Accepted => 0,
88            ConnectReturnCode::RefusedProtocolVersion => 1,
89            ConnectReturnCode::RefusedIdentifierRejected => 2,
90            ConnectReturnCode::ServerUnavailable => 3,
91            ConnectReturnCode::BadUsernamePassword => 4,
92            ConnectReturnCode::NotAuthorized => 5,
93        }
94    }
95    pub(crate) fn from_u8(byte: u8) -> Result<ConnectReturnCode, Error> {
96        match byte {
97            0 => Ok(ConnectReturnCode::Accepted),
98            1 => Ok(ConnectReturnCode::RefusedProtocolVersion),
99            2 => Ok(ConnectReturnCode::RefusedIdentifierRejected),
100            3 => Ok(ConnectReturnCode::ServerUnavailable),
101            4 => Ok(ConnectReturnCode::BadUsernamePassword),
102            5 => Ok(ConnectReturnCode::NotAuthorized),
103            n => Err(Error::InvalidConnectReturnCode(n)),
104        }
105    }
106}
107
108/// Connect packet ([MQTT 3.1]).
109///
110/// [MQTT 3.1]: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718028
111#[derive(Debug, Clone, PartialEq)]
112pub struct Connect<'a> {
113    pub protocol: Protocol,
114    pub keep_alive: u16,
115    pub client_id: &'a str,
116    pub clean_session: bool,
117    pub last_will: Option<LastWill<'a>>,
118    pub username: Option<&'a str>,
119    pub password: Option<&'a [u8]>,
120}
121
122/// Connack packet ([MQTT 3.2]).
123///
124/// [MQTT 3.2]: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718033
125#[derive(Debug, Clone, Copy, PartialEq)]
126pub struct Connack {
127    pub session_present: bool,
128    pub code: ConnectReturnCode,
129}
130
131impl<'a> Connect<'a> {
132    pub(crate) fn from_buffer(buf: &'a [u8], offset: &mut usize) -> Result<Self, Error> {
133        let protocol = Protocol::from_buffer(buf, offset)?;
134
135        let connect_flags = buf[*offset];
136        let keep_alive = ((buf[*offset + 1] as u16) << 8) | buf[*offset + 2] as u16;
137        *offset += 3;
138
139        let client_id = read_str(buf, offset)?;
140
141        let last_will = if connect_flags & 0b100 != 0 {
142            let will_topic = read_str(buf, offset)?;
143            let will_message = read_bytes(buf, offset)?;
144            let will_qod = QoS::from_u8((connect_flags & 0b11000) >> 3)?;
145            Some(LastWill {
146                topic: will_topic,
147                message: will_message,
148                qos: will_qod,
149                retain: (connect_flags & 0b00100000) != 0,
150            })
151        } else {
152            None
153        };
154
155        let username = if connect_flags & 0b10000000 != 0 {
156            Some(read_str(buf, offset)?)
157        } else {
158            None
159        };
160
161        let password = if connect_flags & 0b01000000 != 0 {
162            Some(read_bytes(buf, offset)?)
163        } else {
164            None
165        };
166
167        let clean_session = (connect_flags & 0b10) != 0;
168
169        Ok(Connect {
170            protocol,
171            keep_alive,
172            client_id,
173            clean_session,
174            last_will,
175            username,
176            password,
177        })
178    }
179
180    pub(crate) fn len(&self) -> usize {
181        let mut length: usize = 6 + 1 + 1; // NOTE: protocol_name(6) + protocol_level(1) + flags(1);
182        length += 2 + self.client_id.len();
183        length += 2; // keep alive
184        if let Some(username) = self.username {
185            length += username.len();
186            length += 2;
187        };
188        if let Some(password) = self.password {
189            length += password.len();
190            length += 2;
191        };
192        if let Some(last_will) = &self.last_will {
193            length += last_will.message.len();
194            length += last_will.topic.len();
195            length += 4;
196        };
197        length
198    }
199
200    pub(crate) fn to_buffer(&self, buf: &mut [u8], offset: &mut usize) -> Result<usize, Error> {
201        let header: u8 = 0b00010000;
202        let mut connect_flags: u8 = 0b00000000;
203        if self.clean_session {
204            connect_flags |= 0b10;
205        };
206        if self.username.is_some() {
207            connect_flags |= 0b10000000;
208        };
209        if self.password.is_some() {
210            connect_flags |= 0b01000000;
211        };
212        if let Some(last_will) = &self.last_will {
213            connect_flags |= 0b00000100;
214            connect_flags |= last_will.qos.as_u8() << 3;
215            if last_will.retain {
216                connect_flags |= 0b00100000;
217            };
218        };
219        let length = self.len();
220        check_remaining(buf, offset, length + 1)?;
221
222        // NOTE: putting data into buffer.
223        write_u8(buf, offset, header)?;
224
225        let write_len = write_length(buf, offset, length)? + 1;
226        self.protocol.to_buffer(buf, offset)?;
227
228        write_u8(buf, offset, connect_flags)?;
229        write_u16(buf, offset, self.keep_alive)?;
230
231        write_string(buf, offset, self.client_id)?;
232
233        if let Some(last_will) = &self.last_will {
234            write_string(buf, offset, last_will.topic)?;
235            write_bytes(buf, offset, &last_will.message)?;
236        };
237
238        if let Some(username) = self.username {
239            write_string(buf, offset, username)?;
240        };
241        if let Some(password) = self.password {
242            write_bytes(buf, offset, password)?;
243        };
244        // NOTE: END
245        Ok(write_len)
246    }
247}
248
249impl Connack {
250    pub(crate) fn from_buffer(buf: &[u8], offset: &mut usize) -> Result<Self, Error> {
251        let flags = buf[*offset];
252        let return_code = buf[*offset + 1];
253        *offset += 2;
254        Ok(Connack {
255            session_present: (flags & 0b1 == 1),
256            code: ConnectReturnCode::from_u8(return_code)?,
257        })
258    }
259    pub(crate) fn to_buffer(&self, buf: &mut [u8], offset: &mut usize) -> Result<usize, Error> {
260        check_remaining(buf, offset, 4)?;
261        let header: u8 = 0b00100000;
262        let length: u8 = 2;
263        let mut flags: u8 = 0b00000000;
264        if self.session_present {
265            flags |= 0b1;
266        };
267        let rc = self.code.as_u8();
268        write_u8(buf, offset, header)?;
269        write_u8(buf, offset, length)?;
270        write_u8(buf, offset, flags)?;
271        write_u8(buf, offset, rc)?;
272        Ok(4)
273    }
274}