1pub mod builder;
63
64pub use builder::MqttSnBuilder;
65
66use crate::layer::field::{FieldError, FieldValue};
67use crate::layer::{Layer, LayerIndex, LayerKind};
68
69pub const MQTTSN_MIN_HEADER_LEN: usize = 2;
75
76pub const MQTTSN_PORT: u16 = 1883;
78
79pub const ADVERTISE: u8 = 0x00;
81pub const SEARCHGW: u8 = 0x01;
82pub const GWINFO: u8 = 0x02;
83pub const CONNECT: u8 = 0x04;
84pub const CONNACK: u8 = 0x05;
85pub const WILLTOPICREQ: u8 = 0x06;
86pub const WILLTOPIC: u8 = 0x07;
87pub const WILLMSGREQ: u8 = 0x08;
88pub const WILLMSG: u8 = 0x09;
89pub const REGISTER: u8 = 0x0A;
90pub const REGACK: u8 = 0x0B;
91pub const PUBLISH: u8 = 0x0C;
92pub const PUBACK: u8 = 0x0D;
93pub const PUBCOMP: u8 = 0x0E;
94pub const PUBREC: u8 = 0x0F;
95pub const PUBREL: u8 = 0x10;
96pub const SUBSCRIBE: u8 = 0x12;
97pub const SUBACK: u8 = 0x13;
98pub const UNSUBSCRIBE: u8 = 0x14;
99pub const UNSUBACK: u8 = 0x15;
100pub const PINGREQ: u8 = 0x16;
101pub const PINGRESP: u8 = 0x17;
102pub const DISCONNECT: u8 = 0x18;
103pub const WILLTOPICUPD: u8 = 0x1A;
104pub const WILLTOPICRESP: u8 = 0x1B;
105pub const WILLMSGUPD: u8 = 0x1C;
106pub const WILLMSGRESP: u8 = 0x1D;
107pub const ENCAPS_MSG: u8 = 0xFE;
108
109pub const RC_ACCEPTED: u8 = 0x00;
111pub const RC_REJ_CONGESTION: u8 = 0x01;
112pub const RC_REJ_INVALID_TID: u8 = 0x02;
113pub const RC_REJ_NOT_SUPPORTED: u8 = 0x03;
114
115pub const TID_NORMAL: u8 = 0b00;
117pub const TID_PREDEF: u8 = 0b01;
118pub const TID_SHORT: u8 = 0b10;
119
120pub static MQTTSN_FIELD_NAMES: &[&str] = &[
122 "length",
123 "type",
124 "flags",
125 "dup",
126 "qos",
127 "retain",
128 "will",
129 "cleansess",
130 "tid_type",
131 "gw_id",
132 "duration",
133 "radius",
134 "gw_addr",
135 "prot_id",
136 "client_id",
137 "return_code",
138 "tid",
139 "mid",
140 "data",
141 "topic_name",
142 "will_topic",
143 "will_msg",
144];
145
146pub fn decode_mqttsn_length(buf: &[u8]) -> Result<(usize, u16), FieldError> {
158 if buf.is_empty() {
159 return Err(FieldError::BufferTooShort {
160 offset: 0,
161 need: 1,
162 have: 0,
163 });
164 }
165 if buf[0] == 0x01 {
166 if buf.len() < 3 {
168 return Err(FieldError::BufferTooShort {
169 offset: 0,
170 need: 3,
171 have: buf.len(),
172 });
173 }
174 let len = u16::from_be_bytes([buf[1], buf[2]]);
175 Ok((3, len))
176 } else {
177 Ok((1, u16::from(buf[0])))
178 }
179}
180
181#[must_use]
188pub fn is_mqttsn_payload(buf: &[u8]) -> bool {
189 if buf.len() < MQTTSN_MIN_HEADER_LEN {
190 return false;
191 }
192 let (len_hdr_size, pkt_len) = match decode_mqttsn_length(buf) {
193 Ok(v) => v,
194 Err(_) => return false,
195 };
196 if (pkt_len as usize) < len_hdr_size + 1 {
198 return false;
199 }
200 if (pkt_len as usize) > buf.len() {
202 return false;
203 }
204 let msg_type = buf[len_hdr_size];
205 is_known_message_type(msg_type)
206}
207
208fn is_known_message_type(t: u8) -> bool {
210 matches!(
211 t,
212 ADVERTISE
213 | SEARCHGW
214 | GWINFO
215 | CONNECT
216 | CONNACK
217 | WILLTOPICREQ
218 | WILLTOPIC
219 | WILLMSGREQ
220 | WILLMSG
221 | REGISTER
222 | REGACK
223 | PUBLISH
224 | PUBACK
225 | PUBCOMP
226 | PUBREC
227 | PUBREL
228 | SUBSCRIBE
229 | SUBACK
230 | UNSUBSCRIBE
231 | UNSUBACK
232 | PINGREQ
233 | PINGRESP
234 | DISCONNECT
235 | WILLTOPICUPD
236 | WILLTOPICRESP
237 | WILLMSGUPD
238 | WILLMSGRESP
239 | ENCAPS_MSG
240 )
241}
242
243#[must_use]
245pub fn message_type_name(t: u8) -> &'static str {
246 match t {
247 ADVERTISE => "ADVERTISE",
248 SEARCHGW => "SEARCHGW",
249 GWINFO => "GWINFO",
250 CONNECT => "CONNECT",
251 CONNACK => "CONNACK",
252 WILLTOPICREQ => "WILLTOPICREQ",
253 WILLTOPIC => "WILLTOPIC",
254 WILLMSGREQ => "WILLMSGREQ",
255 WILLMSG => "WILLMSG",
256 REGISTER => "REGISTER",
257 REGACK => "REGACK",
258 PUBLISH => "PUBLISH",
259 PUBACK => "PUBACK",
260 PUBCOMP => "PUBCOMP",
261 PUBREC => "PUBREC",
262 PUBREL => "PUBREL",
263 SUBSCRIBE => "SUBSCRIBE",
264 SUBACK => "SUBACK",
265 UNSUBSCRIBE => "UNSUBSCRIBE",
266 UNSUBACK => "UNSUBACK",
267 PINGREQ => "PINGREQ",
268 PINGRESP => "PINGRESP",
269 DISCONNECT => "DISCONNECT",
270 WILLTOPICUPD => "WILLTOPICUPD",
271 WILLTOPICRESP => "WILLTOPICRESP",
272 WILLMSGUPD => "WILLMSGUPD",
273 WILLMSGRESP => "WILLMSGRESP",
274 ENCAPS_MSG => "ENCAPS_MSG",
275 _ => "UNKNOWN",
276 }
277}
278
279#[must_use]
281pub fn return_code_name(rc: u8) -> &'static str {
282 match rc {
283 RC_ACCEPTED => "Accepted",
284 RC_REJ_CONGESTION => "Rejected: congestion",
285 RC_REJ_INVALID_TID => "Rejected: invalid topic ID",
286 RC_REJ_NOT_SUPPORTED => "Rejected: not supported",
287 _ => "Unknown",
288 }
289}
290
291#[derive(Debug, Clone)]
297pub struct MqttSnLayer {
298 pub index: LayerIndex,
299}
300
301impl MqttSnLayer {
302 #[must_use]
304 pub fn new(index: LayerIndex) -> Self {
305 Self { index }
306 }
307
308 fn slice<'a>(&self, buf: &'a [u8]) -> &'a [u8] {
310 self.index.slice(buf)
311 }
312
313 fn length_info(&self, buf: &[u8]) -> Result<(usize, u16), FieldError> {
315 let s = self.slice(buf);
316 decode_mqttsn_length(s)
317 }
318
319 fn type_offset(&self, buf: &[u8]) -> Result<usize, FieldError> {
321 let (len_hdr_size, _) = self.length_info(buf)?;
322 Ok(len_hdr_size)
323 }
324
325 fn body_offset(&self, buf: &[u8]) -> Result<usize, FieldError> {
327 Ok(self.type_offset(buf)? + 1)
328 }
329
330 pub fn packet_length(&self, buf: &[u8]) -> Result<u16, FieldError> {
336 let (_, len) = self.length_info(buf)?;
337 Ok(len)
338 }
339
340 pub fn msg_type(&self, buf: &[u8]) -> Result<u8, FieldError> {
342 let s = self.slice(buf);
343 let off = self.type_offset(buf)?;
344 if s.len() <= off {
345 return Err(FieldError::BufferTooShort {
346 offset: self.index.start + off,
347 need: 1,
348 have: 0,
349 });
350 }
351 Ok(s[off])
352 }
353
354 fn has_flags(&self, buf: &[u8]) -> Result<bool, FieldError> {
360 let mt = self.msg_type(buf)?;
361 Ok(matches!(
362 mt,
363 CONNECT | WILLTOPIC | PUBLISH | SUBSCRIBE | SUBACK | UNSUBSCRIBE | WILLTOPICUPD
364 ))
365 }
366
367 pub fn flags(&self, buf: &[u8]) -> Result<u8, FieldError> {
369 if !self.has_flags(buf)? {
370 return Err(FieldError::InvalidValue(format!(
371 "message type {} has no flags byte",
372 self.msg_type(buf)?
373 )));
374 }
375 let s = self.slice(buf);
376 let off = self.body_offset(buf)?;
377 if s.len() <= off {
378 return Err(FieldError::BufferTooShort {
379 offset: self.index.start + off,
380 need: 1,
381 have: 0,
382 });
383 }
384 Ok(s[off])
385 }
386
387 pub fn dup(&self, buf: &[u8]) -> Result<bool, FieldError> {
389 Ok((self.flags(buf)? >> 7) & 1 == 1)
390 }
391
392 pub fn qos(&self, buf: &[u8]) -> Result<u8, FieldError> {
394 Ok((self.flags(buf)? >> 5) & 0x03)
395 }
396
397 pub fn retain(&self, buf: &[u8]) -> Result<bool, FieldError> {
399 Ok((self.flags(buf)? >> 4) & 1 == 1)
400 }
401
402 pub fn will(&self, buf: &[u8]) -> Result<bool, FieldError> {
404 Ok((self.flags(buf)? >> 3) & 1 == 1)
405 }
406
407 pub fn cleansess(&self, buf: &[u8]) -> Result<bool, FieldError> {
409 Ok((self.flags(buf)? >> 2) & 1 == 1)
410 }
411
412 pub fn tid_type(&self, buf: &[u8]) -> Result<u8, FieldError> {
414 Ok(self.flags(buf)? & 0x03)
415 }
416
417 pub fn gw_id(&self, buf: &[u8]) -> Result<u8, FieldError> {
423 let mt = self.msg_type(buf)?;
424 if mt != ADVERTISE && mt != GWINFO {
425 return Err(FieldError::InvalidValue(format!(
426 "gw_id not available for type {}",
427 message_type_name(mt)
428 )));
429 }
430 let s = self.slice(buf);
431 let off = self.body_offset(buf)?;
432 if s.len() <= off {
433 return Err(FieldError::BufferTooShort {
434 offset: self.index.start + off,
435 need: 1,
436 have: 0,
437 });
438 }
439 Ok(s[off])
440 }
441
442 pub fn duration(&self, buf: &[u8]) -> Result<u16, FieldError> {
445 let mt = self.msg_type(buf)?;
446 let s = self.slice(buf);
447 let body = self.body_offset(buf)?;
448 let off = match mt {
449 ADVERTISE => body + 1, CONNECT => body + 1 + 1, DISCONNECT => body, _ => {
453 return Err(FieldError::InvalidValue(format!(
454 "duration not available for type {}",
455 message_type_name(mt)
456 )));
457 },
458 };
459 if s.len() < off + 2 {
460 return Err(FieldError::BufferTooShort {
461 offset: self.index.start + off,
462 need: 2,
463 have: s.len().saturating_sub(off),
464 });
465 }
466 Ok(u16::from_be_bytes([s[off], s[off + 1]]))
467 }
468
469 pub fn radius(&self, buf: &[u8]) -> Result<u8, FieldError> {
471 let mt = self.msg_type(buf)?;
472 if mt != SEARCHGW {
473 return Err(FieldError::InvalidValue(format!(
474 "radius not available for type {}",
475 message_type_name(mt)
476 )));
477 }
478 let s = self.slice(buf);
479 let off = self.body_offset(buf)?;
480 if s.len() <= off {
481 return Err(FieldError::BufferTooShort {
482 offset: self.index.start + off,
483 need: 1,
484 have: 0,
485 });
486 }
487 Ok(s[off])
488 }
489
490 pub fn gw_addr<'a>(&self, buf: &'a [u8]) -> Result<&'a [u8], FieldError> {
492 let mt = self.msg_type(buf)?;
493 if mt != GWINFO {
494 return Err(FieldError::InvalidValue(format!(
495 "gw_addr not available for type {}",
496 message_type_name(mt)
497 )));
498 }
499 let s = self.slice(buf);
500 let (_, pkt_len) = self.length_info(buf)?;
501 let off = self.body_offset(buf)? + 1; let end = (pkt_len as usize).min(s.len());
503 if off > end {
504 return Ok(&[]);
505 }
506 Ok(&s[off..end])
507 }
508
509 pub fn prot_id(&self, buf: &[u8]) -> Result<u8, FieldError> {
511 let mt = self.msg_type(buf)?;
512 if mt != CONNECT {
513 return Err(FieldError::InvalidValue(format!(
514 "prot_id not available for type {}",
515 message_type_name(mt)
516 )));
517 }
518 let s = self.slice(buf);
519 let off = self.body_offset(buf)? + 1; if s.len() <= off {
521 return Err(FieldError::BufferTooShort {
522 offset: self.index.start + off,
523 need: 1,
524 have: 0,
525 });
526 }
527 Ok(s[off])
528 }
529
530 pub fn client_id<'a>(&self, buf: &'a [u8]) -> Result<&'a str, FieldError> {
532 let mt = self.msg_type(buf)?;
533 let s = self.slice(buf);
534 let (_, pkt_len) = self.length_info(buf)?;
535 let body = self.body_offset(buf)?;
536 let off = match mt {
537 CONNECT => body + 1 + 1 + 2, PINGREQ => body, _ => {
540 return Err(FieldError::InvalidValue(format!(
541 "client_id not available for type {}",
542 message_type_name(mt)
543 )));
544 },
545 };
546 let end = (pkt_len as usize).min(s.len());
547 if off > end {
548 return Ok("");
549 }
550 std::str::from_utf8(&s[off..end]).map_err(|e| FieldError::InvalidValue(e.to_string()))
551 }
552
553 pub fn return_code(&self, buf: &[u8]) -> Result<u8, FieldError> {
555 let mt = self.msg_type(buf)?;
556 let s = self.slice(buf);
557 let body = self.body_offset(buf)?;
558 let off = match mt {
559 CONNACK | WILLTOPICRESP | WILLMSGRESP => body,
560 REGACK | PUBACK => body + 4, SUBACK => body + 1 + 2 + 2, _ => {
563 return Err(FieldError::InvalidValue(format!(
564 "return_code not available for type {}",
565 message_type_name(mt)
566 )));
567 },
568 };
569 if s.len() <= off {
570 return Err(FieldError::BufferTooShort {
571 offset: self.index.start + off,
572 need: 1,
573 have: 0,
574 });
575 }
576 Ok(s[off])
577 }
578
579 pub fn tid(&self, buf: &[u8]) -> Result<u16, FieldError> {
581 let mt = self.msg_type(buf)?;
582 let s = self.slice(buf);
583 let body = self.body_offset(buf)?;
584 let off = match mt {
585 REGISTER | REGACK | PUBACK => body, PUBLISH => body + 1, SUBSCRIBE | UNSUBSCRIBE => body + 1 + 2, SUBACK => body + 1, _ => {
590 return Err(FieldError::InvalidValue(format!(
591 "tid not available for type {}",
592 message_type_name(mt)
593 )));
594 },
595 };
596 if s.len() < off + 2 {
597 return Err(FieldError::BufferTooShort {
598 offset: self.index.start + off,
599 need: 2,
600 have: s.len().saturating_sub(off),
601 });
602 }
603 Ok(u16::from_be_bytes([s[off], s[off + 1]]))
604 }
605
606 pub fn mid(&self, buf: &[u8]) -> Result<u16, FieldError> {
609 let mt = self.msg_type(buf)?;
610 let s = self.slice(buf);
611 let body = self.body_offset(buf)?;
612 let off = match mt {
613 REGISTER | REGACK | PUBACK => body + 2, PUBLISH => body + 1 + 2, PUBCOMP | PUBREC | PUBREL | UNSUBACK => body, SUBSCRIBE | UNSUBSCRIBE => body + 1, SUBACK => body + 1 + 2, _ => {
619 return Err(FieldError::InvalidValue(format!(
620 "mid not available for type {}",
621 message_type_name(mt)
622 )));
623 },
624 };
625 if s.len() < off + 2 {
626 return Err(FieldError::BufferTooShort {
627 offset: self.index.start + off,
628 need: 2,
629 have: s.len().saturating_sub(off),
630 });
631 }
632 Ok(u16::from_be_bytes([s[off], s[off + 1]]))
633 }
634
635 pub fn data<'a>(&self, buf: &'a [u8]) -> Result<&'a [u8], FieldError> {
637 let mt = self.msg_type(buf)?;
638 if mt != PUBLISH {
639 return Err(FieldError::InvalidValue(format!(
640 "data not available for type {}",
641 message_type_name(mt)
642 )));
643 }
644 let s = self.slice(buf);
645 let (_, pkt_len) = self.length_info(buf)?;
646 let off = self.body_offset(buf)? + 1 + 2 + 2; let end = (pkt_len as usize).min(s.len());
648 if off > end {
649 return Ok(&[]);
650 }
651 Ok(&s[off..end])
652 }
653
654 pub fn topic_name<'a>(&self, buf: &'a [u8]) -> Result<&'a str, FieldError> {
656 let mt = self.msg_type(buf)?;
657 let s = self.slice(buf);
658 let (_, pkt_len) = self.length_info(buf)?;
659 let body = self.body_offset(buf)?;
660 let off = match mt {
661 REGISTER => body + 2 + 2, SUBSCRIBE | UNSUBSCRIBE => body + 1 + 2 + 2, _ => {
664 return Err(FieldError::InvalidValue(format!(
665 "topic_name not available for type {}",
666 message_type_name(mt)
667 )));
668 },
669 };
670 let end = (pkt_len as usize).min(s.len());
671 if off > end {
672 return Ok("");
673 }
674 std::str::from_utf8(&s[off..end]).map_err(|e| FieldError::InvalidValue(e.to_string()))
675 }
676
677 pub fn will_topic<'a>(&self, buf: &'a [u8]) -> Result<&'a str, FieldError> {
679 let mt = self.msg_type(buf)?;
680 if mt != WILLTOPIC && mt != WILLTOPICUPD {
681 return Err(FieldError::InvalidValue(format!(
682 "will_topic not available for type {}",
683 message_type_name(mt)
684 )));
685 }
686 let s = self.slice(buf);
687 let (_, pkt_len) = self.length_info(buf)?;
688 let off = self.body_offset(buf)? + 1; let end = (pkt_len as usize).min(s.len());
690 if off > end {
691 return Ok("");
692 }
693 std::str::from_utf8(&s[off..end]).map_err(|e| FieldError::InvalidValue(e.to_string()))
694 }
695
696 pub fn will_msg<'a>(&self, buf: &'a [u8]) -> Result<&'a [u8], FieldError> {
698 let mt = self.msg_type(buf)?;
699 if mt != WILLMSG && mt != WILLMSGUPD {
700 return Err(FieldError::InvalidValue(format!(
701 "will_msg not available for type {}",
702 message_type_name(mt)
703 )));
704 }
705 let s = self.slice(buf);
706 let (_, pkt_len) = self.length_info(buf)?;
707 let off = self.body_offset(buf)?;
708 let end = (pkt_len as usize).min(s.len());
709 if off > end {
710 return Ok(&[]);
711 }
712 Ok(&s[off..end])
713 }
714
715 pub fn set_msg_type(&self, buf: &mut [u8], value: u8) -> Result<(), FieldError> {
721 let off = self.index.start + self.type_offset(buf)?;
722 if buf.len() <= off {
723 return Err(FieldError::BufferTooShort {
724 offset: off,
725 need: 1,
726 have: 0,
727 });
728 }
729 buf[off] = value;
730 Ok(())
731 }
732
733 pub fn set_flags(&self, buf: &mut [u8], value: u8) -> Result<(), FieldError> {
735 if !self.has_flags(buf)? {
736 return Err(FieldError::InvalidValue(
737 "this message type has no flags byte".into(),
738 ));
739 }
740 let off = self.index.start + self.body_offset(buf)?;
741 if buf.len() <= off {
742 return Err(FieldError::BufferTooShort {
743 offset: off,
744 need: 1,
745 have: 0,
746 });
747 }
748 buf[off] = value;
749 Ok(())
750 }
751
752 fn compute_header_len(&self, buf: &[u8]) -> usize {
758 let s = self.slice(buf);
759 match decode_mqttsn_length(s) {
760 Ok((_, pkt_len)) => (pkt_len as usize).min(s.len()),
761 Err(_) => s.len(),
762 }
763 }
764
765 fn make_summary(&self, buf: &[u8]) -> String {
771 let mt = self.msg_type(buf).unwrap_or(0xFF);
772 let name = message_type_name(mt);
773 match mt {
774 PUBLISH => {
775 let tid = self.tid(buf).map_or_else(|_| "?".into(), |v| v.to_string());
776 format!("MQTT-SN {name} tid={tid}")
777 },
778 CONNECT => {
779 let cid = self.client_id(buf).unwrap_or("?");
780 format!("MQTT-SN {name} client_id={cid}")
781 },
782 CONNACK | WILLTOPICRESP | WILLMSGRESP => {
783 let rc = self.return_code(buf).unwrap_or(0xFF);
784 format!("MQTT-SN {} rc={} ({})", name, rc, return_code_name(rc))
785 },
786 _ => format!("MQTT-SN {name}"),
787 }
788 }
789
790 #[must_use]
796 pub fn field_names() -> &'static [&'static str] {
797 MQTTSN_FIELD_NAMES
798 }
799
800 pub fn get_field(&self, buf: &[u8], name: &str) -> Option<Result<FieldValue, FieldError>> {
802 match name {
803 "length" => Some(self.packet_length(buf).map(FieldValue::U16)),
804 "type" => Some(self.msg_type(buf).map(FieldValue::U8)),
805 "flags" => Some(self.flags(buf).map(FieldValue::U8)),
806 "dup" => Some(self.dup(buf).map(FieldValue::Bool)),
807 "qos" => Some(self.qos(buf).map(FieldValue::U8)),
808 "retain" => Some(self.retain(buf).map(FieldValue::Bool)),
809 "will" => Some(self.will(buf).map(FieldValue::Bool)),
810 "cleansess" => Some(self.cleansess(buf).map(FieldValue::Bool)),
811 "tid_type" => Some(self.tid_type(buf).map(FieldValue::U8)),
812 "gw_id" => Some(self.gw_id(buf).map(FieldValue::U8)),
813 "duration" => Some(self.duration(buf).map(FieldValue::U16)),
814 "radius" => Some(self.radius(buf).map(FieldValue::U8)),
815 "prot_id" => Some(self.prot_id(buf).map(FieldValue::U8)),
816 "return_code" => Some(self.return_code(buf).map(FieldValue::U8)),
817 "tid" => Some(self.tid(buf).map(FieldValue::U16)),
818 "mid" => Some(self.mid(buf).map(FieldValue::U16)),
819 "client_id" => Some(self.client_id(buf).map(|s| FieldValue::Str(s.to_string()))),
820 "topic_name" => Some(self.topic_name(buf).map(|s| FieldValue::Str(s.to_string()))),
821 "will_topic" => Some(self.will_topic(buf).map(|s| FieldValue::Str(s.to_string()))),
822 "data" => Some(self.data(buf).map(|d| FieldValue::Bytes(d.to_vec()))),
823 "will_msg" => Some(self.will_msg(buf).map(|d| FieldValue::Bytes(d.to_vec()))),
824 "gw_addr" => Some(self.gw_addr(buf).map(|d| FieldValue::Bytes(d.to_vec()))),
825 _ => None,
826 }
827 }
828
829 pub fn set_field(
831 &self,
832 buf: &mut [u8],
833 name: &str,
834 value: FieldValue,
835 ) -> Option<Result<(), FieldError>> {
836 match name {
837 "type" => {
838 if let FieldValue::U8(v) = value {
839 Some(self.set_msg_type(buf, v))
840 } else {
841 Some(Err(FieldError::InvalidValue(format!(
842 "type: expected U8, got {value:?}"
843 ))))
844 }
845 },
846 "flags" => {
847 if let FieldValue::U8(v) = value {
848 Some(self.set_flags(buf, v))
849 } else {
850 Some(Err(FieldError::InvalidValue(format!(
851 "flags: expected U8, got {value:?}"
852 ))))
853 }
854 },
855 _ => None,
856 }
857 }
858}
859
860impl Layer for MqttSnLayer {
861 fn kind(&self) -> LayerKind {
862 LayerKind::MqttSn
863 }
864
865 fn summary(&self, data: &[u8]) -> String {
866 self.make_summary(data)
867 }
868
869 fn header_len(&self, data: &[u8]) -> usize {
870 self.compute_header_len(data)
871 }
872
873 fn field_names(&self) -> &'static [&'static str] {
874 MQTTSN_FIELD_NAMES
875 }
876}
877
878#[cfg(test)]
883mod tests {
884 use super::*;
885
886 fn make_packet(msg_type: u8, body: &[u8]) -> Vec<u8> {
888 let total = 2 + body.len(); assert!(total <= 255, "use make_extended_packet for >255 bytes");
890 let mut buf = Vec::with_capacity(total);
891 buf.push(total as u8);
892 buf.push(msg_type);
893 buf.extend_from_slice(body);
894 buf
895 }
896
897 fn layer_from(buf: &[u8]) -> MqttSnLayer {
899 MqttSnLayer::new(LayerIndex::new(LayerKind::MqttSn, 0, buf.len()))
900 }
901
902 #[test]
903 fn test_decode_length_short() {
904 let buf = [0x05, 0x04]; let (hdr_size, len) = decode_mqttsn_length(&buf).unwrap();
906 assert_eq!(hdr_size, 1);
907 assert_eq!(len, 5);
908 }
909
910 #[test]
911 fn test_decode_length_extended() {
912 let buf = [0x01, 0x01, 0x00]; let (hdr_size, len) = decode_mqttsn_length(&buf).unwrap();
914 assert_eq!(hdr_size, 3);
915 assert_eq!(len, 256);
916 }
917
918 #[test]
919 fn test_decode_length_empty() {
920 let buf: [u8; 0] = [];
921 assert!(decode_mqttsn_length(&buf).is_err());
922 }
923
924 #[test]
925 fn test_decode_length_extended_too_short() {
926 let buf = [0x01, 0x01]; assert!(decode_mqttsn_length(&buf).is_err());
928 }
929
930 #[test]
931 fn test_is_mqttsn_payload_valid_searchgw() {
932 let pkt = make_packet(SEARCHGW, &[0x00]);
933 assert!(is_mqttsn_payload(&pkt));
934 }
935
936 #[test]
937 fn test_is_mqttsn_payload_too_short() {
938 let buf = [0x01];
939 assert!(!is_mqttsn_payload(&buf));
940 }
941
942 #[test]
943 fn test_is_mqttsn_payload_unknown_type() {
944 let buf = [0x02, 0xFF]; assert!(!is_mqttsn_payload(&buf));
946 }
947
948 #[test]
949 fn test_message_type_name_values() {
950 assert_eq!(message_type_name(CONNECT), "CONNECT");
951 assert_eq!(message_type_name(PUBLISH), "PUBLISH");
952 assert_eq!(message_type_name(PINGREQ), "PINGREQ");
953 assert_eq!(message_type_name(0xFF), "UNKNOWN");
954 }
955
956 #[test]
957 fn test_return_code_name_values() {
958 assert_eq!(return_code_name(RC_ACCEPTED), "Accepted");
959 assert_eq!(return_code_name(RC_REJ_CONGESTION), "Rejected: congestion");
960 assert_eq!(return_code_name(0xFF), "Unknown");
961 }
962
963 #[test]
964 fn test_advertise() {
965 let pkt = make_packet(ADVERTISE, &[0x01, 0x00, 0x3C]);
966 let l = layer_from(&pkt);
967 assert_eq!(l.msg_type(&pkt).unwrap(), ADVERTISE);
968 assert_eq!(l.gw_id(&pkt).unwrap(), 1);
969 assert_eq!(l.duration(&pkt).unwrap(), 60);
970 assert_eq!(l.packet_length(&pkt).unwrap(), 5);
971 }
972
973 #[test]
974 fn test_searchgw() {
975 let pkt = make_packet(SEARCHGW, &[0x03]);
976 let l = layer_from(&pkt);
977 assert_eq!(l.msg_type(&pkt).unwrap(), SEARCHGW);
978 assert_eq!(l.radius(&pkt).unwrap(), 3);
979 }
980
981 #[test]
982 fn test_gwinfo() {
983 let pkt = make_packet(GWINFO, &[0x01, 0xC0, 0xA8, 0x01, 0x01]);
984 let l = layer_from(&pkt);
985 assert_eq!(l.msg_type(&pkt).unwrap(), GWINFO);
986 assert_eq!(l.gw_id(&pkt).unwrap(), 1);
987 assert_eq!(l.gw_addr(&pkt).unwrap(), &[0xC0, 0xA8, 0x01, 0x01]);
988 }
989
990 #[test]
991 fn test_connect() {
992 let pkt = make_packet(CONNECT, &[0x0C, 0x01, 0x00, 0x3C, b't', b'e', b's', b't']);
993 let l = layer_from(&pkt);
994 assert_eq!(l.msg_type(&pkt).unwrap(), CONNECT);
995 assert_eq!(l.flags(&pkt).unwrap(), 0x0C);
996 assert!(l.will(&pkt).unwrap());
997 assert!(l.cleansess(&pkt).unwrap());
998 assert!(!l.dup(&pkt).unwrap());
999 assert_eq!(l.qos(&pkt).unwrap(), 0);
1000 assert!(!l.retain(&pkt).unwrap());
1001 assert_eq!(l.tid_type(&pkt).unwrap(), 0);
1002 assert_eq!(l.prot_id(&pkt).unwrap(), 1);
1003 assert_eq!(l.duration(&pkt).unwrap(), 60);
1004 assert_eq!(l.client_id(&pkt).unwrap(), "test");
1005 }
1006
1007 #[test]
1008 fn test_connack() {
1009 let pkt = make_packet(CONNACK, &[RC_ACCEPTED]);
1010 let l = layer_from(&pkt);
1011 assert_eq!(l.msg_type(&pkt).unwrap(), CONNACK);
1012 assert_eq!(l.return_code(&pkt).unwrap(), RC_ACCEPTED);
1013 }
1014
1015 #[test]
1016 fn test_register() {
1017 let pkt = make_packet(REGISTER, &[0x00, 0x01, 0x00, 0x02, b'a', b'/', b'b']);
1018 let l = layer_from(&pkt);
1019 assert_eq!(l.msg_type(&pkt).unwrap(), REGISTER);
1020 assert_eq!(l.tid(&pkt).unwrap(), 1);
1021 assert_eq!(l.mid(&pkt).unwrap(), 2);
1022 assert_eq!(l.topic_name(&pkt).unwrap(), "a/b");
1023 }
1024
1025 #[test]
1026 fn test_regack() {
1027 let pkt = make_packet(REGACK, &[0x00, 0x05, 0x00, 0x03, RC_ACCEPTED]);
1028 let l = layer_from(&pkt);
1029 assert_eq!(l.msg_type(&pkt).unwrap(), REGACK);
1030 assert_eq!(l.tid(&pkt).unwrap(), 5);
1031 assert_eq!(l.mid(&pkt).unwrap(), 3);
1032 assert_eq!(l.return_code(&pkt).unwrap(), RC_ACCEPTED);
1033 }
1034
1035 #[test]
1036 fn test_publish() {
1037 let pkt = make_packet(PUBLISH, &[0x20, 0x00, 0x01, 0x00, 0x02, b'h', b'i']);
1038 let l = layer_from(&pkt);
1039 assert_eq!(l.msg_type(&pkt).unwrap(), PUBLISH);
1040 assert_eq!(l.qos(&pkt).unwrap(), 1);
1041 assert_eq!(l.tid(&pkt).unwrap(), 1);
1042 assert_eq!(l.mid(&pkt).unwrap(), 2);
1043 assert_eq!(l.data(&pkt).unwrap(), b"hi");
1044 }
1045
1046 #[test]
1047 fn test_puback() {
1048 let pkt = make_packet(PUBACK, &[0x00, 0x01, 0x00, 0x02, RC_ACCEPTED]);
1049 let l = layer_from(&pkt);
1050 assert_eq!(l.msg_type(&pkt).unwrap(), PUBACK);
1051 assert_eq!(l.tid(&pkt).unwrap(), 1);
1052 assert_eq!(l.mid(&pkt).unwrap(), 2);
1053 assert_eq!(l.return_code(&pkt).unwrap(), RC_ACCEPTED);
1054 }
1055
1056 #[test]
1057 fn test_pubcomp() {
1058 let pkt = make_packet(PUBCOMP, &[0x00, 0x07]);
1059 let l = layer_from(&pkt);
1060 assert_eq!(l.msg_type(&pkt).unwrap(), PUBCOMP);
1061 assert_eq!(l.mid(&pkt).unwrap(), 7);
1062 }
1063
1064 #[test]
1065 fn test_suback() {
1066 let pkt = make_packet(SUBACK, &[0x00, 0x00, 0x01, 0x00, 0x02, RC_ACCEPTED]);
1067 let l = layer_from(&pkt);
1068 assert_eq!(l.msg_type(&pkt).unwrap(), SUBACK);
1069 assert_eq!(l.tid(&pkt).unwrap(), 1);
1070 assert_eq!(l.mid(&pkt).unwrap(), 2);
1071 assert_eq!(l.return_code(&pkt).unwrap(), RC_ACCEPTED);
1072 }
1073
1074 #[test]
1075 fn test_unsuback() {
1076 let pkt = make_packet(UNSUBACK, &[0x00, 0x05]);
1077 let l = layer_from(&pkt);
1078 assert_eq!(l.msg_type(&pkt).unwrap(), UNSUBACK);
1079 assert_eq!(l.mid(&pkt).unwrap(), 5);
1080 }
1081
1082 #[test]
1083 fn test_pingreq_empty() {
1084 let pkt = make_packet(PINGREQ, &[]);
1085 let l = layer_from(&pkt);
1086 assert_eq!(l.msg_type(&pkt).unwrap(), PINGREQ);
1087 assert_eq!(l.client_id(&pkt).unwrap(), "");
1088 }
1089
1090 #[test]
1091 fn test_pingreq_with_client_id() {
1092 let pkt = make_packet(PINGREQ, b"sensor1");
1093 let l = layer_from(&pkt);
1094 assert_eq!(l.msg_type(&pkt).unwrap(), PINGREQ);
1095 assert_eq!(l.client_id(&pkt).unwrap(), "sensor1");
1096 }
1097
1098 #[test]
1099 fn test_pingresp() {
1100 let pkt = make_packet(PINGRESP, &[]);
1101 let l = layer_from(&pkt);
1102 assert_eq!(l.msg_type(&pkt).unwrap(), PINGRESP);
1103 }
1104
1105 #[test]
1106 fn test_disconnect_empty() {
1107 let pkt = make_packet(DISCONNECT, &[]);
1108 let l = layer_from(&pkt);
1109 assert_eq!(l.msg_type(&pkt).unwrap(), DISCONNECT);
1110 }
1111
1112 #[test]
1113 fn test_disconnect_with_duration() {
1114 let pkt = make_packet(DISCONNECT, &[0x00, 0x3C]);
1115 let l = layer_from(&pkt);
1116 assert_eq!(l.msg_type(&pkt).unwrap(), DISCONNECT);
1117 assert_eq!(l.duration(&pkt).unwrap(), 60);
1118 }
1119
1120 #[test]
1121 fn test_willtopic() {
1122 let pkt = make_packet(WILLTOPIC, &[0x00, b'w', b'/', b't']);
1123 let l = layer_from(&pkt);
1124 assert_eq!(l.msg_type(&pkt).unwrap(), WILLTOPIC);
1125 assert_eq!(l.will_topic(&pkt).unwrap(), "w/t");
1126 }
1127
1128 #[test]
1129 fn test_willmsg() {
1130 let pkt = make_packet(WILLMSG, &[b'b', b'y', b'e']);
1131 let l = layer_from(&pkt);
1132 assert_eq!(l.msg_type(&pkt).unwrap(), WILLMSG);
1133 assert_eq!(l.will_msg(&pkt).unwrap(), b"bye");
1134 }
1135
1136 #[test]
1137 fn test_willtopicresp() {
1138 let pkt = make_packet(WILLTOPICRESP, &[RC_ACCEPTED]);
1139 let l = layer_from(&pkt);
1140 assert_eq!(l.msg_type(&pkt).unwrap(), WILLTOPICRESP);
1141 assert_eq!(l.return_code(&pkt).unwrap(), RC_ACCEPTED);
1142 }
1143
1144 #[test]
1145 fn test_willmsgresp() {
1146 let pkt = make_packet(WILLMSGRESP, &[RC_REJ_NOT_SUPPORTED]);
1147 let l = layer_from(&pkt);
1148 assert_eq!(l.msg_type(&pkt).unwrap(), WILLMSGRESP);
1149 assert_eq!(l.return_code(&pkt).unwrap(), RC_REJ_NOT_SUPPORTED);
1150 }
1151
1152 #[test]
1153 fn test_flags_all_bits() {
1154 let pkt = make_packet(CONNECT, &[0xFF, 0x01, 0x00, 0x3C]);
1155 let l = layer_from(&pkt);
1156 assert!(l.dup(&pkt).unwrap());
1157 assert_eq!(l.qos(&pkt).unwrap(), 3);
1158 assert!(l.retain(&pkt).unwrap());
1159 assert!(l.will(&pkt).unwrap());
1160 assert!(l.cleansess(&pkt).unwrap());
1161 assert_eq!(l.tid_type(&pkt).unwrap(), 0x03);
1162 }
1163
1164 #[test]
1165 fn test_get_field_type() {
1166 let pkt = make_packet(CONNECT, &[0x04, 0x01, 0x00, 0x3C, b'x']);
1167 let l = layer_from(&pkt);
1168 match l.get_field(&pkt, "type") {
1169 Some(Ok(FieldValue::U8(v))) => assert_eq!(v, CONNECT),
1170 other => panic!("expected Some(Ok(U8(CONNECT))), got {:?}", other),
1171 }
1172 }
1173
1174 #[test]
1175 fn test_get_field_unknown() {
1176 let pkt = make_packet(PINGRESP, &[]);
1177 let l = layer_from(&pkt);
1178 assert!(l.get_field(&pkt, "nonexistent").is_none());
1179 }
1180
1181 #[test]
1182 fn test_set_msg_type() {
1183 let mut pkt = make_packet(PINGREQ, &[]);
1184 let l = layer_from(&pkt);
1185 l.set_msg_type(&mut pkt, PINGRESP).unwrap();
1186 assert_eq!(l.msg_type(&pkt).unwrap(), PINGRESP);
1187 }
1188
1189 #[test]
1190 fn test_set_flags() {
1191 let mut pkt = make_packet(CONNECT, &[0x00, 0x01, 0x00, 0x3C]);
1192 let l = layer_from(&pkt);
1193 l.set_flags(&mut pkt, 0x0C).unwrap();
1194 assert!(l.will(&pkt).unwrap());
1195 assert!(l.cleansess(&pkt).unwrap());
1196 }
1197
1198 #[test]
1199 fn test_set_flags_no_flags_msg() {
1200 let mut pkt = make_packet(PINGRESP, &[]);
1201 let l = layer_from(&pkt);
1202 assert!(l.set_flags(&mut pkt, 0x00).is_err());
1203 }
1204
1205 #[test]
1206 fn test_summary_publish() {
1207 let pkt = make_packet(PUBLISH, &[0x00, 0x00, 0x05, 0x00, 0x01, b'x']);
1208 let l = layer_from(&pkt);
1209 let s = l.make_summary(&pkt);
1210 assert!(s.contains("PUBLISH"));
1211 assert!(s.contains("tid=5"));
1212 }
1213
1214 #[test]
1215 fn test_summary_connect() {
1216 let pkt = make_packet(CONNECT, &[0x04, 0x01, 0x00, 0x3C, b's', b'1']);
1217 let l = layer_from(&pkt);
1218 let s = l.make_summary(&pkt);
1219 assert!(s.contains("CONNECT"));
1220 assert!(s.contains("client_id=s1"));
1221 }
1222
1223 #[test]
1224 fn test_summary_connack() {
1225 let pkt = make_packet(CONNACK, &[RC_ACCEPTED]);
1226 let l = layer_from(&pkt);
1227 let s = l.make_summary(&pkt);
1228 assert!(s.contains("CONNACK"));
1229 assert!(s.contains("Accepted"));
1230 }
1231
1232 #[test]
1233 fn test_layer_trait_kind() {
1234 let pkt = make_packet(PINGRESP, &[]);
1235 let l = layer_from(&pkt);
1236 assert_eq!(l.kind(), LayerKind::MqttSn);
1237 }
1238
1239 #[test]
1240 fn test_layer_trait_header_len() {
1241 let pkt = make_packet(SEARCHGW, &[0x05]);
1242 let l = layer_from(&pkt);
1243 assert_eq!(Layer::header_len(&l, &pkt), 3);
1244 }
1245
1246 #[test]
1247 fn test_layer_trait_field_names() {
1248 let pkt = make_packet(PINGRESP, &[]);
1249 let l = layer_from(&pkt);
1250 let names = Layer::field_names(&l);
1251 assert!(names.contains(&"type"));
1252 assert!(names.contains(&"length"));
1253 assert!(names.contains(&"flags"));
1254 }
1255
1256 #[test]
1257 fn test_extended_length_packet() {
1258 let mut buf = vec![0u8; 260];
1259 buf[0] = 0x01;
1260 buf[1] = 0x01;
1261 buf[2] = 0x04; buf[3] = PUBLISH;
1263 buf[4] = 0x00; let l = layer_from(&buf);
1265 assert_eq!(l.packet_length(&buf).unwrap(), 260);
1266 assert_eq!(l.msg_type(&buf).unwrap(), PUBLISH);
1267 }
1268
1269 #[test]
1270 fn test_roundtrip_builder_parse() {
1271 let built = MqttSnBuilder::connect()
1272 .cleansess(true)
1273 .prot_id(1)
1274 .duration(60)
1275 .client_id(b"test")
1276 .build();
1277 let l = layer_from(&built);
1278 assert_eq!(l.msg_type(&built).unwrap(), CONNECT);
1279 assert!(l.cleansess(&built).unwrap());
1280 assert_eq!(l.prot_id(&built).unwrap(), 1);
1281 assert_eq!(l.duration(&built).unwrap(), 60);
1282 assert_eq!(l.client_id(&built).unwrap(), "test");
1283 }
1284}