You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

576 lines
21 KiB

  1. using MQTTnet.Exceptions;
  2. using MQTTnet.Packets;
  3. using MQTTnet.Protocol;
  4. using System;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Text;
  8. namespace MQTTnet.Serializer
  9. {
  10. public sealed class MqttPacketSerializer : IMqttPacketSerializer
  11. {
  12. private static byte[] ProtocolVersionV311Name { get; } = Encoding.UTF8.GetBytes("MQTT");
  13. private static byte[] ProtocolVersionV310Name { get; } = Encoding.UTF8.GetBytes("MQIsdp");
  14. public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311;
  15. public ArraySegment<byte> Serialize(MqttBasePacket packet)
  16. {
  17. if (packet == null) throw new ArgumentNullException(nameof(packet));
  18. using (var stream = new MemoryStream(128))
  19. {
  20. // Leave enough head space for max header size (fixed + 4 variable remaining length)
  21. stream.Position = 5;
  22. var fixedHeader = SerializePacket(packet, stream);
  23. stream.Position = 1;
  24. var remainingLength = MqttPacketWriter.EncodeRemainingLength((int)stream.Length - 5, stream);
  25. var headerSize = remainingLength + 1;
  26. var headerOffset = 5 - headerSize;
  27. // Position cursor on correct offset on beginining of array (has leading 0x0)
  28. stream.Position = headerOffset;
  29. stream.WriteByte(fixedHeader);
  30. #if NET461 || NET452 || NETSTANDARD2_0
  31. var buffer = stream.GetBuffer();
  32. #else
  33. var buffer = stream.ToArray();
  34. #endif
  35. return new ArraySegment<byte>(buffer, headerOffset, (int)stream.Length - headerOffset);
  36. }
  37. }
  38. private byte SerializePacket(MqttBasePacket packet, Stream stream)
  39. {
  40. switch (packet)
  41. {
  42. case MqttConnectPacket connectPacket: return Serialize(connectPacket, stream);
  43. case MqttConnAckPacket connAckPacket: return Serialize(connAckPacket, stream);
  44. case MqttDisconnectPacket _: return SerializeEmptyPacket(MqttControlPacketType.Disconnect);
  45. case MqttPingReqPacket _: return SerializeEmptyPacket(MqttControlPacketType.PingReq);
  46. case MqttPingRespPacket _: return SerializeEmptyPacket(MqttControlPacketType.PingResp);
  47. case MqttPublishPacket publishPacket: return Serialize(publishPacket, stream);
  48. case MqttPubAckPacket pubAckPacket: return Serialize(pubAckPacket, stream);
  49. case MqttPubRecPacket pubRecPacket: return Serialize(pubRecPacket, stream);
  50. case MqttPubRelPacket pubRelPacket: return Serialize(pubRelPacket, stream);
  51. case MqttPubCompPacket pubCompPacket: return Serialize(pubCompPacket, stream);
  52. case MqttSubscribePacket subscribePacket: return Serialize(subscribePacket, stream);
  53. case MqttSubAckPacket subAckPacket: return Serialize(subAckPacket, stream);
  54. case MqttUnsubscribePacket unsubscribePacket: return Serialize(unsubscribePacket, stream);
  55. case MqttUnsubAckPacket unsubAckPacket: return Serialize(unsubAckPacket, stream);
  56. default: throw new MqttProtocolViolationException("Packet type invalid.");
  57. }
  58. }
  59. public MqttBasePacket Deserialize(MqttPacketHeader header, Stream stream)
  60. {
  61. if (header == null) throw new ArgumentNullException(nameof(header));
  62. if (stream == null) throw new ArgumentNullException(nameof(stream));
  63. switch (header.ControlPacketType)
  64. {
  65. case MqttControlPacketType.Connect: return DeserializeConnect(stream);
  66. case MqttControlPacketType.ConnAck: return DeserializeConnAck(stream);
  67. case MqttControlPacketType.Disconnect: return new MqttDisconnectPacket();
  68. case MqttControlPacketType.Publish: return DeserializePublish(stream, header);
  69. case MqttControlPacketType.PubAck: return DeserializePubAck(stream);
  70. case MqttControlPacketType.PubRec: return DeserializePubRec(stream);
  71. case MqttControlPacketType.PubRel: return DeserializePubRel(stream);
  72. case MqttControlPacketType.PubComp: return DeserializePubComp(stream);
  73. case MqttControlPacketType.PingReq: return new MqttPingReqPacket();
  74. case MqttControlPacketType.PingResp: return new MqttPingRespPacket();
  75. case MqttControlPacketType.Subscribe: return DeserializeSubscribe(stream, header);
  76. case MqttControlPacketType.SubAck: return DeserializeSubAck(stream, header);
  77. case MqttControlPacketType.Unsubscibe: return DeserializeUnsubscribe(stream, header);
  78. case MqttControlPacketType.UnsubAck: return DeserializeUnsubAck(stream);
  79. default: throw new MqttProtocolViolationException($"Packet type ({(int)header.ControlPacketType}) not supported.");
  80. }
  81. }
  82. private static MqttBasePacket DeserializeUnsubAck(Stream stream)
  83. {
  84. return new MqttUnsubAckPacket
  85. {
  86. PacketIdentifier = stream.ReadUInt16()
  87. };
  88. }
  89. private static MqttBasePacket DeserializePubComp(Stream stream)
  90. {
  91. return new MqttPubCompPacket
  92. {
  93. PacketIdentifier = stream.ReadUInt16()
  94. };
  95. }
  96. private static MqttBasePacket DeserializePubRel(Stream stream)
  97. {
  98. return new MqttPubRelPacket
  99. {
  100. PacketIdentifier = stream.ReadUInt16()
  101. };
  102. }
  103. private static MqttBasePacket DeserializePubRec(Stream stream)
  104. {
  105. return new MqttPubRecPacket
  106. {
  107. PacketIdentifier = stream.ReadUInt16()
  108. };
  109. }
  110. private static MqttBasePacket DeserializePubAck(Stream stream)
  111. {
  112. return new MqttPubAckPacket
  113. {
  114. PacketIdentifier = stream.ReadUInt16()
  115. };
  116. }
  117. private static MqttBasePacket DeserializeUnsubscribe(Stream stream, MqttPacketHeader header)
  118. {
  119. var packet = new MqttUnsubscribePacket
  120. {
  121. PacketIdentifier = stream.ReadUInt16(),
  122. };
  123. while (stream.Position != header.BodyLength)
  124. {
  125. packet.TopicFilters.Add(stream.ReadStringWithLengthPrefix());
  126. }
  127. return packet;
  128. }
  129. private static MqttBasePacket DeserializeSubscribe(Stream stream, MqttPacketHeader header)
  130. {
  131. var packet = new MqttSubscribePacket
  132. {
  133. PacketIdentifier = stream.ReadUInt16()
  134. };
  135. while (stream.Position != header.BodyLength)
  136. {
  137. packet.TopicFilters.Add(new TopicFilter(
  138. stream.ReadStringWithLengthPrefix(),
  139. (MqttQualityOfServiceLevel)stream.ReadByte()));
  140. }
  141. return packet;
  142. }
  143. private static MqttBasePacket DeserializePublish(Stream stream, MqttPacketHeader mqttPacketHeader)
  144. {
  145. var fixedHeader = new ByteReader(mqttPacketHeader.FixedHeader);
  146. var retain = fixedHeader.Read();
  147. var qualityOfServiceLevel = (MqttQualityOfServiceLevel)fixedHeader.Read(2);
  148. var dup = fixedHeader.Read();
  149. var topic = stream.ReadStringWithLengthPrefix();
  150. ushort? packetIdentifier = null;
  151. if (qualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
  152. {
  153. packetIdentifier = stream.ReadUInt16();
  154. }
  155. var packet = new MqttPublishPacket
  156. {
  157. PacketIdentifier = packetIdentifier,
  158. Retain = retain,
  159. Topic = topic,
  160. Payload = stream.ReadRemainingData(mqttPacketHeader),
  161. QualityOfServiceLevel = qualityOfServiceLevel,
  162. Dup = dup
  163. };
  164. return packet;
  165. }
  166. private static MqttBasePacket DeserializeConnect(Stream stream)
  167. {
  168. stream.ReadBytes(2); // Skip 2 bytes for header and remaining length.
  169. MqttProtocolVersion protocolVersion;
  170. var protocolName = stream.ReadBytes(4);
  171. if (protocolName.SequenceEqual(ProtocolVersionV311Name))
  172. {
  173. protocolVersion = MqttProtocolVersion.V311;
  174. }
  175. else
  176. {
  177. protocolName = protocolName.Concat(stream.ReadBytes(2)).ToArray();
  178. if (protocolName.SequenceEqual(ProtocolVersionV310Name))
  179. {
  180. protocolVersion = MqttProtocolVersion.V310;
  181. }
  182. else
  183. {
  184. throw new MqttProtocolViolationException("Protocol name is not supported.");
  185. }
  186. }
  187. stream.ReadByte(); // Skip protocol level
  188. var connectFlags = stream.ReadByte();
  189. var connectFlagsReader = new ByteReader(connectFlags);
  190. connectFlagsReader.Read(); // Reserved.
  191. var packet = new MqttConnectPacket
  192. {
  193. ProtocolVersion = protocolVersion,
  194. CleanSession = connectFlagsReader.Read()
  195. };
  196. var willFlag = connectFlagsReader.Read();
  197. var willQoS = connectFlagsReader.Read(2);
  198. var willRetain = connectFlagsReader.Read();
  199. var passwordFlag = connectFlagsReader.Read();
  200. var usernameFlag = connectFlagsReader.Read();
  201. packet.KeepAlivePeriod = stream.ReadUInt16();
  202. packet.ClientId = stream.ReadStringWithLengthPrefix();
  203. if (willFlag)
  204. {
  205. packet.WillMessage = new MqttApplicationMessage
  206. {
  207. Topic = stream.ReadStringWithLengthPrefix(),
  208. Payload = stream.ReadWithLengthPrefix(),
  209. QualityOfServiceLevel = (MqttQualityOfServiceLevel)willQoS,
  210. Retain = willRetain
  211. };
  212. }
  213. if (usernameFlag)
  214. {
  215. packet.Username = stream.ReadStringWithLengthPrefix();
  216. }
  217. if (passwordFlag)
  218. {
  219. packet.Password = stream.ReadStringWithLengthPrefix();
  220. }
  221. ValidateConnectPacket(packet);
  222. return packet;
  223. }
  224. private static MqttBasePacket DeserializeSubAck(Stream stream, MqttPacketHeader header)
  225. {
  226. var packet = new MqttSubAckPacket
  227. {
  228. PacketIdentifier = stream.ReadUInt16()
  229. };
  230. while (stream.Position != header.BodyLength)
  231. {
  232. packet.SubscribeReturnCodes.Add((MqttSubscribeReturnCode)stream.ReadByte());
  233. }
  234. return packet;
  235. }
  236. private MqttBasePacket DeserializeConnAck(Stream stream)
  237. {
  238. var packet = new MqttConnAckPacket();
  239. var firstByteReader = new ByteReader(stream.ReadByte());
  240. if (ProtocolVersion == MqttProtocolVersion.V311)
  241. {
  242. packet.IsSessionPresent = firstByteReader.Read();
  243. }
  244. packet.ConnectReturnCode = (MqttConnectReturnCode)stream.ReadByte();
  245. return packet;
  246. }
  247. private static void ValidateConnectPacket(MqttConnectPacket packet)
  248. {
  249. if (packet == null) throw new ArgumentNullException(nameof(packet));
  250. if (string.IsNullOrEmpty(packet.ClientId) && !packet.CleanSession)
  251. {
  252. throw new MqttProtocolViolationException("CleanSession must be set if ClientId is empty [MQTT-3.1.3-7].");
  253. }
  254. }
  255. private static void ValidatePublishPacket(MqttPublishPacket packet)
  256. {
  257. if (packet == null) throw new ArgumentNullException(nameof(packet));
  258. if (packet.QualityOfServiceLevel == 0 && packet.Dup)
  259. {
  260. throw new MqttProtocolViolationException("Dup flag must be false for QoS 0 packets [MQTT-3.3.1-2].");
  261. }
  262. }
  263. private byte Serialize(MqttConnectPacket packet, Stream stream)
  264. {
  265. ValidateConnectPacket(packet);
  266. // Write variable header
  267. if (ProtocolVersion == MqttProtocolVersion.V311)
  268. {
  269. stream.WriteWithLengthPrefix(ProtocolVersionV311Name);
  270. stream.WriteByte(0x04); // 3.1.2.2 Protocol Level 4
  271. }
  272. else
  273. {
  274. stream.WriteWithLengthPrefix(ProtocolVersionV310Name);
  275. stream.WriteByte(0x03); // Protocol Level 3
  276. }
  277. var connectFlags = new ByteWriter(); // 3.1.2.3 Connect Flags
  278. connectFlags.Write(false); // Reserved
  279. connectFlags.Write(packet.CleanSession);
  280. connectFlags.Write(packet.WillMessage != null);
  281. if (packet.WillMessage != null)
  282. {
  283. connectFlags.Write((int)packet.WillMessage.QualityOfServiceLevel, 2);
  284. connectFlags.Write(packet.WillMessage.Retain);
  285. }
  286. else
  287. {
  288. connectFlags.Write(0, 2);
  289. connectFlags.Write(false);
  290. }
  291. if (packet.Password != null && packet.Username == null)
  292. {
  293. throw new MqttProtocolViolationException("If the User Name Flag is set to 0, the Password Flag MUST be set to 0 [MQTT-3.1.2-22].");
  294. }
  295. connectFlags.Write(packet.Password != null);
  296. connectFlags.Write(packet.Username != null);
  297. stream.Write(connectFlags);
  298. stream.Write(packet.KeepAlivePeriod);
  299. stream.WriteWithLengthPrefix(packet.ClientId);
  300. if (packet.WillMessage != null)
  301. {
  302. stream.WriteWithLengthPrefix(packet.WillMessage.Topic);
  303. stream.WriteWithLengthPrefix(packet.WillMessage.Payload);
  304. }
  305. if (packet.Username != null)
  306. {
  307. stream.WriteWithLengthPrefix(packet.Username);
  308. }
  309. if (packet.Password != null)
  310. {
  311. stream.WriteWithLengthPrefix(packet.Password);
  312. }
  313. return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Connect);
  314. }
  315. private byte Serialize(MqttConnAckPacket packet, Stream stream)
  316. {
  317. if (ProtocolVersion == MqttProtocolVersion.V310)
  318. {
  319. stream.WriteByte(0);
  320. }
  321. else if (ProtocolVersion == MqttProtocolVersion.V311)
  322. {
  323. var connectAcknowledgeFlags = new ByteWriter();
  324. connectAcknowledgeFlags.Write(packet.IsSessionPresent);
  325. stream.Write(connectAcknowledgeFlags);
  326. }
  327. else
  328. {
  329. throw new MqttProtocolViolationException("Protocol version not supported.");
  330. }
  331. stream.WriteByte((byte)packet.ConnectReturnCode);
  332. return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.ConnAck);
  333. }
  334. private static byte Serialize(MqttPubRelPacket packet, Stream stream)
  335. {
  336. if (!packet.PacketIdentifier.HasValue)
  337. {
  338. throw new MqttProtocolViolationException("PubRel packet has no packet identifier.");
  339. }
  340. stream.Write(packet.PacketIdentifier.Value);
  341. return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02);
  342. }
  343. private static byte Serialize(MqttPublishPacket packet, Stream stream)
  344. {
  345. ValidatePublishPacket(packet);
  346. stream.WriteWithLengthPrefix(packet.Topic);
  347. if (packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
  348. {
  349. if (!packet.PacketIdentifier.HasValue)
  350. {
  351. throw new MqttProtocolViolationException("Publish packet has no packet identifier.");
  352. }
  353. stream.Write(packet.PacketIdentifier.Value);
  354. }
  355. else
  356. {
  357. if (packet.PacketIdentifier > 0)
  358. {
  359. throw new MqttProtocolViolationException("Packet identifier must be empty if QoS == 0 [MQTT-2.3.1-5].");
  360. }
  361. }
  362. if (packet.Payload?.Length > 0)
  363. {
  364. stream.Write(packet.Payload, 0, packet.Payload.Length);
  365. }
  366. byte fixedHeader = 0;
  367. if (packet.Retain)
  368. {
  369. fixedHeader |= 0x01;
  370. }
  371. fixedHeader |= (byte)((byte)packet.QualityOfServiceLevel << 1);
  372. if (packet.Dup)
  373. {
  374. fixedHeader |= 0x08;
  375. }
  376. return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Publish, fixedHeader);
  377. }
  378. private static byte Serialize(MqttPubAckPacket packet, Stream stream)
  379. {
  380. if (!packet.PacketIdentifier.HasValue)
  381. {
  382. throw new MqttProtocolViolationException("PubAck packet has no packet identifier.");
  383. }
  384. stream.Write(packet.PacketIdentifier.Value);
  385. return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubAck);
  386. }
  387. private static byte Serialize(MqttPubRecPacket packet, Stream stream)
  388. {
  389. if (!packet.PacketIdentifier.HasValue)
  390. {
  391. throw new MqttProtocolViolationException("PubRec packet has no packet identifier.");
  392. }
  393. stream.Write(packet.PacketIdentifier.Value);
  394. return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRec);
  395. }
  396. private static byte Serialize(MqttPubCompPacket packet, Stream stream)
  397. {
  398. if (!packet.PacketIdentifier.HasValue)
  399. {
  400. throw new MqttProtocolViolationException("PubComp packet has no packet identifier.");
  401. }
  402. stream.Write(packet.PacketIdentifier.Value);
  403. return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubComp);
  404. }
  405. private static byte Serialize(MqttSubscribePacket packet, Stream stream)
  406. {
  407. if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3].");
  408. if (!packet.PacketIdentifier.HasValue)
  409. {
  410. throw new MqttProtocolViolationException("Subscribe packet has no packet identifier.");
  411. }
  412. stream.Write(packet.PacketIdentifier.Value);
  413. if (packet.TopicFilters?.Count > 0)
  414. {
  415. foreach (var topicFilter in packet.TopicFilters)
  416. {
  417. stream.WriteWithLengthPrefix(topicFilter.Topic);
  418. stream.WriteByte((byte)topicFilter.QualityOfServiceLevel);
  419. }
  420. }
  421. return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Subscribe, 0x02);
  422. }
  423. private static byte Serialize(MqttSubAckPacket packet, Stream stream)
  424. {
  425. if (!packet.PacketIdentifier.HasValue)
  426. {
  427. throw new MqttProtocolViolationException("SubAck packet has no packet identifier.");
  428. }
  429. stream.Write(packet.PacketIdentifier.Value);
  430. if (packet.SubscribeReturnCodes?.Any() == true)
  431. {
  432. foreach (var packetSubscribeReturnCode in packet.SubscribeReturnCodes)
  433. {
  434. stream.WriteByte((byte)packetSubscribeReturnCode);
  435. }
  436. }
  437. return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.SubAck);
  438. }
  439. private static byte Serialize(MqttUnsubscribePacket packet, Stream stream)
  440. {
  441. if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.10.3-2].");
  442. if (!packet.PacketIdentifier.HasValue)
  443. {
  444. throw new MqttProtocolViolationException("Unsubscribe packet has no packet identifier.");
  445. }
  446. stream.Write(packet.PacketIdentifier.Value);
  447. if (packet.TopicFilters?.Any() == true)
  448. {
  449. foreach (var topicFilter in packet.TopicFilters)
  450. {
  451. stream.WriteWithLengthPrefix(topicFilter);
  452. }
  453. }
  454. return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Unsubscibe, 0x02);
  455. }
  456. private static byte Serialize(MqttUnsubAckPacket packet, Stream stream)
  457. {
  458. if (!packet.PacketIdentifier.HasValue)
  459. {
  460. throw new MqttProtocolViolationException("UnsubAck packet has no packet identifier.");
  461. }
  462. stream.Write(packet.PacketIdentifier.Value);
  463. return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck);
  464. }
  465. private static byte SerializeEmptyPacket(MqttControlPacketType type)
  466. {
  467. return MqttPacketWriter.BuildFixedHeader(type);
  468. }
  469. }
  470. }