MqttMsgConnect.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. /*
  2. Copyright (c) 2013, 2014 Paolo Patierno
  3. All rights reserved. This program and the accompanying materials
  4. are made available under the terms of the Eclipse Public License v1.0
  5. and Eclipse Distribution License v1.0 which accompany this distribution.
  6. The Eclipse Public License is available at
  7. http://www.eclipse.org/legal/epl-v10.html
  8. and the Eclipse Distribution License is available at
  9. http://www.eclipse.org/org/documents/edl-v10.php.
  10. Contributors:
  11. Paolo Patierno - initial API and implementation and/or initial documentation
  12. */
  13. using System;
  14. using System.Text;
  15. using uPLibrary.Networking.M2Mqtt.Exceptions;
  16. namespace uPLibrary.Networking.M2Mqtt.Messages
  17. {
  18. /// <summary>
  19. /// Class for CONNECT message from client to broker
  20. /// </summary>
  21. public class MqttMsgConnect : MqttMsgBase
  22. {
  23. #region Constants...
  24. // protocol name supported
  25. internal const string PROTOCOL_NAME = "MQIsdp";
  26. // max length for client id
  27. internal const int CLIENT_ID_MAX_LENGTH = 23;
  28. // variable header fields
  29. internal const byte PROTOCOL_NAME_LEN_SIZE = 2;
  30. internal const byte PROTOCOL_NAME_SIZE = 6;
  31. internal const byte PROTOCOL_VERSION_NUMBER_SIZE = 1;
  32. internal const byte CONNECT_FLAGS_SIZE = 1;
  33. internal const byte KEEP_ALIVE_TIME_SIZE = 2;
  34. internal const byte PROTOCOL_VERSION = 0x03;
  35. internal const ushort KEEP_ALIVE_PERIOD_DEFAULT = 60; // seconds
  36. internal const ushort MAX_KEEP_ALIVE = 65535; // 16 bit
  37. // connect flags
  38. internal const byte USERNAME_FLAG_MASK = 0x80;
  39. internal const byte USERNAME_FLAG_OFFSET = 0x07;
  40. internal const byte USERNAME_FLAG_SIZE = 0x01;
  41. internal const byte PASSWORD_FLAG_MASK = 0x40;
  42. internal const byte PASSWORD_FLAG_OFFSET = 0x06;
  43. internal const byte PASSWORD_FLAG_SIZE = 0x01;
  44. internal const byte WILL_RETAIN_FLAG_MASK = 0x20;
  45. internal const byte WILL_RETAIN_FLAG_OFFSET = 0x05;
  46. internal const byte WILL_RETAIN_FLAG_SIZE = 0x01;
  47. internal const byte WILL_QOS_FLAG_MASK = 0x18;
  48. internal const byte WILL_QOS_FLAG_OFFSET = 0x03;
  49. internal const byte WILL_QOS_FLAG_SIZE = 0x02;
  50. internal const byte WILL_FLAG_MASK = 0x04;
  51. internal const byte WILL_FLAG_OFFSET = 0x02;
  52. internal const byte WILL_FLAG_SIZE = 0x01;
  53. internal const byte CLEAN_SESSION_FLAG_MASK = 0x02;
  54. internal const byte CLEAN_SESSION_FLAG_OFFSET = 0x01;
  55. internal const byte CLEAN_SESSION_FLAG_SIZE = 0x01;
  56. #endregion
  57. #region Properties...
  58. /// <summary>
  59. /// Protocol name
  60. /// </summary>
  61. public string ProtocolName
  62. {
  63. get { return this.protocolName; }
  64. set { this.protocolName = value; }
  65. }
  66. /// <summary>
  67. /// Protocol version
  68. /// </summary>
  69. public byte ProtocolVersion
  70. {
  71. get { return this.protocolVersion; }
  72. set { this.protocolVersion = value; }
  73. }
  74. /// <summary>
  75. /// Client identifier
  76. /// </summary>
  77. public string ClientId
  78. {
  79. get { return this.clientId; }
  80. set { this.clientId = value; }
  81. }
  82. /// <summary>
  83. /// Will retain flag
  84. /// </summary>
  85. public bool WillRetain
  86. {
  87. get { return this.willRetain; }
  88. set { this.willRetain = value; }
  89. }
  90. /// <summary>
  91. /// Will QOS level
  92. /// </summary>
  93. public byte WillQosLevel
  94. {
  95. get { return this.willQosLevel; }
  96. set { this.willQosLevel = value; }
  97. }
  98. /// <summary>
  99. /// Will flag
  100. /// </summary>
  101. public bool WillFlag
  102. {
  103. get { return this.willFlag; }
  104. set { this.willFlag = value; }
  105. }
  106. /// <summary>
  107. /// Will topic
  108. /// </summary>
  109. public string WillTopic
  110. {
  111. get { return this.willTopic; }
  112. set { this.willTopic = value; }
  113. }
  114. /// <summary>
  115. /// Will message
  116. /// </summary>
  117. public string WillMessage
  118. {
  119. get { return this.willMessage; }
  120. set { this.willMessage = value; }
  121. }
  122. /// <summary>
  123. /// Username
  124. /// </summary>
  125. public string Username
  126. {
  127. get { return this.username; }
  128. set { this.username = value; }
  129. }
  130. /// <summary>
  131. /// Password
  132. /// </summary>
  133. public string Password
  134. {
  135. get { return this.password; }
  136. set { this.password = value; }
  137. }
  138. /// <summary>
  139. /// Clean session flag
  140. /// </summary>
  141. public bool CleanSession
  142. {
  143. get { return this.cleanSession; }
  144. set { this.cleanSession = value; }
  145. }
  146. /// <summary>
  147. /// Keep alive period
  148. /// </summary>
  149. public ushort KeepAlivePeriod
  150. {
  151. get { return this.keepAlivePeriod; }
  152. set { this.keepAlivePeriod = value; }
  153. }
  154. #endregion
  155. // protocol name
  156. private string protocolName;
  157. // protocol version
  158. private byte protocolVersion;
  159. // client identifier
  160. private string clientId;
  161. // will retain flag
  162. protected bool willRetain;
  163. // will quality of service level
  164. protected byte willQosLevel;
  165. // will flag
  166. private bool willFlag;
  167. // will topic
  168. private string willTopic;
  169. // will message
  170. private string willMessage;
  171. // username
  172. private string username;
  173. // password
  174. private string password;
  175. // clean session flag
  176. private bool cleanSession;
  177. // keep alive period (in sec)
  178. private ushort keepAlivePeriod;
  179. /// <summary>
  180. /// Constructor
  181. /// </summary>
  182. public MqttMsgConnect()
  183. {
  184. this.type = MQTT_MSG_CONNECT_TYPE;
  185. this.protocolName = PROTOCOL_NAME;
  186. this.protocolVersion = PROTOCOL_VERSION;
  187. }
  188. /// <summary>
  189. /// Constructor
  190. /// </summary>
  191. /// <param name="clientId">Client identifier</param>
  192. public MqttMsgConnect(string clientId) :
  193. this(clientId, null, null, false, QOS_LEVEL_AT_LEAST_ONCE, false, null, null, true, KEEP_ALIVE_PERIOD_DEFAULT)
  194. {
  195. }
  196. /// <summary>
  197. /// Constructor
  198. /// </summary>
  199. /// <param name="clientId">Client identifier</param>
  200. /// <param name="username">Username</param>
  201. /// <param name="password">Password</param>
  202. /// <param name="willRetain">Will retain flag</param>
  203. /// <param name="willQosLevel">Will QOS level</param>
  204. /// <param name="willFlag">Will flag</param>
  205. /// <param name="willTopic">Will topic</param>
  206. /// <param name="willMessage">Will message</param>
  207. /// <param name="cleanSession">Clean sessione flag</param>
  208. /// <param name="keepAlivePeriod">Keep alive period</param>
  209. public MqttMsgConnect(string clientId,
  210. string username,
  211. string password,
  212. bool willRetain,
  213. byte willQosLevel,
  214. bool willFlag,
  215. string willTopic,
  216. string willMessage,
  217. bool cleanSession,
  218. ushort keepAlivePeriod
  219. )
  220. {
  221. this.type = MQTT_MSG_CONNECT_TYPE;
  222. this.protocolName = PROTOCOL_NAME;
  223. this.protocolVersion = PROTOCOL_VERSION;
  224. this.clientId = clientId;
  225. this.username = username;
  226. this.password = password;
  227. this.willRetain = willRetain;
  228. this.willQosLevel = willQosLevel;
  229. this.willFlag = willFlag;
  230. this.willTopic = willTopic;
  231. this.willMessage = willMessage;
  232. this.cleanSession = cleanSession;
  233. this.keepAlivePeriod = keepAlivePeriod;
  234. }
  235. /// <summary>
  236. /// Parse bytes for a CONNECT message
  237. /// </summary>
  238. /// <param name="fixedHeaderFirstByte">First fixed header byte</param>
  239. /// <param name="channel">Channel connected to the broker</param>
  240. /// <returns>CONNECT message instance</returns>
  241. public static MqttMsgConnect Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel)
  242. {
  243. byte[] buffer;
  244. int index = 0;
  245. int protNameUtf8Length;
  246. byte[] protNameUtf8;
  247. bool isUsernameFlag;
  248. bool isPasswordFlag;
  249. int clientIdUtf8Length;
  250. byte[] clientIdUtf8;
  251. int willTopicUtf8Length;
  252. byte[] willTopicUtf8;
  253. int willMessageUtf8Length;
  254. byte[] willMessageUtf8;
  255. int usernameUtf8Length;
  256. byte[] usernameUtf8;
  257. int passwordUtf8Length;
  258. byte[] passwordUtf8;
  259. MqttMsgConnect msg = new MqttMsgConnect();
  260. // get remaining length and allocate buffer
  261. int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
  262. buffer = new byte[remainingLength];
  263. // read bytes from socket...
  264. channel.Receive(buffer);
  265. // protocol name
  266. protNameUtf8Length = ((buffer[index++] << 8) & 0xFF00);
  267. protNameUtf8Length |= buffer[index++];
  268. protNameUtf8 = new byte[protNameUtf8Length];
  269. Array.Copy(buffer, index, protNameUtf8, 0, protNameUtf8Length);
  270. index += protNameUtf8Length;
  271. msg.protocolName = new String(Encoding.UTF8.GetChars(protNameUtf8));
  272. // protocol version
  273. msg.protocolVersion = buffer[index];
  274. index += PROTOCOL_VERSION_NUMBER_SIZE;
  275. // connect flags
  276. isUsernameFlag = (buffer[index] & USERNAME_FLAG_MASK) != 0x00;
  277. isPasswordFlag = (buffer[index] & PASSWORD_FLAG_MASK) != 0x00;
  278. msg.willRetain = (buffer[index] & WILL_RETAIN_FLAG_MASK) != 0x00;
  279. msg.willQosLevel = (byte)((buffer[index] & WILL_QOS_FLAG_MASK) >> WILL_QOS_FLAG_OFFSET);
  280. msg.willFlag = (buffer[index] & WILL_FLAG_MASK) != 0x00;
  281. msg.cleanSession = (buffer[index] & CLEAN_SESSION_FLAG_MASK) != 0x00;
  282. index += CONNECT_FLAGS_SIZE;
  283. // keep alive timer
  284. msg.keepAlivePeriod = (ushort)((buffer[index++] << 8) & 0xFF00);
  285. msg.keepAlivePeriod |= buffer[index++];
  286. // client identifier
  287. clientIdUtf8Length = ((buffer[index++] << 8) & 0xFF00);
  288. clientIdUtf8Length |= buffer[index++];
  289. clientIdUtf8 = new byte[clientIdUtf8Length];
  290. Array.Copy(buffer, index, clientIdUtf8, 0, clientIdUtf8Length);
  291. index += clientIdUtf8Length;
  292. msg.clientId = new String(Encoding.UTF8.GetChars(clientIdUtf8));
  293. // will topic and will message
  294. if (msg.willFlag)
  295. {
  296. willTopicUtf8Length = ((buffer[index++] << 8) & 0xFF00);
  297. willTopicUtf8Length |= buffer[index++];
  298. willTopicUtf8 = new byte[willTopicUtf8Length];
  299. Array.Copy(buffer, index, willTopicUtf8, 0, willTopicUtf8Length);
  300. index += willTopicUtf8Length;
  301. msg.willTopic = new String(Encoding.UTF8.GetChars(willTopicUtf8));
  302. willMessageUtf8Length = ((buffer[index++] << 8) & 0xFF00);
  303. willMessageUtf8Length |= buffer[index++];
  304. willMessageUtf8 = new byte[willMessageUtf8Length];
  305. Array.Copy(buffer, index, willMessageUtf8, 0, willMessageUtf8Length);
  306. index += willMessageUtf8Length;
  307. msg.willMessage = new String(Encoding.UTF8.GetChars(willMessageUtf8));
  308. }
  309. // username
  310. if (isUsernameFlag)
  311. {
  312. usernameUtf8Length = ((buffer[index++] << 8) & 0xFF00);
  313. usernameUtf8Length |= buffer[index++];
  314. usernameUtf8 = new byte[usernameUtf8Length];
  315. Array.Copy(buffer, index, usernameUtf8, 0, usernameUtf8Length);
  316. index += usernameUtf8Length;
  317. msg.username = new String(Encoding.UTF8.GetChars(usernameUtf8));
  318. }
  319. // password
  320. if (isPasswordFlag)
  321. {
  322. passwordUtf8Length = ((buffer[index++] << 8) & 0xFF00);
  323. passwordUtf8Length |= buffer[index++];
  324. passwordUtf8 = new byte[passwordUtf8Length];
  325. Array.Copy(buffer, index, passwordUtf8, 0, passwordUtf8Length);
  326. index += passwordUtf8Length;
  327. msg.password = new String(Encoding.UTF8.GetChars(passwordUtf8));
  328. }
  329. return msg;
  330. }
  331. public override byte[] GetBytes()
  332. {
  333. int fixedHeaderSize = 0;
  334. int varHeaderSize = 0;
  335. int payloadSize = 0;
  336. int remainingLength = 0;
  337. byte[] buffer;
  338. int index = 0;
  339. byte[] clientIdUtf8 = Encoding.UTF8.GetBytes(this.clientId);
  340. byte[] willTopicUtf8 = (this.willTopic != null) ? Encoding.UTF8.GetBytes(this.willTopic) : null;
  341. byte[] willMessageUtf8 = (this.willMessage != null) ? Encoding.UTF8.GetBytes(this.willMessage) : null;
  342. byte[] usernameUtf8 = (this.username != null) ? Encoding.UTF8.GetBytes(this.username) : null;
  343. byte[] passwordUtf8 = (this.password != null) ? Encoding.UTF8.GetBytes(this.password) : null;
  344. // will flag set but will topic wrong
  345. if (this.willFlag && (willTopicUtf8.Length == 0))
  346. throw new MqttClientException(MqttClientErrorCode.WillTopicWrong);
  347. if (this.keepAlivePeriod > MAX_KEEP_ALIVE)
  348. throw new MqttClientException(MqttClientErrorCode.KeepAliveWrong);
  349. // protocol name field size
  350. varHeaderSize += (PROTOCOL_NAME_LEN_SIZE + PROTOCOL_NAME_SIZE);
  351. // protocol version number field size
  352. varHeaderSize += PROTOCOL_VERSION_NUMBER_SIZE;
  353. // connect flags field size
  354. varHeaderSize += CONNECT_FLAGS_SIZE;
  355. // keep alive timer field size
  356. varHeaderSize += KEEP_ALIVE_TIME_SIZE;
  357. // client identifier field size
  358. payloadSize += clientIdUtf8.Length + 2;
  359. // will topic field size
  360. payloadSize += (willTopicUtf8 != null) ? (willTopicUtf8.Length + 2) : 0;
  361. // will message field size
  362. payloadSize += (willMessageUtf8 != null) ? (willMessageUtf8.Length + 2) : 0;
  363. // username field size
  364. payloadSize += (usernameUtf8 != null) ? (usernameUtf8.Length + 2) : 0;
  365. // password field size
  366. payloadSize += (passwordUtf8 != null) ? (passwordUtf8.Length + 2) : 0;
  367. remainingLength += (varHeaderSize + payloadSize);
  368. // first byte of fixed header
  369. fixedHeaderSize = 1;
  370. int temp = remainingLength;
  371. // increase fixed header size based on remaining length
  372. // (each remaining length byte can encode until 128)
  373. do
  374. {
  375. fixedHeaderSize++;
  376. temp = temp / 128;
  377. } while (temp > 0);
  378. // allocate buffer for message
  379. buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize];
  380. // first fixed header byte
  381. buffer[index++] = (MQTT_MSG_CONNECT_TYPE << MSG_TYPE_OFFSET);
  382. // encode remaining length
  383. index = this.encodeRemainingLength(remainingLength, buffer, index);
  384. // protocol name
  385. buffer[index++] = 0; // MSB protocol name size
  386. buffer[index++] = PROTOCOL_NAME_SIZE; // LSB protocol name size
  387. buffer[index++] = (byte)'M';
  388. buffer[index++] = (byte)'Q';
  389. buffer[index++] = (byte)'I';
  390. buffer[index++] = (byte)'s';
  391. buffer[index++] = (byte)'d';
  392. buffer[index++] = (byte)'p';
  393. // protocol version
  394. buffer[index++] = PROTOCOL_VERSION;
  395. // connect flags
  396. byte connectFlags = 0x00;
  397. connectFlags |= (this.username != null) ? (byte)(1 << USERNAME_FLAG_OFFSET) : (byte)0x00;
  398. connectFlags |= (this.password != null) ? (byte)(1 << PASSWORD_FLAG_OFFSET) : (byte)0x00;
  399. connectFlags |= (this.willRetain) ? (byte)(1 << WILL_RETAIN_FLAG_OFFSET) : (byte)0x00;
  400. connectFlags |= (byte)(this.willQosLevel << WILL_QOS_FLAG_OFFSET);
  401. connectFlags |= (this.willFlag) ? (byte)(1 << WILL_FLAG_OFFSET) : (byte)0x00;
  402. connectFlags |= (this.cleanSession) ? (byte)(1 << CLEAN_SESSION_FLAG_OFFSET) : (byte)0x00;
  403. buffer[index++] = connectFlags;
  404. // keep alive period
  405. buffer[index++] = (byte)((this.keepAlivePeriod >> 8) & 0x00FF); // MSB
  406. buffer[index++] = (byte)(this.keepAlivePeriod & 0x00FF); // LSB
  407. // client identifier
  408. buffer[index++] = (byte)((clientIdUtf8.Length >> 8) & 0x00FF); // MSB
  409. buffer[index++] = (byte)(clientIdUtf8.Length & 0x00FF); // LSB
  410. Array.Copy(clientIdUtf8, 0, buffer, index, clientIdUtf8.Length);
  411. index += clientIdUtf8.Length;
  412. // will topic
  413. if (this.willFlag && (this.willTopic != null))
  414. {
  415. buffer[index++] = (byte)((willTopicUtf8.Length >> 8) & 0x00FF); // MSB
  416. buffer[index++] = (byte)(willTopicUtf8.Length & 0x00FF); // LSB
  417. Array.Copy(willTopicUtf8, 0, buffer, index, willTopicUtf8.Length);
  418. index += willTopicUtf8.Length;
  419. }
  420. // will message
  421. if (this.willFlag && (this.willMessage != null))
  422. {
  423. buffer[index++] = (byte)((willMessageUtf8.Length >> 8) & 0x00FF); // MSB
  424. buffer[index++] = (byte)(willMessageUtf8.Length & 0x00FF); // LSB
  425. Array.Copy(willMessageUtf8, 0, buffer, index, willMessageUtf8.Length);
  426. index += willMessageUtf8.Length;
  427. }
  428. // username
  429. if (this.username != null)
  430. {
  431. buffer[index++] = (byte)((usernameUtf8.Length >> 8) & 0x00FF); // MSB
  432. buffer[index++] = (byte)(usernameUtf8.Length & 0x00FF); // LSB
  433. Array.Copy(usernameUtf8, 0, buffer, index, usernameUtf8.Length);
  434. index += usernameUtf8.Length;
  435. }
  436. // password
  437. if (this.password != null)
  438. {
  439. buffer[index++] = (byte)((passwordUtf8.Length >> 8) & 0x00FF); // MSB
  440. buffer[index++] = (byte)(passwordUtf8.Length & 0x00FF); // LSB
  441. Array.Copy(passwordUtf8, 0, buffer, index, passwordUtf8.Length);
  442. index += passwordUtf8.Length;
  443. }
  444. return buffer;
  445. }
  446. public override string ToString()
  447. {
  448. #if TRACE
  449. return this.GetTraceString(
  450. "CONNECT",
  451. new object[] { "protocolName", "protocolVersion", "clientId", "willFlag", "willRetain", "willQosLevel", "willTopic", "willMessage", "username", "password", "cleanSession", "keepAlivePeriod" },
  452. new object[] { this.protocolName, this.protocolVersion, this.clientId, this.willFlag, this.willRetain, this.willQosLevel, this.willTopic, this.willMessage, this.username, this.password, this.cleanSession, this.keepAlivePeriod });
  453. #else
  454. return base.ToString();
  455. #endif
  456. }
  457. }
  458. }