1use std::str;
4
5use super::types::{MqttPacketType, MqttProperty, QoS};
6
7pub fn read_remaining_length(buf: &[u8]) -> Option<(u32, usize)> {
9 let mut value: u32 = 0;
10 let mut multiplier = 1u32;
11 for (i, &byte) in buf.iter().enumerate() {
12 value += (byte as u32 & 0x7F) * multiplier;
13 if byte & 0x80 == 0 { return Some((value, i + 1)); }
14 multiplier *= 128;
15 if multiplier > 128 * 128 * 128 { return None; }
16 }
17 None
18}
19
20pub fn write_remaining_length(len: u32) -> Vec<u8> {
21 let mut out = Vec::new();
22 let mut v = len;
23 loop {
24 let mut byte = (v % 128) as u8;
25 v /= 128;
26 if v > 0 { byte |= 0x80; }
27 out.push(byte);
28 if v == 0 { break; }
29 }
30 out
31}
32
33pub struct MqttPacket<'a> {
35 buf: &'a [u8],
36 header_len: usize,
37 remaining_len: u32,
38}
39
40impl<'a> MqttPacket<'a> {
41 pub fn parse(buf: &'a [u8]) -> Option<Self> {
42 if buf.is_empty() { return None; }
43 let (remaining_len, header_len) = read_remaining_length(&buf[1..])?;
44 Some(Self { buf, header_len: 1 + header_len, remaining_len })
45 }
46
47 pub fn packet_type(&self) -> MqttPacketType { MqttPacketType::from(self.buf[0]) }
48 pub fn flags(&self) -> u8 { self.buf[0] & 0x0F }
49 pub fn qos(&self) -> QoS { QoS::from(self.flags()) }
50 pub fn remaining_length(&self) -> u32 { self.remaining_len }
51 pub fn variable_header(&self) -> &'a [u8] { &self.buf[self.header_len..] }
52 pub fn payload(&self) -> &'a [u8] {
53 let vh = self.variable_header();
54 let vh_len = self._vh_len();
55 &vh[vh_len..]
56 }
57
58 fn _vh_len(&self) -> usize {
59 match self.packet_type() {
60 MqttPacketType::Connect => {
61 let vh = self.variable_header();
62 if vh.len() < 6 { return 0; }
63 let plen = u16::from_be_bytes([vh[0], vh[1]]) as usize;
64 let mut pos = 2 + plen; if pos + 4 > vh.len() { return pos; }
66 pos += 1; let flags = vh[pos]; pos += 1; pos += 2; if vh[pos-4] == 5 { if let Some((_, plen)) = read_remaining_length(&vh[pos..]) { pos += plen; }
72 }
73 if pos + 2 > vh.len() { return pos; }
75 let cid_len = u16::from_be_bytes([vh[pos], vh[pos+1]]) as usize;
76 pos += 2 + cid_len;
77 if flags & 0x04 != 0 && pos + 2 <= vh.len() {
79 if vh[2 + plen] == 5 {
81 if let Some((_, plen)) = read_remaining_length(&vh[pos..]) { pos += plen; }
82 }
83 if pos + 2 <= vh.len() {
84 let wt_len = u16::from_be_bytes([vh[pos], vh[pos+1]]) as usize;
85 pos += 2 + wt_len;
86 if pos + 2 <= vh.len() {
87 let wm_len = u16::from_be_bytes([vh[pos], vh[pos+1]]) as usize;
88 pos += 2 + wm_len;
89 }
90 }
91 }
92 if flags & 0x80 != 0 && pos + 2 <= vh.len() {
94 let ulen = u16::from_be_bytes([vh[pos], vh[pos+1]]) as usize;
95 pos += 2 + ulen;
96 }
97 if flags & 0x40 != 0 && pos + 2 <= vh.len() {
99 let plen = u16::from_be_bytes([vh[pos], vh[pos+1]]) as usize;
100 pos += 2 + plen;
101 }
102 pos
103 }
104 MqttPacketType::Connack => {
105 let vh = self.variable_header();
106 if vh.is_empty() { return 0; }
107 let mut pos = 2; if vh.len() > 2 { if let Some((_, plen)) = read_remaining_length(&vh[pos..]) { pos += plen; }
110 }
111 pos
112 }
113 MqttPacketType::Publish => {
114 let vh = self.variable_header();
115 if vh.len() < 2 { return 0; }
116 let tlen = u16::from_be_bytes([vh[0], vh[1]]) as usize;
117 let mut pos = 2 + tlen;
118 if self.qos() as u8 > 0 { pos += 2; } pos
120 }
121 MqttPacketType::Subscribe => {
122 let vh = self.variable_header();
123 if vh.len() < 2 { return 0; }
124 2 }
126 _ => 0,
127 }
128 }
129
130 pub fn message(&self) -> MqttMessage<'a> {
131 let vh = self.variable_header();
132 match self.packet_type() {
133 MqttPacketType::Connect => {
134 if vh.len() < 6 { return MqttMessage::Unknown { packet_type: MqttPacketType::Connect, data: vh }; }
135 let plen = u16::from_be_bytes([vh[0], vh[1]]) as usize;
136 let proto = str::from_utf8(&vh[2..2+plen]).unwrap_or("");
137 let level = vh[2+plen];
138 let flags = vh[3+plen];
139 let keep_alive = u16::from_be_bytes([vh[4+plen], vh[5+plen]]);
140 let mut pos = 6 + plen;
141 let properties = if level == 5 {
143 parse_properties(&vh[pos..]).unwrap_or_default()
144 } else { vec![] };
145 if level == 5 { if let Some((_, pl)) = read_remaining_length(&vh[pos..]) { pos += pl; } }
146 let cid_len = if pos + 2 <= vh.len() { u16::from_be_bytes([vh[pos], vh[pos+1]]) as usize } else { 0 };
148 pos += 2;
149 let client_id = str::from_utf8(&vh[pos..pos+cid_len]).unwrap_or("");
150 pos += cid_len;
151 let (will_topic, will_message) = if flags & 0x04 != 0 {
153 let mut wt = None; let mut wm = None;
154 if level == 5 { if let Some((_, pl)) = read_remaining_length(&vh[pos..]) { pos += pl; } }
155 if pos + 2 <= vh.len() { let wtl = u16::from_be_bytes([vh[pos], vh[pos+1]]) as usize; pos += 2; if pos+wtl <= vh.len() { wt = str::from_utf8(&vh[pos..pos+wtl]).ok(); pos += wtl; } }
156 if pos + 2 <= vh.len() { let wml = u16::from_be_bytes([vh[pos], vh[pos+1]]) as usize; pos += 2; if pos+wml <= vh.len() { wm = Some(&vh[pos..pos+wml]); pos += wml; } }
157 (wt, wm)
158 } else { (None, None) };
159 let username = if flags & 0x80 != 0 { if pos+2<=vh.len() { let ul = u16::from_be_bytes([vh[pos],vh[pos+1]]) as usize; pos+=2; str::from_utf8(&vh[pos..pos+ul]).ok() } else { None } } else { None };
161 let password = if flags & 0x40 != 0 { if pos+2<=vh.len() { let pl = u16::from_be_bytes([vh[pos],vh[pos+1]]) as usize; pos+=2; Some(&vh[pos..pos+pl]) } else { None } } else { None };
162 MqttMessage::Connect {
163 protocol_name: proto, protocol_level: level,
164 clean_start: flags & 0x02 != 0, keep_alive,
165 client_id, will_topic, will_message, username, password, properties,
166 }
167 }
168 MqttPacketType::Publish => {
169 if vh.len() < 2 { return MqttMessage::Unknown { packet_type: MqttPacketType::Publish, data: vh }; }
170 let tlen = u16::from_be_bytes([vh[0], vh[1]]) as usize;
171 let topic = str::from_utf8(&vh[2..2+tlen]).unwrap_or("");
172 let mut pos = 2 + tlen;
173 let packet_id = if self.qos() as u8 > 0 && pos + 2 <= vh.len() { let id = u16::from_be_bytes([vh[pos], vh[pos+1]]); pos += 2; Some(id) } else { None };
174 let payload = &vh[pos..];
175 MqttMessage::Publish { topic, packet_id, payload }
176 }
177 MqttPacketType::Subscribe => {
178 let packet_id = if vh.len() >= 2 { u16::from_be_bytes([vh[0], vh[1]]) } else { 0 };
179 let topics = if vh.len() > 2 {
180 let data = &vh[2..];
181 let mut topics = Vec::new();
182 let mut p = 0;
183 while p + 3 <= data.len() {
184 let tl = u16::from_be_bytes([data[p], data[p+1]]) as usize;
185 p += 2;
186 if p + tl < data.len() {
187 let tf = str::from_utf8(&data[p..p+tl]).unwrap_or("");
188 topics.push((tf, data[p+tl]));
189 p += tl + 1;
190 } else { break; }
191 }
192 topics
193 } else { vec![] };
194 MqttMessage::Subscribe { packet_id, topics }
195 }
196 _ => MqttMessage::Unknown { packet_type: self.packet_type(), data: vh },
197 }
198 }
199}
200
201fn parse_properties(data: &[u8]) -> Option<Vec<MqttProperty>> {
202 let (prop_len, mut pos) = read_remaining_length(data)?;
203 let end = (pos + prop_len as usize).min(data.len());
204 let mut props = Vec::new();
205 while pos < end {
206 let id = data[pos]; pos += 1;
207 match id {
208 0x11 => { if pos+4<=end { props.push(MqttProperty::SessionExpiryInterval(u32::from_be_bytes([data[pos],data[pos+1],data[pos+2],data[pos+3]]))); pos+=4; } }
209 0x27 => { if pos+4<=end { props.push(MqttProperty::MaximumPacketSize(u32::from_be_bytes([data[pos],data[pos+1],data[pos+2],data[pos+3]]))); pos+=4; } }
210 0x23 => { if pos+2<=end { props.push(MqttProperty::TopicAlias(u16::from_be_bytes([data[pos],data[pos+1]]))); pos+=2; } }
211 0x19 => { props.push(MqttProperty::RequestResponseInfo(data.get(pos).copied().unwrap_or(0) != 0)); pos += 1; }
212 _ => { let mut v = vec![id]; while pos < end { v.push(data[pos]); pos += 1; } props.push(MqttProperty::Unknown(id, v)); }
213 }
214 }
215 Some(props)
216}
217
218#[derive(Debug, Clone)]
220pub enum MqttMessage<'a> {
221 Connect {
222 protocol_name: &'a str, protocol_level: u8, clean_start: bool, keep_alive: u16,
223 client_id: &'a str, will_topic: Option<&'a str>, will_message: Option<&'a [u8]>,
224 username: Option<&'a str>, password: Option<&'a [u8]>, properties: Vec<MqttProperty>,
225 },
226 Publish { topic: &'a str, packet_id: Option<u16>, payload: &'a [u8] },
227 Subscribe { packet_id: u16, topics: Vec<(&'a str, u8)> },
228 Unknown { packet_type: MqttPacketType, data: &'a [u8] },
229}
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234
235 #[test]
236 fn remaining_length_encoding() {
237 assert_eq!(read_remaining_length(&[0x00]).unwrap(), (0, 1));
238 assert_eq!(read_remaining_length(&[0x7F]).unwrap(), (127, 1));
239 assert_eq!(read_remaining_length(&[0x80, 0x01]).unwrap(), (128, 2));
240 assert_eq!(read_remaining_length(&[0xFF, 0x7F]).unwrap(), (16383, 2));
241 assert_eq!(write_remaining_length(128), vec![0x80, 0x01]);
242 assert_eq!(write_remaining_length(127), vec![0x7F]);
243 }
244
245 #[test]
246 fn parse_mqtt311_connect() {
247 let mut data = vec![0x10u8]; let mut payload = vec![0x00, 0x04, b'M', b'Q', b'T', b'T']; payload.push(4); payload.push(0x02); payload.extend_from_slice(&60u16.to_be_bytes()); payload.extend_from_slice(&[0x00, 0x04, b't', b'e', b's', b't']); data.extend_from_slice(&write_remaining_length(payload.len() as u32));
256 data.extend_from_slice(&payload);
257
258 let pkt = MqttPacket::parse(&data).unwrap();
259 assert_eq!(pkt.packet_type(), MqttPacketType::Connect);
260 match pkt.message() {
261 MqttMessage::Connect { client_id, clean_start, keep_alive, .. } => {
262 assert_eq!(client_id, "test"); assert!(clean_start); assert_eq!(keep_alive, 60);
263 }
264 _ => panic!("expected Connect"),
265 }
266 }
267
268 #[test]
269 fn parse_mqtt_publish() {
270 let topic = b"test/topic";
271 let payload = b"hello";
272 let mut vh = vec![];
273 vh.extend_from_slice(&(topic.len() as u16).to_be_bytes());
274 vh.extend_from_slice(topic);
275 let mut data = vec![0x30u8]; data.extend_from_slice(&write_remaining_length((vh.len() + payload.len()) as u32));
278 data.extend_from_slice(&vh);
279 data.extend_from_slice(payload);
280
281 let pkt = MqttPacket::parse(&data).unwrap();
282 match pkt.message() {
283 MqttMessage::Publish { topic: t, payload: p, .. } => {
284 assert_eq!(t, "test/topic"); assert_eq!(p, b"hello");
285 }
286 _ => panic!("expected Publish"),
287 }
288 }
289}