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.
 
 
 
 

606 lines
20 KiB

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