mod disconnect;
mod ping;
mod pubcomp;
mod pubrec;
mod pubrel;
mod unsuback;
mod unsubscribe;
pub(crate) mod subscribe;
pub mod connack;
pub mod connect;
pub mod puback;
pub mod publish;
pub mod suback;
use core::convert::TryFrom;
use num_enum::TryFromPrimitive;
use crate::{MqttError, Pid, QoS};
pub use self::{
connack::ConnAck,
connect::Connect,
disconnect::Disconnect,
ping::{PingReq, PingResp},
puback::PubAck,
pubcomp::PubComp,
publish::Publish,
pubrec::PubRec,
pubrel::PubRel,
suback::SubAck,
subscribe::Subscribe,
unsuback::UnsubAck,
unsubscribe::Unsubscribe,
};
#[repr(u8)]
#[derive(TryFromPrimitive, Debug, Clone, Copy, PartialEq, Eq)]
pub enum PropertyType {
PayloadFormatIndicator = 0x01,
MessageExpiryInterval = 0x02,
ContentType = 0x03,
ResponseTopic = 0x08,
CorrelationData = 0x09,
SubscriptionIdentifier = 0x0B,
SessionExpiryInterval = 0x11,
AssignedClientIdentifier = 0x12,
ServerKeepAlive = 0x13,
AuthenticationMethod = 0x15,
AuthenticationData = 0x16,
RequestProblemInformation = 0x17,
WillDelayInterval = 0x18,
RequestResponseInformation = 0x19,
ResponseInformation = 0x1A,
ServerReference = 0x1C,
ReasonString = 0x1F,
ReceiveMaximum = 0x21,
TopicAliasMaximum = 0x22,
TopicAlias = 0x23,
MaximumQos = 0x24,
RetainAvailable = 0x25,
UserProperty = 0x26,
MaximumPacketSize = 0x27,
WildcardSubscriptionAvailable = 0x28,
SubscriptionIdentifierAvailable = 0x29,
SharedSubscriptionAvailable = 0x2A,
}
#[repr(u8)]
#[derive(TryFromPrimitive, Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum PacketType {
Connect = 0x01,
ConnAck = 0x02,
Publish = 0x03,
PubAck = 0x04,
PubRec = 0x05,
PubRel = 0x06,
PubComp = 0x07,
Subscribe = 0x08,
SubAck = 0x09,
Unsubscribe = 0x0A,
UnsubAck = 0x0B,
PingReq = 0x0C,
PingResp = 0x0D,
Disconnect = 0x0E,
}
fn property(code: u8) -> Result<PropertyType, MqttError> {
PropertyType::try_from(code).map_err(|_| MqttError::InvalidPropertyByte(code))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Header {
pub typ: PacketType,
pub dup: bool,
pub qos: QoS,
pub retain: bool,
}
impl Header {
pub fn new(hd: u8) -> Result<Header, MqttError> {
let typ: PacketType =
PacketType::try_from(hd >> 4).map_err(|_| MqttError::InvalidHeader)?;
let flags_ok = match typ {
PacketType::Connect => hd & 0b1111 == 0,
PacketType::ConnAck => hd & 0b1111 == 0,
PacketType::Publish => true,
PacketType::PubAck => hd & 0b1111 == 0,
PacketType::PubRec => hd & 0b1111 == 0,
PacketType::PubRel => hd & 0b1111 == 0b0010,
PacketType::PubComp => hd & 0b1111 == 0,
PacketType::Subscribe => hd & 0b1111 == 0b0010,
PacketType::SubAck => hd & 0b1111 == 0,
PacketType::Unsubscribe => hd & 0b1111 == 0b0010,
PacketType::UnsubAck => hd & 0b1111 == 0,
PacketType::PingReq => hd & 0b1111 == 0,
PacketType::PingResp => hd & 0b1111 == 0,
PacketType::Disconnect => hd & 0b1111 == 0,
};
if !flags_ok {
return Err(MqttError::InvalidHeader);
}
Ok(Header {
typ,
dup: hd & 0b1000 != 0,
qos: QoS::try_from((hd & 0b110) >> 1)?,
retain: hd & 1 == 1,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Packet<'a> {
Connect(Connect<'a>),
ConnAck(ConnAck<'a>),
Publish(Publish<'a>),
PubAck(PubAck<'a>),
PubRec(PubRec<'a>),
PubRel(PubRel<'a>),
PubComp(PubComp<'a>),
Subscribe(Subscribe<'a>),
SubAck(SubAck<'a>),
Unsubscribe(Unsubscribe<'a>),
UnsubAck(UnsubAck<'a>),
PingReq,
PingResp,
Disconnect(Disconnect<'a>),
}
impl<'a> Packet<'a> {
pub fn type_name(&self) -> &'static str {
match *self {
Packet::Connect(_) => "Connect",
Packet::ConnAck(_) => "ConnAck",
Packet::Publish(_) => "Publish",
Packet::PubAck(_) => "PubAck",
Packet::PubRec(_) => "PubRec",
Packet::PubRel(_) => "PubRel",
Packet::PubComp(_) => "PubComp",
Packet::Subscribe(_) => "Subscribe",
Packet::SubAck(_) => "SubAck",
Packet::Unsubscribe(_) => "Unsubscribe",
Packet::UnsubAck(_) => "UnsubAck",
Packet::PingReq => "PingReq",
Packet::PingResp => "PingResp",
Packet::Disconnect(_) => "Disconnect",
}
}
}
pub fn clone_packet(input: &[u8], output: &mut [u8]) -> Result<usize, MqttError> {
if input.is_empty() {
return Ok(0);
}
let mut offset = 0;
while Header::new(input[offset]).is_err() {
offset += 1;
if input[offset..].is_empty() {
return Ok(0);
}
}
let start = offset;
if let Some((_, remaining_len)) = read_header(input, &mut offset)? {
let end = offset + remaining_len;
let len = end - start;
output[..len].copy_from_slice(&input[start..end]);
return Ok(end);
}
Ok(0)
}
pub fn read_packet(buf: &[u8]) -> Result<Packet, MqttError> {
let mut offset = 0;
let packet_header = read_header(buf, &mut offset)?;
match packet_header {
Some(packet_header) => {
let (header, length) = packet_header;
if length == 0 {
return match header.typ {
PacketType::PingReq => Ok(Packet::PingReq),
PacketType::PingResp => Ok(Packet::PingResp),
PacketType::Disconnect => Ok(Packet::Disconnect(Disconnect::default())),
_ => Err(MqttError::PayloadRequired),
};
}
let packet = match header.typ {
PacketType::Connect => Packet::Connect(Connect::read(buf, &mut offset)?),
PacketType::ConnAck => Packet::ConnAck(ConnAck::read(buf, &mut offset)?),
PacketType::Publish => Packet::Publish(Publish::read(header, buf, &mut offset)?),
PacketType::PubAck => {
let inner = if length == 2 {
let pid = Pid::read(buf, &mut offset)?;
PubAck::new(pid)
} else {
PubAck::read(buf, &mut offset)?
};
Packet::PubAck(inner)
}
PacketType::PubRec => Packet::PubRec(PubRec::read(buf, &mut offset)?),
PacketType::PubRel => Packet::PubRel(PubRel::read(buf, &mut offset)?),
PacketType::PubComp => Packet::PubComp(PubComp::read(buf, &mut offset)?),
PacketType::Subscribe => Packet::Subscribe(Subscribe::read(buf, &mut offset)?),
PacketType::SubAck => Packet::SubAck(SubAck::read(buf, &mut offset)?),
PacketType::Unsubscribe => {
Packet::Unsubscribe(Unsubscribe::read(buf, &mut offset)?)
}
PacketType::UnsubAck => Packet::UnsubAck(UnsubAck::read(buf, &mut offset)?),
PacketType::PingReq => Packet::PingReq,
PacketType::PingResp => Packet::PingResp,
PacketType::Disconnect => Packet::Disconnect(Disconnect::read(buf, &mut offset)?),
};
Ok(packet)
}
None => Err(MqttError::InvalidHeader),
}
}
pub fn write_packet(packet: &Packet, buf: &mut [u8]) -> Result<usize, MqttError> {
let mut offset = 0;
let write_len = match packet {
Packet::Connect(connect) => connect.write(buf, &mut offset)?,
Packet::ConnAck(connack) => connack.write(buf, &mut offset)?,
Packet::Publish(publish) => publish.write(buf, &mut offset)?,
Packet::PubAck(puback) => puback.write(buf, &mut offset)?,
Packet::PubRec(pubrec) => pubrec.write(buf, &mut offset)?,
Packet::PubRel(pubrel) => pubrel.write(buf, &mut offset)?,
Packet::PubComp(pubcomp) => pubcomp.write(buf, &mut offset)?,
Packet::Subscribe(subscribe) => subscribe.write(buf, &mut offset)?,
Packet::SubAck(suback) => suback.write(buf, &mut offset)?,
Packet::Unsubscribe(unsubscribe) => unsubscribe.write(buf, &mut offset)?,
Packet::UnsubAck(unsuback) => unsuback.write(buf, &mut offset)?,
Packet::PingReq => PingReq::write(buf, &mut offset)?,
Packet::PingResp => PingResp::write(buf, &mut offset)?,
Packet::Disconnect(disconnect) => disconnect.write(buf, &mut offset)?,
};
Ok(write_len)
}
pub fn read_header(buf: &[u8], offset: &mut usize) -> Result<Option<(Header, usize)>, MqttError> {
let mut len: usize = 0;
for pos in 0..=3 {
if buf.len() > *offset + pos + 1 {
let byte = buf[*offset + pos + 1];
len += (byte as usize & 0x7F) << (pos * 7);
if (byte & 0x80) == 0 {
if buf.len() < *offset + 2 + pos + len {
return Ok(None);
}
let header = Header::new(buf[*offset])?;
*offset += pos + 2;
return Ok(Some((header, len)));
}
} else {
return Ok(None);
}
}
Err(MqttError::InvalidHeader)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn read_connect_packet() {
let packet = vec![
0x10, 0x9d, 0x01, 0x00, 0x04, 0x4d, 0x51, 0x54, 0x54, 0x05, 0xc6, 0x00, 0x00, 0x2f, 0x11, 0x00, 0x00, 0x04, 0xd2, 0x21, 0x01, 0xb0, 0x27, 0x00, 0x00, 0x00, 0x64, 0x22, 0x01, 0xc8, 0x19, 0x01, 0x17, 0x01, 0x15, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x16, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04, 0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
0x74, 0x00, 0x09, 0x6d, 0x79, 0x2d, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x01, 0x00, 0x02, 0x00, 0x00, 0x10, 0xe1, 0x03, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x00, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x09, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04, 0x18, 0x00, 0x00, 0x04, 0xd2, 0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
0x74, 0x00, 0x0f, 0x6d, 0x79, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x73, 0x74, 0x61,
0x74, 0x75, 0x73, 0x00, 0x04, 0x64, 0x65, 0x61, 0x64, 0x00, 0x06, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x6f, 0x00, 0x07, 0x63, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x61, ];
let mut offset = 0;
let header = read_header(&packet, &mut offset).unwrap().unwrap();
assert_eq!(PacketType::Connect, header.0.typ);
assert_eq!(offset, 3);
}
#[test]
fn read_connack_packet() {
let packet = vec![
0x20, 0x57, 0x00, 0x00, 0x54, 0x12, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x16, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04, 0x15, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x27, 0x00, 0x00, 0x00, 0x64, 0x24, 0x02, 0x1f, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x21, 0x01, 0xb0, 0x1a, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x25, 0x01, 0x13, 0x04, 0xd2, 0x1c, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x11, 0x00, 0x00, 0x04, 0xd2, 0x2a, 0x00, 0x22, 0x01, 0xc8, 0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
0x74, 0x28, 0x01, 0x29, 0x01, ];
let mut offset = 0;
let header = read_header(&packet, &mut offset).unwrap().unwrap();
assert_eq!(PacketType::ConnAck, header.0.typ);
assert_eq!(offset, 2);
}
#[test]
fn read_disconnect_packet() {
let packet = vec![
0xE0, 0x00, ];
let mut offset = 0;
let header = read_header(&packet, &mut offset).unwrap().unwrap();
assert_eq!(PacketType::Disconnect, header.0.typ);
assert_eq!(offset, 2);
}
#[test]
fn read_pingreq_packet() {
let packet = vec![
0xC0, 0x00, ];
let mut offset = 0;
let header = read_header(&packet, &mut offset).unwrap().unwrap();
assert_eq!(PacketType::PingReq, header.0.typ);
assert_eq!(offset, 2);
}
#[test]
fn read_pingresp_packet() {
let packet = vec![
0xD0, 0x00, ];
let mut offset = 0;
let header = read_header(&packet, &mut offset).unwrap().unwrap();
assert_eq!(PacketType::PingResp, header.0.typ);
assert_eq!(offset, 2);
}
#[test]
fn read_puback_packet() {
let packet = vec![
0x40, 0x18, 0x00, 0x2a, 0x10, 0x14, 0x1f, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
0x74, ];
let mut offset = 0;
let header = read_header(&packet, &mut offset).unwrap().unwrap();
assert_eq!(PacketType::PubAck, header.0.typ);
assert_eq!(offset, 2);
}
#[test]
fn read_pubcomp_packet() {
let packet = vec![
0x70, 0x18, 0x00, 0x2a, 0x92, 0x14, 0x1f, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
0x74, ];
let mut offset = 0;
let header = read_header(&packet, &mut offset).unwrap().unwrap();
assert_eq!(PacketType::PubComp, header.0.typ);
assert_eq!(offset, 2);
}
#[test]
fn read_publish_packet() {
let packet = vec![
0x34, 0x40, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x2a, 0x33, 0x01, 0x01, 0x02, 0x00, 0x00, 0x10, 0xe1, 0x23, 0x00, 0x64, 0x08, 0x00, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x09, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04, 0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
0x74, 0x0b, 0x78, 0x0b, 0x80, 0x80, 0x01, 0x03, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x74, 0x65, 0x73, 0x74, ];
let mut offset = 0;
let header = read_header(&packet, &mut offset).unwrap().unwrap();
assert_eq!(PacketType::Publish, header.0.typ);
assert_eq!(offset, 2);
}
#[test]
fn read_pubrec_packet() {
let packet = vec![
0x50, 0x18, 0x00, 0x2a, 0x10, 0x14, 0x1f, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
0x74, ];
let mut offset = 0;
let header = read_header(&packet, &mut offset).unwrap().unwrap();
assert_eq!(PacketType::PubRec, header.0.typ);
assert_eq!(offset, 2);
}
#[test]
fn read_pubrel_packet() {
let packet = vec![
0x62, 0x18, 0x00, 0x2a, 0x92, 0x14, 0x1f, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
0x74, ];
let mut offset = 0;
let header = read_header(&packet, &mut offset).unwrap().unwrap();
assert_eq!(PacketType::PubRel, header.0.typ);
assert_eq!(offset, 2);
}
#[test]
fn read_suback_packet() {
let packet = vec![
0x90, 0x1b, 0x00, 0x2a, 0x14, 0x1f, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74,
0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x01, 0x02, 0x80, ];
let mut offset = 0;
let header = read_header(&packet, &mut offset).unwrap().unwrap();
assert_eq!(PacketType::SubAck, header.0.typ);
assert_eq!(offset, 2);
}
#[test]
fn read_subscribe_packet() {
let packet = vec![
0x82, 0x1a, 0x00, 0x2a, 0x0f, 0x0b, 0x64, 0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
0x74, 0x00, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2d, ];
let mut offset = 0;
let header = read_header(&packet, &mut offset).unwrap().unwrap();
assert_eq!(PacketType::Subscribe, header.0.typ);
assert_eq!(offset, 2);
}
#[test]
fn read_unubscribe_packet() {
let packet = vec![
0xa2, 0x1e, 0x00, 0x0a, 0x0d, 0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
0x74, 0x00, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x05, 0x77, 0x6f, 0x72, 0x6c, 0x64, ];
let mut offset = 0;
let header = read_header(&packet, &mut offset).unwrap().unwrap();
assert_eq!(PacketType::Unsubscribe, header.0.typ);
assert_eq!(offset, 2);
}
#[test]
fn read_unsuback_packet() {
let packet = vec![
0xb0, 0x19, 0x00, 0x0a, 0x14, 0x1f, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
0x74, 0x87, 0x8f, ];
let mut offset = 0;
let header = read_header(&packet, &mut offset).unwrap().unwrap();
assert_eq!(PacketType::UnsubAck, header.0.typ);
assert_eq!(offset, 2);
}
#[test]
fn clone_packet_from_bytes_exact() {
let bytes = vec![
0x77, 0x11, 0x22, 0xbe, 0xef, 0x90, 0x1b, 0x00, 0x2a, 0x14, 0x1f, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74,
0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x01, 0x02, 0x80, 0x77, 0x1f, 0xde, 0xad, ];
let mut packet_bytes = [0x00u8; 64];
let len = clone_packet(&bytes, &mut packet_bytes).unwrap();
let packet = read_packet(&packet_bytes[..len]).unwrap();
assert!(matches!(packet, Packet::SubAck(_)));
}
#[ignore = "read_packet should decode MQTT packet from slice which length is bigger than exact packet length"]
#[test]
fn clone_packet_from_bytes() {
let bytes = vec![
0x77, 0x11, 0x22, 0xbe, 0xef, 0x90, 0x1b, 0x00, 0x2a, 0x14, 0x1f, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74,
0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x01, 0x02, 0x80, 0x77, 0x1f, 0xde, 0xad, ];
let mut packet_bytes = [0x00u8; 64];
let _len = clone_packet(&bytes, &mut packet_bytes).unwrap();
let packet = read_packet(&packet_bytes).unwrap();
assert!(matches!(packet, Packet::SubAck(_)));
}
#[test]
fn write_suback_packet() {
let expected = vec![
0x90, 0x07, 0x00, 0x2a, 0x00, 0x00, 0x01, 0x02, 0x80, ];
use suback::SubscribeReasonCode;
let mut reason_codes: heapless::Vec<_, 32> = heapless::Vec::new();
reason_codes.push(SubscribeReasonCode::QoS0).unwrap();
reason_codes.push(SubscribeReasonCode::QoS1).unwrap();
reason_codes.push(SubscribeReasonCode::QoS2).unwrap();
reason_codes.push(SubscribeReasonCode::Unspecified).unwrap();
let packet = Packet::SubAck(SubAck {
pid: crate::Pid::new(42),
reason_codes,
properties: None,
});
let mut packet_bytes = [0x00u8; 64];
let len = write_packet(&packet, &mut packet_bytes).unwrap();
assert_eq!(expected, packet_bytes[..len]);
}
}