MQTTProtocolClient.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877
  1. /*******************************************************************************
  2. * Copyright (c) 2009, 2019 IBM Corp.
  3. *
  4. * All rights reserved. This program and the accompanying materials
  5. * are made available under the terms of the Eclipse Public License v1.0
  6. * and Eclipse Distribution License v1.0 which accompany this distribution.
  7. *
  8. * The Eclipse Public License is available at
  9. * http://www.eclipse.org/legal/epl-v10.html
  10. * and the Eclipse Distribution License is available at
  11. * http://www.eclipse.org/org/documents/edl-v10.php.
  12. *
  13. * Contributors:
  14. * Ian Craggs - initial API and implementation and/or initial documentation
  15. * Ian Craggs, Allan Stockdill-Mander - SSL updates
  16. * Ian Craggs - fix for bug 413429 - connectionLost not called
  17. * Ian Craggs - fix for bug 421103 - trying to write to same socket, in retry
  18. * Rong Xiang, Ian Craggs - C++ compatibility
  19. * Ian Craggs - turn off DUP flag for PUBREL - MQTT 3.1.1
  20. * Ian Craggs - ensure that acks are not sent if write is outstanding on socket
  21. * Ian Craggs - MQTT 5.0 support
  22. *******************************************************************************/
  23. /**
  24. * @file
  25. * \brief Functions dealing with the MQTT protocol exchanges
  26. *
  27. * Some other related functions are in the MQTTProtocolOut module
  28. * */
  29. #include <stdlib.h>
  30. #include <string.h>
  31. #include "MQTTProtocolClient.h"
  32. #if !defined(NO_PERSISTENCE)
  33. #include "MQTTPersistence.h"
  34. #endif
  35. #include "SocketBuffer.h"
  36. #include "StackTrace.h"
  37. #include "Heap.h"
  38. #if !defined(min)
  39. #define min(A,B) ( (A) < (B) ? (A):(B))
  40. #endif
  41. extern MQTTProtocol state;
  42. extern ClientStates* bstate;
  43. static void MQTTProtocol_storeQoS0(Clients* pubclient, Publish* publish);
  44. static int MQTTProtocol_startPublishCommon(
  45. Clients* pubclient,
  46. Publish* publish,
  47. int qos,
  48. int retained);
  49. static void MQTTProtocol_retries(time_t now, Clients* client, int regardless);
  50. /**
  51. * List callback function for comparing Message structures by message id
  52. * @param a first integer value
  53. * @param b second integer value
  54. * @return boolean indicating whether a and b are equal
  55. */
  56. int messageIDCompare(void* a, void* b)
  57. {
  58. Messages* msg = (Messages*)a;
  59. return msg->msgid == *(int*)b;
  60. }
  61. /**
  62. * Assign a new message id for a client. Make sure it isn't already being used and does
  63. * not exceed the maximum.
  64. * @param client a client structure
  65. * @return the next message id to use, or 0 if none available
  66. */
  67. int MQTTProtocol_assignMsgId(Clients* client)
  68. {
  69. int start_msgid = client->msgID;
  70. int msgid = start_msgid;
  71. FUNC_ENTRY;
  72. msgid = (msgid == MAX_MSG_ID) ? 1 : msgid + 1;
  73. while (ListFindItem(client->outboundMsgs, &msgid, messageIDCompare) != NULL)
  74. {
  75. msgid = (msgid == MAX_MSG_ID) ? 1 : msgid + 1;
  76. if (msgid == start_msgid)
  77. { /* we've tried them all - none free */
  78. msgid = 0;
  79. break;
  80. }
  81. }
  82. if (msgid != 0)
  83. client->msgID = msgid;
  84. FUNC_EXIT_RC(msgid);
  85. return msgid;
  86. }
  87. static void MQTTProtocol_storeQoS0(Clients* pubclient, Publish* publish)
  88. {
  89. int len;
  90. pending_write* pw = NULL;
  91. FUNC_ENTRY;
  92. /* store the publication until the write is finished */
  93. pw = malloc(sizeof(pending_write));
  94. Log(TRACE_MIN, 12, NULL);
  95. pw->p = MQTTProtocol_storePublication(publish, &len);
  96. pw->socket = pubclient->net.socket;
  97. ListAppend(&(state.pending_writes), pw, sizeof(pending_write)+len);
  98. /* we don't copy QoS 0 messages unless we have to, so now we have to tell the socket buffer where
  99. the saved copy is */
  100. if (SocketBuffer_updateWrite(pw->socket, pw->p->topic, pw->p->payload) == NULL)
  101. Log(LOG_SEVERE, 0, "Error updating write");
  102. FUNC_EXIT;
  103. }
  104. /**
  105. * Utility function to start a new publish exchange.
  106. * @param pubclient the client to send the publication to
  107. * @param publish the publication data
  108. * @param qos the MQTT QoS to use
  109. * @param retained boolean - whether to set the MQTT retained flag
  110. * @return the completion code
  111. */
  112. static int MQTTProtocol_startPublishCommon(Clients* pubclient, Publish* publish, int qos, int retained)
  113. {
  114. int rc = TCPSOCKET_COMPLETE;
  115. FUNC_ENTRY;
  116. rc = MQTTPacket_send_publish(publish, 0, qos, retained, &pubclient->net, pubclient->clientID);
  117. if (qos == 0 && rc == TCPSOCKET_INTERRUPTED)
  118. MQTTProtocol_storeQoS0(pubclient, publish);
  119. FUNC_EXIT_RC(rc);
  120. return rc;
  121. }
  122. /**
  123. * Start a new publish exchange. Store any state necessary and try to send the packet
  124. * @param pubclient the client to send the publication to
  125. * @param publish the publication data
  126. * @param qos the MQTT QoS to use
  127. * @param retained boolean - whether to set the MQTT retained flag
  128. * @param mm - pointer to the message to send
  129. * @return the completion code
  130. */
  131. int MQTTProtocol_startPublish(Clients* pubclient, Publish* publish, int qos, int retained, Messages** mm)
  132. {
  133. Publish p = *publish;
  134. int rc = 0;
  135. FUNC_ENTRY;
  136. if (qos > 0)
  137. {
  138. *mm = MQTTProtocol_createMessage(publish, mm, qos, retained);
  139. ListAppend(pubclient->outboundMsgs, *mm, (*mm)->len);
  140. /* we change these pointers to the saved message location just in case the packet could not be written
  141. entirely; the socket buffer will use these locations to finish writing the packet */
  142. p.payload = (*mm)->publish->payload;
  143. p.topic = (*mm)->publish->topic;
  144. p.properties = (*mm)->properties;
  145. p.MQTTVersion = (*mm)->MQTTVersion;
  146. }
  147. rc = MQTTProtocol_startPublishCommon(pubclient, &p, qos, retained);
  148. FUNC_EXIT_RC(rc);
  149. return rc;
  150. }
  151. /**
  152. * Copy and store message data for retries
  153. * @param publish the publication data
  154. * @param mm - pointer to the message data to store
  155. * @param qos the MQTT QoS to use
  156. * @param retained boolean - whether to set the MQTT retained flag
  157. * @return pointer to the message data stored
  158. */
  159. Messages* MQTTProtocol_createMessage(Publish* publish, Messages **mm, int qos, int retained)
  160. {
  161. Messages* m = malloc(sizeof(Messages));
  162. FUNC_ENTRY;
  163. m->len = sizeof(Messages);
  164. if (*mm == NULL || (*mm)->publish == NULL)
  165. {
  166. int len1;
  167. *mm = m;
  168. m->publish = MQTTProtocol_storePublication(publish, &len1);
  169. m->len += len1;
  170. }
  171. else
  172. {
  173. ++(((*mm)->publish)->refcount);
  174. m->publish = (*mm)->publish;
  175. }
  176. m->msgid = publish->msgId;
  177. m->qos = qos;
  178. m->retain = retained;
  179. m->MQTTVersion = publish->MQTTVersion;
  180. if (m->MQTTVersion >= 5)
  181. m->properties = MQTTProperties_copy(&publish->properties);
  182. time(&(m->lastTouch));
  183. if (qos == 2)
  184. m->nextMessageType = PUBREC;
  185. FUNC_EXIT;
  186. return m;
  187. }
  188. /**
  189. * Store message data for possible retry
  190. * @param publish the publication data
  191. * @param len returned length of the data stored
  192. * @return the publication stored
  193. */
  194. Publications* MQTTProtocol_storePublication(Publish* publish, int* len)
  195. {
  196. Publications* p = malloc(sizeof(Publications));
  197. FUNC_ENTRY;
  198. p->refcount = 1;
  199. *len = (int)strlen(publish->topic)+1;
  200. p->topic = malloc(*len);
  201. strcpy(p->topic, publish->topic);
  202. if (Heap_findItem(publish->topic))
  203. {
  204. free(publish->topic);
  205. publish->topic = NULL;
  206. }
  207. *len += sizeof(Publications);
  208. p->topiclen = publish->topiclen;
  209. p->payloadlen = publish->payloadlen;
  210. p->payload = malloc(publish->payloadlen);
  211. memcpy(p->payload, publish->payload, p->payloadlen);
  212. *len += publish->payloadlen;
  213. ListAppend(&(state.publications), p, *len);
  214. FUNC_EXIT;
  215. return p;
  216. }
  217. /**
  218. * Remove stored message data. Opposite of storePublication
  219. * @param p stored publication to remove
  220. */
  221. void MQTTProtocol_removePublication(Publications* p)
  222. {
  223. FUNC_ENTRY;
  224. if (p && --(p->refcount) == 0)
  225. {
  226. free(p->payload);
  227. free(p->topic);
  228. ListRemove(&(state.publications), p);
  229. }
  230. FUNC_EXIT;
  231. }
  232. /**
  233. * Process an incoming publish packet for a socket
  234. * @param pack pointer to the publish packet
  235. * @param sock the socket on which the packet was received
  236. * @return completion code
  237. */
  238. int MQTTProtocol_handlePublishes(void* pack, int sock)
  239. {
  240. Publish* publish = (Publish*)pack;
  241. Clients* client = NULL;
  242. char* clientid = NULL;
  243. int rc = TCPSOCKET_COMPLETE;
  244. FUNC_ENTRY;
  245. client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
  246. clientid = client->clientID;
  247. Log(LOG_PROTOCOL, 11, NULL, sock, clientid, publish->msgId, publish->header.bits.qos,
  248. publish->header.bits.retain, min(20, publish->payloadlen), publish->payload);
  249. if (publish->header.bits.qos == 0)
  250. Protocol_processPublication(publish, client);
  251. else if (!Socket_noPendingWrites(sock))
  252. rc = SOCKET_ERROR; /* queue acks? */
  253. else if (publish->header.bits.qos == 1)
  254. {
  255. /* send puback before processing the publications because a lot of return publications could fill up the socket buffer */
  256. rc = MQTTPacket_send_puback(publish->MQTTVersion, publish->msgId, &client->net, client->clientID);
  257. /* if we get a socket error from sending the puback, should we ignore the publication? */
  258. Protocol_processPublication(publish, client);
  259. }
  260. else if (publish->header.bits.qos == 2)
  261. {
  262. /* store publication in inbound list */
  263. int len;
  264. int already_received = 0;
  265. ListElement* listElem = NULL;
  266. Messages* m = malloc(sizeof(Messages));
  267. Publications* p = MQTTProtocol_storePublication(publish, &len);
  268. m->publish = p;
  269. m->msgid = publish->msgId;
  270. m->qos = publish->header.bits.qos;
  271. m->retain = publish->header.bits.retain;
  272. m->MQTTVersion = publish->MQTTVersion;
  273. if (m->MQTTVersion >= MQTTVERSION_5)
  274. m->properties = MQTTProperties_copy(&publish->properties);
  275. m->nextMessageType = PUBREL;
  276. if ((listElem = ListFindItem(client->inboundMsgs, &(m->msgid), messageIDCompare)) != NULL)
  277. { /* discard queued publication with same msgID that the current incoming message */
  278. Messages* msg = (Messages*)(listElem->content);
  279. MQTTProtocol_removePublication(msg->publish);
  280. if (msg->MQTTVersion >= MQTTVERSION_5)
  281. MQTTProperties_free(&msg->properties);
  282. ListInsert(client->inboundMsgs, m, sizeof(Messages) + len, listElem);
  283. ListRemove(client->inboundMsgs, msg);
  284. already_received = 1;
  285. } else
  286. ListAppend(client->inboundMsgs, m, sizeof(Messages) + len);
  287. rc = MQTTPacket_send_pubrec(publish->MQTTVersion, publish->msgId, &client->net, client->clientID);
  288. if (m->MQTTVersion >= MQTTVERSION_5 && already_received == 0)
  289. {
  290. Publish publish1;
  291. publish1.header.bits.qos = m->qos;
  292. publish1.header.bits.retain = m->retain;
  293. publish1.msgId = m->msgid;
  294. publish1.topic = m->publish->topic;
  295. publish1.topiclen = m->publish->topiclen;
  296. publish1.payload = m->publish->payload;
  297. publish1.payloadlen = m->publish->payloadlen;
  298. publish1.MQTTVersion = m->MQTTVersion;
  299. publish1.properties = m->properties;
  300. Protocol_processPublication(&publish1, client);
  301. ListRemove(&(state.publications), m->publish);
  302. m->publish = NULL;
  303. }
  304. publish->topic = NULL;
  305. }
  306. MQTTPacket_freePublish(publish);
  307. FUNC_EXIT_RC(rc);
  308. return rc;
  309. }
  310. /**
  311. * Process an incoming puback packet for a socket
  312. * @param pack pointer to the publish packet
  313. * @param sock the socket on which the packet was received
  314. * @return completion code
  315. */
  316. int MQTTProtocol_handlePubacks(void* pack, int sock)
  317. {
  318. Puback* puback = (Puback*)pack;
  319. Clients* client = NULL;
  320. int rc = TCPSOCKET_COMPLETE;
  321. FUNC_ENTRY;
  322. client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
  323. Log(LOG_PROTOCOL, 14, NULL, sock, client->clientID, puback->msgId);
  324. /* look for the message by message id in the records of outbound messages for this client */
  325. if (ListFindItem(client->outboundMsgs, &(puback->msgId), messageIDCompare) == NULL)
  326. Log(TRACE_MIN, 3, NULL, "PUBACK", client->clientID, puback->msgId);
  327. else
  328. {
  329. Messages* m = (Messages*)(client->outboundMsgs->current->content);
  330. if (m->qos != 1)
  331. Log(TRACE_MIN, 4, NULL, "PUBACK", client->clientID, puback->msgId, m->qos);
  332. else
  333. {
  334. Log(TRACE_MIN, 6, NULL, "PUBACK", client->clientID, puback->msgId);
  335. #if !defined(NO_PERSISTENCE)
  336. rc = MQTTPersistence_remove(client,
  337. (m->MQTTVersion >= MQTTVERSION_5) ? PERSISTENCE_V5_PUBLISH_SENT : PERSISTENCE_PUBLISH_SENT,
  338. m->qos, puback->msgId);
  339. #endif
  340. MQTTProtocol_removePublication(m->publish);
  341. if (m->MQTTVersion >= MQTTVERSION_5)
  342. MQTTProperties_free(&m->properties);
  343. ListRemove(client->outboundMsgs, m);
  344. }
  345. }
  346. if (puback->MQTTVersion >= MQTTVERSION_5)
  347. MQTTProperties_free(&puback->properties);
  348. free(pack);
  349. FUNC_EXIT_RC(rc);
  350. return rc;
  351. }
  352. /**
  353. * Process an incoming pubrec packet for a socket
  354. * @param pack pointer to the publish packet
  355. * @param sock the socket on which the packet was received
  356. * @return completion code
  357. */
  358. int MQTTProtocol_handlePubrecs(void* pack, int sock)
  359. {
  360. Pubrec* pubrec = (Pubrec*)pack;
  361. Clients* client = NULL;
  362. int rc = TCPSOCKET_COMPLETE;
  363. FUNC_ENTRY;
  364. client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
  365. Log(LOG_PROTOCOL, 15, NULL, sock, client->clientID, pubrec->msgId);
  366. /* look for the message by message id in the records of outbound messages for this client */
  367. client->outboundMsgs->current = NULL;
  368. if (ListFindItem(client->outboundMsgs, &(pubrec->msgId), messageIDCompare) == NULL)
  369. {
  370. if (pubrec->header.bits.dup == 0)
  371. Log(TRACE_MIN, 3, NULL, "PUBREC", client->clientID, pubrec->msgId);
  372. }
  373. else
  374. {
  375. Messages* m = (Messages*)(client->outboundMsgs->current->content);
  376. if (m->qos != 2)
  377. {
  378. if (pubrec->header.bits.dup == 0)
  379. Log(TRACE_MIN, 4, NULL, "PUBREC", client->clientID, pubrec->msgId, m->qos);
  380. }
  381. else if (m->nextMessageType != PUBREC)
  382. {
  383. if (pubrec->header.bits.dup == 0)
  384. Log(TRACE_MIN, 5, NULL, "PUBREC", client->clientID, pubrec->msgId);
  385. }
  386. else
  387. {
  388. if (pubrec->MQTTVersion >= MQTTVERSION_5 && pubrec->rc >= MQTTREASONCODE_UNSPECIFIED_ERROR)
  389. {
  390. Log(TRACE_MIN, -1, "Pubrec error %d received for client %s msgid %d, not sending PUBREL",
  391. pubrec->rc, client->clientID, pubrec->msgId);
  392. #if !defined(NO_PERSISTENCE)
  393. rc = MQTTPersistence_remove(client,
  394. (pubrec->MQTTVersion >= MQTTVERSION_5) ? PERSISTENCE_V5_PUBLISH_SENT : PERSISTENCE_PUBLISH_SENT,
  395. m->qos, pubrec->msgId);
  396. #endif
  397. MQTTProtocol_removePublication(m->publish);
  398. if (m->MQTTVersion >= MQTTVERSION_5)
  399. MQTTProperties_free(&m->properties);
  400. ListRemove(client->outboundMsgs, m);
  401. (++state.msgs_sent);
  402. }
  403. else
  404. {
  405. rc = MQTTPacket_send_pubrel(pubrec->MQTTVersion, pubrec->msgId, 0, &client->net, client->clientID);
  406. m->nextMessageType = PUBCOMP;
  407. time(&(m->lastTouch));
  408. }
  409. }
  410. }
  411. if (pubrec->MQTTVersion >= MQTTVERSION_5)
  412. MQTTProperties_free(&pubrec->properties);
  413. free(pack);
  414. FUNC_EXIT_RC(rc);
  415. return rc;
  416. }
  417. /**
  418. * Process an incoming pubrel packet for a socket
  419. * @param pack pointer to the publish packet
  420. * @param sock the socket on which the packet was received
  421. * @return completion code
  422. */
  423. int MQTTProtocol_handlePubrels(void* pack, int sock)
  424. {
  425. Pubrel* pubrel = (Pubrel*)pack;
  426. Clients* client = NULL;
  427. int rc = TCPSOCKET_COMPLETE;
  428. FUNC_ENTRY;
  429. client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
  430. Log(LOG_PROTOCOL, 17, NULL, sock, client->clientID, pubrel->msgId);
  431. /* look for the message by message id in the records of inbound messages for this client */
  432. if (ListFindItem(client->inboundMsgs, &(pubrel->msgId), messageIDCompare) == NULL)
  433. {
  434. if (pubrel->header.bits.dup == 0)
  435. Log(TRACE_MIN, 3, NULL, "PUBREL", client->clientID, pubrel->msgId);
  436. else if (!Socket_noPendingWrites(sock))
  437. rc = SOCKET_ERROR; /* queue acks? */
  438. else
  439. /* Apparently this is "normal" behaviour, so we don't need to issue a warning */
  440. rc = MQTTPacket_send_pubcomp(pubrel->MQTTVersion, pubrel->msgId, &client->net, client->clientID);
  441. }
  442. else
  443. {
  444. Messages* m = (Messages*)(client->inboundMsgs->current->content);
  445. if (m->qos != 2)
  446. Log(TRACE_MIN, 4, NULL, "PUBREL", client->clientID, pubrel->msgId, m->qos);
  447. else if (m->nextMessageType != PUBREL)
  448. Log(TRACE_MIN, 5, NULL, "PUBREL", client->clientID, pubrel->msgId);
  449. else if (!Socket_noPendingWrites(sock))
  450. rc = SOCKET_ERROR; /* queue acks? */
  451. else
  452. {
  453. Publish publish;
  454. memset(&publish, '\0', sizeof(publish));
  455. /* send pubcomp before processing the publications because a lot of return publications could fill up the socket buffer */
  456. rc = MQTTPacket_send_pubcomp(pubrel->MQTTVersion, pubrel->msgId, &client->net, client->clientID);
  457. publish.header.bits.qos = m->qos;
  458. publish.header.bits.retain = m->retain;
  459. publish.msgId = m->msgid;
  460. if (m->publish)
  461. {
  462. publish.topic = m->publish->topic;
  463. publish.topiclen = m->publish->topiclen;
  464. publish.payload = m->publish->payload;
  465. publish.payloadlen = m->publish->payloadlen;
  466. }
  467. publish.MQTTVersion = m->MQTTVersion;
  468. if (publish.MQTTVersion >= MQTTVERSION_5)
  469. publish.properties = m->properties;
  470. else
  471. Protocol_processPublication(&publish, client); /* only for 3.1.1 and lower */
  472. #if !defined(NO_PERSISTENCE)
  473. rc += MQTTPersistence_remove(client,
  474. (m->MQTTVersion >= MQTTVERSION_5) ? PERSISTENCE_V5_PUBLISH_RECEIVED : PERSISTENCE_PUBLISH_RECEIVED,
  475. m->qos, pubrel->msgId);
  476. #endif
  477. if (m->MQTTVersion >= MQTTVERSION_5)
  478. MQTTProperties_free(&m->properties);
  479. if (m->publish)
  480. ListRemove(&(state.publications), m->publish);
  481. ListRemove(client->inboundMsgs, m);
  482. ++(state.msgs_received);
  483. }
  484. }
  485. if (pubrel->MQTTVersion >= MQTTVERSION_5)
  486. MQTTProperties_free(&pubrel->properties);
  487. free(pack);
  488. FUNC_EXIT_RC(rc);
  489. return rc;
  490. }
  491. /**
  492. * Process an incoming pubcomp packet for a socket
  493. * @param pack pointer to the publish packet
  494. * @param sock the socket on which the packet was received
  495. * @return completion code
  496. */
  497. int MQTTProtocol_handlePubcomps(void* pack, int sock)
  498. {
  499. Pubcomp* pubcomp = (Pubcomp*)pack;
  500. Clients* client = NULL;
  501. int rc = TCPSOCKET_COMPLETE;
  502. FUNC_ENTRY;
  503. client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
  504. Log(LOG_PROTOCOL, 19, NULL, sock, client->clientID, pubcomp->msgId);
  505. /* look for the message by message id in the records of outbound messages for this client */
  506. if (ListFindItem(client->outboundMsgs, &(pubcomp->msgId), messageIDCompare) == NULL)
  507. {
  508. if (pubcomp->header.bits.dup == 0)
  509. Log(TRACE_MIN, 3, NULL, "PUBCOMP", client->clientID, pubcomp->msgId);
  510. }
  511. else
  512. {
  513. Messages* m = (Messages*)(client->outboundMsgs->current->content);
  514. if (m->qos != 2)
  515. Log(TRACE_MIN, 4, NULL, "PUBCOMP", client->clientID, pubcomp->msgId, m->qos);
  516. else
  517. {
  518. if (m->nextMessageType != PUBCOMP)
  519. Log(TRACE_MIN, 5, NULL, "PUBCOMP", client->clientID, pubcomp->msgId);
  520. else
  521. {
  522. Log(TRACE_MIN, 6, NULL, "PUBCOMP", client->clientID, pubcomp->msgId);
  523. #if !defined(NO_PERSISTENCE)
  524. rc = MQTTPersistence_remove(client,
  525. (m->MQTTVersion >= MQTTVERSION_5) ? PERSISTENCE_V5_PUBLISH_SENT : PERSISTENCE_PUBLISH_SENT,
  526. m->qos, pubcomp->msgId);
  527. if (rc != 0)
  528. Log(LOG_ERROR, -1, "Error removing PUBCOMP for client id %s msgid %d from persistence", client->clientID, pubcomp->msgId);
  529. #endif
  530. MQTTProtocol_removePublication(m->publish);
  531. if (m->MQTTVersion >= MQTTVERSION_5)
  532. MQTTProperties_free(&m->properties);
  533. ListRemove(client->outboundMsgs, m);
  534. (++state.msgs_sent);
  535. }
  536. }
  537. }
  538. if (pubcomp->MQTTVersion >= MQTTVERSION_5)
  539. MQTTProperties_free(&pubcomp->properties);
  540. free(pack);
  541. FUNC_EXIT_RC(rc);
  542. return rc;
  543. }
  544. /**
  545. * MQTT protocol keepAlive processing. Sends PINGREQ packets as required.
  546. * @param now current time
  547. */
  548. void MQTTProtocol_keepalive(time_t now)
  549. {
  550. ListElement* current = NULL;
  551. FUNC_ENTRY;
  552. ListNextElement(bstate->clients, &current);
  553. while (current)
  554. {
  555. Clients* client = (Clients*)(current->content);
  556. ListNextElement(bstate->clients, &current);
  557. if (client->connected == 0 || client->keepAliveInterval == 0)
  558. continue;
  559. if (client->ping_outstanding == 1)
  560. {
  561. if (difftime(now, client->net.lastPing) >= client->keepAliveInterval)
  562. {
  563. Log(TRACE_PROTOCOL, -1, "PINGRESP not received in keepalive interval for client %s on socket %d, disconnecting", client->clientID, client->net.socket);
  564. MQTTProtocol_closeSession(client, 1);
  565. }
  566. }
  567. else if (difftime(now, client->net.lastSent) >= client->keepAliveInterval ||
  568. difftime(now, client->net.lastReceived) >= client->keepAliveInterval)
  569. {
  570. if (Socket_noPendingWrites(client->net.socket))
  571. {
  572. if (MQTTPacket_send_pingreq(&client->net, client->clientID) != TCPSOCKET_COMPLETE)
  573. {
  574. Log(TRACE_PROTOCOL, -1, "Error sending PINGREQ for client %s on socket %d, disconnecting", client->clientID, client->net.socket);
  575. MQTTProtocol_closeSession(client, 1);
  576. }
  577. else
  578. {
  579. client->net.lastPing = now;
  580. client->ping_outstanding = 1;
  581. }
  582. }
  583. }
  584. }
  585. FUNC_EXIT;
  586. }
  587. /**
  588. * MQTT retry processing per client
  589. * @param now current time
  590. * @param client - the client to which to apply the retry processing
  591. * @param regardless boolean - retry packets regardless of retry interval (used on reconnect)
  592. */
  593. static void MQTTProtocol_retries(time_t now, Clients* client, int regardless)
  594. {
  595. ListElement* outcurrent = NULL;
  596. FUNC_ENTRY;
  597. if (!regardless && client->retryInterval <= 0) /* 0 or -ive retryInterval turns off retry except on reconnect */
  598. goto exit;
  599. while (client && ListNextElement(client->outboundMsgs, &outcurrent) &&
  600. client->connected && client->good && /* client is connected and has no errors */
  601. Socket_noPendingWrites(client->net.socket)) /* there aren't any previous packets still stacked up on the socket */
  602. {
  603. Messages* m = (Messages*)(outcurrent->content);
  604. if (regardless || difftime(now, m->lastTouch) > max(client->retryInterval, 10))
  605. {
  606. if (m->qos == 1 || (m->qos == 2 && m->nextMessageType == PUBREC))
  607. {
  608. Publish publish;
  609. int rc;
  610. Log(TRACE_MIN, 7, NULL, "PUBLISH", client->clientID, client->net.socket, m->msgid);
  611. publish.msgId = m->msgid;
  612. publish.topic = m->publish->topic;
  613. publish.payload = m->publish->payload;
  614. publish.payloadlen = m->publish->payloadlen;
  615. publish.properties = m->properties;
  616. publish.MQTTVersion = m->MQTTVersion;
  617. rc = MQTTPacket_send_publish(&publish, 1, m->qos, m->retain, &client->net, client->clientID);
  618. if (rc == SOCKET_ERROR)
  619. {
  620. client->good = 0;
  621. Log(TRACE_PROTOCOL, 29, NULL, client->clientID, client->net.socket,
  622. Socket_getpeer(client->net.socket));
  623. MQTTProtocol_closeSession(client, 1);
  624. client = NULL;
  625. }
  626. else
  627. {
  628. if (m->qos == 0 && rc == TCPSOCKET_INTERRUPTED)
  629. MQTTProtocol_storeQoS0(client, &publish);
  630. time(&(m->lastTouch));
  631. }
  632. }
  633. else if (m->qos && m->nextMessageType == PUBCOMP)
  634. {
  635. Log(TRACE_MIN, 7, NULL, "PUBREL", client->clientID, client->net.socket, m->msgid);
  636. if (MQTTPacket_send_pubrel(m->MQTTVersion, m->msgid, 0, &client->net, client->clientID) != TCPSOCKET_COMPLETE)
  637. {
  638. client->good = 0;
  639. Log(TRACE_PROTOCOL, 29, NULL, client->clientID, client->net.socket,
  640. Socket_getpeer(client->net.socket));
  641. MQTTProtocol_closeSession(client, 1);
  642. client = NULL;
  643. }
  644. else
  645. time(&(m->lastTouch));
  646. }
  647. /* break; why not do all retries at once? */
  648. }
  649. }
  650. exit:
  651. FUNC_EXIT;
  652. }
  653. /**
  654. * MQTT retry protocol and socket pending writes processing.
  655. * @param now current time
  656. * @param doRetry boolean - retries as well as pending writes?
  657. * @param regardless boolean - retry packets regardless of retry interval (used on reconnect)
  658. */
  659. void MQTTProtocol_retry(time_t now, int doRetry, int regardless)
  660. {
  661. ListElement* current = NULL;
  662. FUNC_ENTRY;
  663. ListNextElement(bstate->clients, &current);
  664. /* look through the outbound message list of each client, checking to see if a retry is necessary */
  665. while (current)
  666. {
  667. Clients* client = (Clients*)(current->content);
  668. ListNextElement(bstate->clients, &current);
  669. if (client->connected == 0)
  670. continue;
  671. if (client->good == 0)
  672. {
  673. MQTTProtocol_closeSession(client, 1);
  674. continue;
  675. }
  676. if (Socket_noPendingWrites(client->net.socket) == 0)
  677. continue;
  678. if (doRetry)
  679. MQTTProtocol_retries(now, client, regardless);
  680. }
  681. FUNC_EXIT;
  682. }
  683. /**
  684. * Free a client structure
  685. * @param client the client data to free
  686. */
  687. void MQTTProtocol_freeClient(Clients* client)
  688. {
  689. FUNC_ENTRY;
  690. /* free up pending message lists here, and any other allocated data */
  691. MQTTProtocol_freeMessageList(client->outboundMsgs);
  692. MQTTProtocol_freeMessageList(client->inboundMsgs);
  693. ListFree(client->messageQueue);
  694. free(client->clientID);
  695. client->clientID = NULL;
  696. if (client->will)
  697. {
  698. free(client->will->payload);
  699. free(client->will->topic);
  700. free(client->will);
  701. client->will = NULL;
  702. }
  703. if (client->username)
  704. free((void*)client->username);
  705. if (client->password)
  706. free((void*)client->password);
  707. #if defined(OPENSSL)
  708. if (client->sslopts)
  709. {
  710. if (client->sslopts->trustStore)
  711. free((void*)client->sslopts->trustStore);
  712. if (client->sslopts->keyStore)
  713. free((void*)client->sslopts->keyStore);
  714. if (client->sslopts->privateKey)
  715. free((void*)client->sslopts->privateKey);
  716. if (client->sslopts->privateKeyPassword)
  717. free((void*)client->sslopts->privateKeyPassword);
  718. if (client->sslopts->enabledCipherSuites)
  719. free((void*)client->sslopts->enabledCipherSuites);
  720. if (client->sslopts->struct_version >= 2)
  721. {
  722. if (client->sslopts->CApath)
  723. free((void*)client->sslopts->CApath);
  724. }
  725. free(client->sslopts);
  726. client->sslopts = NULL;
  727. }
  728. #endif
  729. /* don't free the client structure itself... this is done elsewhere */
  730. FUNC_EXIT;
  731. }
  732. /**
  733. * Empty a message list, leaving it able to accept new messages
  734. * @param msgList the message list to empty
  735. */
  736. void MQTTProtocol_emptyMessageList(List* msgList)
  737. {
  738. ListElement* current = NULL;
  739. FUNC_ENTRY;
  740. while (ListNextElement(msgList, &current))
  741. {
  742. Messages* m = (Messages*)(current->content);
  743. MQTTProtocol_removePublication(m->publish);
  744. if (m->MQTTVersion >= MQTTVERSION_5)
  745. MQTTProperties_free(&m->properties);
  746. }
  747. ListEmpty(msgList);
  748. FUNC_EXIT;
  749. }
  750. /**
  751. * Empty and free up all storage used by a message list
  752. * @param msgList the message list to empty and free
  753. */
  754. void MQTTProtocol_freeMessageList(List* msgList)
  755. {
  756. FUNC_ENTRY;
  757. MQTTProtocol_emptyMessageList(msgList);
  758. ListFree(msgList);
  759. FUNC_EXIT;
  760. }
  761. /**
  762. * Copy no more than dest_size -1 characters from the string pointed to by src to the array pointed to by dest.
  763. * The destination string will always be null-terminated.
  764. * @param dest the array which characters copy to
  765. * @param src the source string which characters copy from
  766. * @param dest_size the size of the memory pointed to by dest: copy no more than this -1 (allow for null). Must be >= 1
  767. * @return the destination string pointer
  768. */
  769. char* MQTTStrncpy(char *dest, const char *src, size_t dest_size)
  770. {
  771. size_t count = dest_size;
  772. char *temp = dest;
  773. FUNC_ENTRY;
  774. if (dest_size < strlen(src))
  775. Log(TRACE_MIN, -1, "the src string is truncated");
  776. /* We must copy only the first (dest_size - 1) bytes */
  777. while (count > 1 && (*temp++ = *src++))
  778. count--;
  779. *temp = '\0';
  780. FUNC_EXIT;
  781. return dest;
  782. }
  783. /**
  784. * Duplicate a string, safely, allocating space on the heap
  785. * @param src the source string which characters copy from
  786. * @return the duplicated, allocated string
  787. */
  788. char* MQTTStrdup(const char* src)
  789. {
  790. size_t mlen = strlen(src) + 1;
  791. char* temp = malloc(mlen);
  792. MQTTStrncpy(temp, src, mlen);
  793. return temp;
  794. }