/** * Utility function to start a new publish exchange. * @param pubclient the client to send the publication to * @param publish the publication data * @param qos the MQTT QoS to use * @param retained boolean - whether to set the MQTT retained flag * @return the completion code */ int MQTTProtocol_startPublishCommon(Clients* pubclient, Publish* publish, int qos, int retained) { int rc = TCPSOCKET_COMPLETE; FUNC_ENTRY; if (qos == 0 && (strlen(publish->topic) < 5 || strncmp(publish->topic, sysprefix, strlen(sysprefix)) != 0)) { ++(bstate->msgs_sent); bstate->bytes_sent += publish->payloadlen; } #if defined(MQTTS) if (pubclient->protocol == PROTOCOL_MQTTS) { rc = MQTTSProtocol_startPublishCommon(pubclient,publish,0,qos,retained); } else if (pubclient->protocol == PROTOCOL_MQTTS_DTLS) { rc = MQTTSProtocol_startPublishCommon(pubclient,publish,0,qos,retained); } else { #endif rc = MQTTPacket_send_publish(publish, 0, qos, retained, pubclient->socket, pubclient->clientID); if (qos == 0 && rc == TCPSOCKET_INTERRUPTED) MQTTProtocol_storeQoS0(pubclient, publish); #if defined(MQTTS) } #endif FUNC_EXIT_RC(rc); return rc; }
/** * Kick off a new publication, queue it or send it as appropriate. * @param pubclient the client to send the publication to * @param publish the publication data * @param qos the MQTT QoS to use * @param retained boolean - whether to set the MQTT retained flag * @param mm - pointer to the message to send * @return the completion code */ int Protocol_startOrQueuePublish(Clients* pubclient, Publish* publish, int qos, int retained, int priority, Messages** mm) { int rc = TCPSOCKET_COMPLETE; FUNC_ENTRY; if (pubclient->connected && pubclient->good && /* client is connected and has no errors */ Socket_noPendingWrites(pubclient->socket) && /* there aren't any previous packets still stacked up on the socket */ queuedMsgsCount(pubclient) == 0 && /* there are no messages ahead in the queue */ pubclient->outboundMsgs->count < bstate->max_inflight_messages) /* the queue is not full */ { #if defined(MQTTS) if (pubclient->protocol == PROTOCOL_MQTTS_MULTICAST) { MQTTSProtocol_startPublishCommon(pubclient, publish, 0, 3, retained); } else if (pubclient->protocol == PROTOCOL_MQTTS && strlen(publish->topic) > 2 && MQTTSProtocol_getRegisteredTopicId(pubclient, publish->topic) == 0) { if (pubclient->pendingRegistration == NULL) rc = MQTTSProtocol_startRegistration(pubclient, publish->topic); rc = MQTTProtocol_queuePublish(pubclient, publish, qos, retained, priority, mm); } else if (pubclient->protocol == PROTOCOL_MQTTS && qos > 0 && pubclient->outboundMsgs->count > 0) { /* can only have 1 qos 1/2 message in flight with MQTT-S */ rc = MQTTProtocol_queuePublish(pubclient, publish, qos, retained, priority, mm); } else { #endif rc = MQTTProtocol_startPublish(pubclient, publish, qos, retained, mm); /* We don't normally queue QoS 0 messages just send them straight through. But in the case when none of the packet * is written we need to queue it up. If only part of the packet is written, then it is buffered by the socket module. */ if (qos == 0 && rc == TCPSOCKET_NOWORK) rc = MQTTProtocol_queuePublish(pubclient, publish, qos, retained, priority, mm); #if defined(MQTTS) } #endif } else if (qos != 0 || (pubclient->connected && pubclient->good)) /* only queue qos 0 if the client is connected in a good way */ rc = MQTTProtocol_queuePublish(pubclient, publish, qos, retained, priority, mm); FUNC_EXIT_RC(rc); return rc; }
/** * MQTT retry processing per client * @param now current time * @param client - the client to which to apply the retry processing */ void MQTTProtocol_retries(time_t now, Clients* client) { ListElement* outcurrent = NULL; FUNC_ENTRY; #if defined(MQTTS) if (client->protocol == PROTOCOL_MQTTS) { if (client->pendingRegistration != NULL && difftime(now, client->pendingRegistration->sent) > bstate->retry_interval) { Registration* reg = client->pendingRegistration->registration; time(&client->pendingRegistration->sent); /* NB: no dup bit for these packets */ if (MQTTSPacket_send_register(client, reg->id, reg->topicName, client->pendingRegistration->msgId) == SOCKET_ERROR) { client->good = 0; //TODO: update message Log(LOG_WARNING, 29, NULL, client->clientID, client->socket); MQTTProtocol_closeSession(client, 1); client = NULL; } } #if !defined(NO_BRIDGE) if (client->pendingSubscription != NULL && difftime(now, client->pendingSubscription->sent) > bstate->retry_interval) { time(&client->pendingSubscription->sent); if (MQTTSPacket_send_subscribe(client, client->pendingSubscription->topicName,client->pendingSubscription->qos, client->pendingSubscription->msgId) == SOCKET_ERROR) { client->good = 0; //TODO: update message Log(LOG_WARNING, 29, NULL, client->clientID, client->socket); MQTTProtocol_closeSession(client, 1); client = NULL; } } #endif } #endif while (client && ListNextElement(client->outboundMsgs, &outcurrent) && client->connected && client->good && /* client is connected and has no errors */ Socket_noPendingWrites(client->socket)) /* there aren't any previous packets still stacked up on the socket */ { Messages* m = (Messages*)(outcurrent->content); if (difftime(now, m->lastTouch) > bstate->retry_interval) { if (m->qos == 1 || (m->qos == 2 && m->nextMessageType == PUBREC)) { Publish publish; int rc; Log(LOG_INFO, 28, NULL, client->clientID, client->socket, m->msgid); publish.msgId = m->msgid; publish.topic = m->publish->topic; publish.payload = m->publish->payload; publish.payloadlen = m->publish->payloadlen; #if defined(MQTTS) if (client->protocol == PROTOCOL_MQTTS) { if (MQTTSProtocol_startPublishCommon(client, &publish, 1, m->qos, m->retain) == SOCKET_ERROR) { client->good = 0; Log(LOG_WARNING, 29, NULL, client->clientID, client->socket); MQTTProtocol_closeSession(client, 1); client = NULL; } else time(&(m->lastTouch)); } else { #endif rc = MQTTPacket_send_publish(&publish, 1, m->qos, m->retain, client->socket, client->clientID); if (rc == SOCKET_ERROR) { client->good = 0; Log(LOG_WARNING, 29, NULL, client->clientID, client->socket); MQTTProtocol_closeSession(client, 1); client = NULL; } else { if (m->qos == 0 && rc == TCPSOCKET_INTERRUPTED) MQTTProtocol_storeQoS0(client, &publish); time(&(m->lastTouch)); } #if defined(MQTTS) } #endif } else if (m->qos && m->nextMessageType == PUBCOMP) { Log(LOG_WARNING, 30, NULL, client->clientID, m->msgid); #if defined(MQTTS) if (client->protocol == PROTOCOL_MQTTS) { /* NB: no dup bit for PUBREL */ if (MQTTSPacket_send_pubrel(client, m->msgid) == SOCKET_ERROR) { client->good = 0; Log(LOG_WARNING, 18, NULL, client->clientID, client->socket, Socket_getpeer(client->socket)); MQTTProtocol_closeSession(client, 1); client = NULL; } else time(&(m->lastTouch)); } else #endif if (MQTTPacket_send_pubrel(m->msgid, 1, client->socket, client->clientID) != TCPSOCKET_COMPLETE) { client->good = 0; Log(LOG_WARNING, 18, NULL, client->clientID, client->socket, Socket_getpeer(client->socket)); MQTTProtocol_closeSession(client, 1); client = NULL; } else time(&(m->lastTouch)); } /* break; why not do all retries at once? */ } } FUNC_EXIT; }
/** * Close any active session for a client and clean up. * @param client the client to clean up * @param send_will flag to indicate whether a will messsage should be sent if it has been set */ void MQTTProtocol_closeSession(Clients* client, int send_will) { FUNC_ENTRY; client->good = 0; if (in_MQTTPacket_Factory == client->socket || client->closing) goto exit; client->closing = 1; if (client->socket > 0) { if (client->connected) { if (client->outbound && client->will) { Publish pub; pub.payload = "0"; pub.payloadlen = 1; pub.topic = client->will->topic; #if defined(MQTTS) if (client->protocol == PROTOCOL_MQTTS) MQTTSProtocol_startPublishCommon(client, &pub, 0,0,1); if (client->protocol == PROTOCOL_MQTTS_DTLS) MQTTSProtocol_startPublishCommon(client, &pub, 0,0,1); //TODO LW else #endif MQTTPacket_send_publish(&pub, 0, 0, 1, client->socket, client->clientID); MQTTProtocol_sys_publish(client->will->topic, "0"); } #if defined(MQTTS) if (client->protocol == PROTOCOL_MQTTS_MULTICAST) ; else if (client->protocol == PROTOCOL_MQTTS) MQTTSPacket_send_disconnect(client, 0); else if (client->protocol == PROTOCOL_MQTTS_DTLS) MQTTSPacket_send_disconnect(client, 0); else #endif if (client->outbound) MQTTPacket_send_disconnect(client->socket, client->clientID); } if (ListFindItem(&(state.pending_writes), &(client->socket), intcompare)) { pending_write* pw = (pending_write*)(state.pending_writes.current->content); MQTTProtocol_removePublication(pw->p); ListRemove(&(state.pending_writes), pw); } #if defined(MQTTS) if (client->protocol == PROTOCOL_MQTT || client->outbound == 1) { if (client->protocol == PROTOCOL_MQTTS_MULTICAST) Socket_close_only(client->socket); else #endif Socket_close(client->socket); #if defined(MQTTS) } #endif } if (client->connected || client->connect_state) { client->connected = 0; client->connect_state = 0; } if (client->outbound == 0 && client->will != NULL && send_will) { Publish publish; publish.payload = client->will->msg; publish.payloadlen = strlen(client->will->msg); publish.topic = client->will->topic; publish.header.bits.qos = client->will->qos; publish.header.bits.retain = client->will->retained; Protocol_processPublication(&publish, client->clientID); } #if defined(MQTTS) if (client->protocol == PROTOCOL_MQTTS) MQTTSProtocol_emptyRegistrationList(client->registrations); else if (client->protocol == PROTOCOL_MQTTS_DTLS) MQTTSProtocol_emptyRegistrationList(client->registrations); #endif if (client->cleansession) { if (client->outbound && ((BridgeConnections*)(client->bridge_context))->state != CONNECTION_DELETE) { /* bridge outbound client structures are reused on reconnection */ int i; MQTTProtocol_removeAllSubscriptions(client->clientID); MQTTProtocol_emptyMessageList(client->inboundMsgs); MQTTProtocol_emptyMessageList(client->outboundMsgs); for (i = 0; i < PRIORITY_MAX; ++i) MQTTProtocol_emptyMessageList(client->queuedMsgs[i]); client->msgID = 0; } else { #if defined(MQTTS) if ((client->protocol == PROTOCOL_MQTTS && client->outbound == 0) || (client->protocol == PROTOCOL_MQTTS_DTLS && client->outbound == 0) || client->protocol == PROTOCOL_MQTTS_MULTICAST) { if (!TreeRemove(bstate->mqtts_clients, client) && !TreeRemove(bstate->disconnected_mqtts_clients, client)) Log(LOG_ERROR, 39, NULL); else Log(TRACE_MAX, 2, NULL, client->clientID, client->socket); } else { #endif if (!TreeRemove(bstate->clients, client) && !TreeRemove(bstate->disconnected_clients, client)) Log(LOG_ERROR, 39, NULL); else Log(TRACE_MAX, 2, NULL, client->clientID, client->socket); #if defined(MQTTS) } #endif MQTTProtocol_freeClient(client); } } else { int i; for (i = 0; i < PRIORITY_MAX; ++i) MQTTProtocol_removeQoS0Messages(client->queuedMsgs[i]); #if defined(MQTTS) if ((client->protocol == PROTOCOL_MQTTS && client->outbound == 0)||(client->protocol == PROTOCOL_MQTTS_DTLS && client->outbound == 0)) { if (TreeRemove(bstate->mqtts_clients, client)) { client->socket = 0; TreeAdd(bstate->disconnected_mqtts_clients, client, sizeof(Clients) + strlen(client->clientID)+1 + 3*sizeof(List)); } } else { #endif if (TreeRemove(bstate->clients, client)) { client->socket = 0; TreeAdd(bstate->disconnected_clients, client, sizeof(Clients) + strlen(client->clientID)+1 + 3*sizeof(List)); } #if defined(MQTTS) } #endif client->closing = 0; } exit: FUNC_EXIT; }