/** * MQTT retry protocol and socket pending writes processing. * @param now current time * @param doRetry boolean - retries as well as pending writes? */ void MQTTProtocol_retry(time_t now, int doRetry) { ListElement* current = NULL; FUNC_ENTRY; ListNextElement(bstate->clients, ¤t); /* look through the outbound message list of each client, checking to see if a retry is necessary */ while (current) { Clients* client = (Clients*)(current->content); ListNextElement(bstate->clients, ¤t); if (client->connected == 0) continue; if (client->good == 0) { MQTTProtocol_closeSession(client, 1); continue; } if (Socket_noPendingWrites(client->net.socket) == 0) continue; if (doRetry) MQTTProtocol_retries(now, client); } FUNC_EXIT; }
int MQTTClient_disconnect1(MQTTClient handle, int timeout, int internal) { MQTTClients* m = handle; START_TIME_TYPE start; int rc = MQTTCLIENT_SUCCESS; int was_connected = 0; FUNC_ENTRY; Thread_lock_mutex(mqttclient_mutex); if (m == NULL || m->c == NULL) { rc = MQTTCLIENT_FAILURE; goto exit; } if (m->c->connected == 0 && m->c->connect_state == 0) { rc = MQTTCLIENT_DISCONNECTED; goto exit; } was_connected = m->c->connected; /* should be 1 */ if (m->c->connected != 0) { start = MQTTClient_start_clock(); m->c->connect_state = -2; /* indicate disconnecting */ while (m->c->inboundMsgs->count > 0 || m->c->outboundMsgs->count > 0) { /* wait for all inflight message flows to finish, up to timeout */ if (MQTTClient_elapsed(start) >= timeout) break; Thread_unlock_mutex(mqttclient_mutex); MQTTClient_yield(); Thread_lock_mutex(mqttclient_mutex); } } MQTTProtocol_closeSession(m->c, 0); if (Thread_check_sem(m->connect_sem)) Thread_post_sem(m->connect_sem); if (Thread_check_sem(m->connack_sem)) Thread_post_sem(m->connack_sem); if (Thread_check_sem(m->suback_sem)) Thread_post_sem(m->suback_sem); if (Thread_check_sem(m->unsuback_sem)) Thread_post_sem(m->unsuback_sem); exit: MQTTClient_stop(); if (internal && m->cl && was_connected) { Log(TRACE_MIN, -1, "Calling connectionLost for client %s", m->c->clientID); Thread_start(connectionLost_call, m); } Thread_unlock_mutex(mqttclient_mutex); FUNC_EXIT_RC(rc); return rc; }
/** * Process an incoming disconnect packet for a socket * @param pack pointer to the disconnect packet * @param sock the socket on which the packet was received * @return completion code */ int MQTTProtocol_handleDisconnects(void* pack, int sock, Clients* client) { FUNC_ENTRY; client->good = 0; /* don't try and send log message to this client if it is subscribed to $SYS/broker/log */ Log(LOG_PROTOCOL, 5, NULL, sock, client->clientID); if (bstate->connection_messages) Log(LOG_INFO, 38, NULL, client->clientID); MQTTProtocol_closeSession(client, 0); FUNC_EXIT; return TCPSOCKET_COMPLETE; }
/** * MQTT protocol keepAlive processing. Sends PINGREQ packets as required. * @param now current time */ void MQTTProtocol_keepalive(time_t now) { ListElement* current = NULL; FUNC_ENTRY; ListNextElement(bstate->clients, ¤t); while (current) { Clients* client = (Clients*)(current->content); ListNextElement(bstate->clients, ¤t); if (client->connected && client->keepAliveInterval > 0 && (difftime(now, client->net.lastSent) >= client->keepAliveInterval || difftime(now, client->net.lastReceived) >= client->keepAliveInterval)) { if (client->ping_outstanding == 0) { if (Socket_noPendingWrites(client->net.socket)) { if (MQTTPacket_send_pingreq(&client->net, client->clientID) != TCPSOCKET_COMPLETE) { Log(TRACE_PROTOCOL, -1, "Error sending PINGREQ for client %s on socket %d, disconnecting", client->clientID, client->net.socket); MQTTProtocol_closeSession(client, 1); } else { client->net.lastSent = now; client->ping_outstanding = 1; } } } else { Log(TRACE_PROTOCOL, -1, "PINGRESP not received in keepalive interval for client %s on socket %d, disconnecting", client->clientID, client->net.socket); MQTTProtocol_closeSession(client, 1); } } } FUNC_EXIT; }
void MQTTProtocol_shutdownclients(Tree* clients, int terminate) { Node* current = NULL; FUNC_ENTRY; current = TreeNextElement(clients, current); while (current) { Clients* client = (Clients*)(current->content); current = TreeNextElement(clients, current); Log(LOG_INFO, 17, NULL, client->clientID); if (terminate) client->cleansession = 1; /* no persistence, so everything is clean */ MQTTProtocol_closeSession(client, 0); } FUNC_EXIT; }
int MQTTSProtocol_handleDisconnects(void* pack, int sock, char* clientAddr, Clients* client) { MQTTS_Disconnect* disc = (MQTTS_Disconnect*)pack; FUNC_ENTRY; Log(LOG_PROTOCOL, 79, NULL, socket, client->addr, client->clientID, disc->duration); client->good = 0; /* don't try and send log message to this client if it is subscribed to $SYS/broker/log */ Log((bstate->connection_messages) ? LOG_INFO : LOG_PROTOCOL, 38, NULL, client->clientID); // Remove all registrations if duration is zero. // Otherwise keep it. It is a sleeping client if ( disc->duration == 0 ) { MQTTSProtocol_emptyRegistrationList(client->registrations); } MQTTProtocol_closeSession(client, 0); MQTTSPacket_free_packet(pack); FUNC_EXIT; return 0; }
/** * Clean up a client list by closing any marked as "not good". * @param clients the list of clients */ void MQTTProtocol_clean_clients(Tree* clients) { Node* current = NULL; FUNC_ENTRY; current = TreeNextElement(clients, current); while (current) { Clients* client = (Clients*)(current->content); current = TreeNextElement(clients, current); if (client->good == 0) { Log(LOG_WARNING, 18, NULL, client->clientID, client->socket, Socket_getpeer(client->socket)); MQTTProtocol_closeSession(client, 1); } } FUNC_EXIT; }
/** * 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; }
/** * MQTT retry processing per client * @param now current time * @param client - the client to which to apply the retry processing * @param regardless boolean - retry packets regardless of retry interval (used on reconnect) */ static void MQTTProtocol_retries(time_t now, Clients* client, int regardless) { ListElement* outcurrent = NULL; FUNC_ENTRY; if (!regardless && client->retryInterval <= 0) /* 0 or -ive retryInterval turns off retry except on reconnect */ goto exit; while (client && ListNextElement(client->outboundMsgs, &outcurrent) && client->connected && client->good && /* client is connected and has no errors */ Socket_noPendingWrites(client->net.socket)) /* there aren't any previous packets still stacked up on the socket */ { Messages* m = (Messages*)(outcurrent->content); if (regardless || difftime(now, m->lastTouch) > max(client->retryInterval, 10)) { if (m->qos == 1 || (m->qos == 2 && m->nextMessageType == PUBREC)) { Publish publish; int rc; Log(TRACE_MIN, 7, NULL, "PUBLISH", client->clientID, client->net.socket, m->msgid); publish.msgId = m->msgid; publish.topic = m->publish->topic; publish.payload = m->publish->payload; publish.payloadlen = m->publish->payloadlen; rc = MQTTPacket_send_publish(&publish, 1, m->qos, m->retain, &client->net, client->clientID); if (rc == SOCKET_ERROR) { client->good = 0; Log(TRACE_PROTOCOL, 29, NULL, client->clientID, client->net.socket, Socket_getpeer(client->net.socket)); MQTTProtocol_closeSession(client, 1); client = NULL; } else { if (m->qos == 0 && rc == TCPSOCKET_INTERRUPTED) MQTTProtocol_storeQoS0(client, &publish); time(&(m->lastTouch)); } } else if (m->qos && m->nextMessageType == PUBCOMP) { Log(TRACE_MIN, 7, NULL, "PUBREL", client->clientID, client->net.socket, m->msgid); if (MQTTPacket_send_pubrel(m->msgid, 0, &client->net, client->clientID) != TCPSOCKET_COMPLETE) { client->good = 0; Log(TRACE_PROTOCOL, 29, NULL, client->clientID, client->net.socket, Socket_getpeer(client->net.socket)); MQTTProtocol_closeSession(client, 1); client = NULL; } else time(&(m->lastTouch)); } /* break; why not do all retries at once? */ } } exit: FUNC_EXIT; }
void MQTTSProtocol_timeslice(int sock) { int error; MQTTS_Header* pack = NULL; char *clientAddr = NULL ; Clients* client = NULL; struct sockaddr_in6 from; uint8_t *wirelessNodeId = NULL ; uint8_t wirelessNodeIdLen = 0 ; FUNC_ENTRY; pack = MQTTSPacket_Factory(sock, &clientAddr, (struct sockaddr *)&from, &wirelessNodeId , &wirelessNodeIdLen , &error); if (clientAddr) { // IF FRWDENCAP, append ":[WirelessNodeId in HEX]" to clientAddr if (wirelessNodeId && wirelessNodeIdLen > 0) { char* ptr = &clientAddr[strlen(clientAddr)] ; *(ptr++) = ':' ; *(ptr++) = '[' ; int i ; for (i = 0 ; i < wirelessNodeIdLen ; i++) { ptr += sprintf(ptr, "%02X", wirelessNodeId[i]); } *(ptr++) = ']' ; *ptr = '\0'; } client = Protocol_getclientbyaddr(clientAddr); } #if !defined(NO_BRIDGE) if (client == NULL) client = Protocol_getoutboundclient(sock); #endif if (pack == NULL) { if (error == SOCKET_ERROR || error == UDPSOCKET_INCOMPLETE) { if (client != NULL) { client->good = 0; /* make sure we don't try and send messages to ourselves */ //client->connected = 0; if (error == SOCKET_ERROR) Log(LOG_WARNING, 18, NULL, client->clientID, client->socket, Socket_getpeer(client->socket)); else Log(LOG_WARNING, 19, NULL, client->clientID, client->socket, Socket_getpeer(client->socket)); MQTTProtocol_closeSession(client, 0); } else { if (error == SOCKET_ERROR) Log(LOG_WARNING, 20, NULL, sock, Socket_getpeer(sock)); else Log(LOG_WARNING, 21, NULL, sock, Socket_getpeer(sock)); /*Socket_close(sock);*/ } } } else if (pack->header.type != MQTTS_CONNECT && handle_packets[(int)pack->header.type] == NULL) { Log(LOG_WARNING, 22, NULL, pack->header.type, handle_packets[(int)pack->header.type]); MQTTSPacket_free_packet(pack); } else if (client == NULL && pack->header.type != MQTTS_CONNECT && pack->header.type != MQTTS_ADVERTISE && pack->header.type != MQTTS_SEARCHGW && (pack->header.type != MQTTS_PUBLISH || ((MQTTS_Publish*)pack)->flags.QoS != 3)) { Log(LOG_WARNING, 23, NULL, sock, Socket_getpeer(sock), MQTTSPacket_name(pack->header.type)); MQTTSPacket_free_packet(pack); } else if ( pack->header.type == MQTTS_CONNECT ) { MQTTSProtocol_handleConnects(pack, sock, clientAddr, client, wirelessNodeId , wirelessNodeIdLen) ; } else { (*handle_packets[(int)pack->header.type])(pack, sock, clientAddr, client); /* TODO: * - error handling * - centralise calls to time( &(c->lastContact) ); (currently in each _handle* function */ } FUNC_EXIT; }
int MQTTSProtocol_handleConnacks(void* pack, int sock, char* clientAddr) { MQTTS_Connack* connack = (MQTTS_Connack*)pack; int rc = 0; Clients* client = NULL; BridgeConnections* bc = Bridge_getBridgeConnection(sock); FUNC_ENTRY; if (bc == NULL) { rc = SOCKET_ERROR; goto exit; } if (bc->primary && bc->primary->socket == sock) client = bc->primary; else client = bc->backup; if (connack->returnCode != MQTTS_RC_ACCEPTED) { if (client) { if (client->noLocal == 1) client->noLocal = 0; else Log(LOG_WARNING, 132, NULL, connack->returnCode, client->clientID); MQTTProtocol_closeSession(client, 0); } else Socket_close(sock); } else if (client) { ListElement* addr = bc->cur_address; if (client == bc->primary && bc->round_robin == 0) addr = bc->addresses->first; Log(LOG_INFO, 133, NULL, bc->name, (char*)(addr->content)); client->connect_state = 3; //should have been 2 before bc->last_connect_result = connack->returnCode; (bc->no_successful_connections)++; client->connected = 1; client->good = 1; client->ping_outstanding = 0; time(&(client->lastContact)); if (client->will) { Publish pub; Messages* m = NULL; MQTTProtocol_sys_publish(client->will->topic, "1"); pub.payload = "1"; pub.payloadlen = 1; pub.topic = client->will->topic; MQTTProtocol_startOrQueuePublish(client, &pub, 0, client->will->retained, &m); } if (client == bc->primary && (bc->backup && (bc->backup->connected == 1 || bc->backup->connect_state != 0))) { Log(LOG_INFO, 134, NULL, (char*)(bc->cur_address->content)); MQTTProtocol_closeSession(bc->backup, 0); } //if (bc->addresses->count > 1 || bc->no_successful_connections == 1) Bridge_subscribe(bc, client); } //Clients* client = Protocol_getclientbyaddr(clientAddr); MQTTSPacket_free_packet(pack); exit: FUNC_EXIT_RC(rc); return rc; }
/** * MQTT retry protocol and socket pending writes processing. * @param now current time * @param doRetry boolean - retries as well as pending writes? * @return not actually used */ int MQTTProtocol_retry(time_t now, int doRetry) { Node* current = NULL; int rc = 0; FUNC_ENTRY; current = TreeNextElement(bstate->clients, current); /* look through the outbound message list of each client, checking to see if a retry is necessary */ while (current) { Clients* client = (Clients*)(current->content); current = TreeNextElement(bstate->clients, current); if (client->connected == 0) { #if defined(MQTTS) if (client->protocol == PROTOCOL_MQTTS) { if (difftime(now,client->lastContact) > bstate->retry_interval) { int rc2 = 0; /* NB: no dup bit for these packets */ if (client->connect_state == 1) /* TODO: handle err */ rc2 = MQTTSPacket_send_willTopicReq(client); else if (client->connect_state == 2) /* TODO: handle err */ rc2 = MQTTSPacket_send_willMsgReq(client); if (rc2 == SOCKET_ERROR) { client->good = 0; Log(LOG_WARNING, 29, NULL, client->clientID, client->socket); MQTTProtocol_closeSession(client, 1); client = NULL; } } } else if (client->protocol == PROTOCOL_MQTTS_DTLS) { if (difftime(now,client->lastContact) > bstate->retry_interval) { int rc2 = 0; /* NB: no dup bit for these packets */ if (client->connect_state == 1) /* TODO: handle err */ rc2 = MQTTSPacket_send_willTopicReq(client); else if (client->connect_state == 2) /* TODO: handle err */ rc2 = MQTTSPacket_send_willMsgReq(client); if (rc2 == SOCKET_ERROR) { client->good = 0; Log(LOG_WARNING, 29, NULL, client->clientID, client->socket); MQTTProtocol_closeSession(client, 1); client = NULL; } } } #endif continue; } if (client->good == 0) { MQTTProtocol_closeSession(client, 1); continue; } if (Socket_noPendingWrites(client->socket) == 0) continue; if (doRetry) MQTTProtocol_retries(now, client); if (client) { if (MQTTProtocol_processQueued(client)) rc = 1; } } FUNC_EXIT_RC(rc); return rc; }
/** * MQTT protocol timeslice for one packet and client - must not take too long! * @param sock the socket which is ready for the packet to be read from * @param client the client structure which corresponds to the socket */ void MQTTProtocol_timeslice(int sock, Clients* client) { int error; MQTTPacket* pack; FUNC_ENTRY; Log(TRACE_MIN, -1, "%d %s About to read packet for peer address %s", sock, (client == NULL) ? "unknown" : client->clientID, Socket_getpeer(sock)); in_MQTTPacket_Factory = sock; pack = MQTTPacket_Factory(sock, &error); in_MQTTPacket_Factory = -1; if (pack == NULL) { /* there was an error on the socket, so clean it up */ if (error == SOCKET_ERROR || error == BAD_MQTT_PACKET) { if (client != NULL) { client->good = 0; /* make sure we don't try and send messages to ourselves */ if (error == SOCKET_ERROR) Log(LOG_WARNING, 18, NULL, client->clientID, sock, Socket_getpeer(sock)); else Log(LOG_WARNING, 19, NULL, client->clientID, sock, Socket_getpeer(sock)); MQTTProtocol_closeSession(client, 1); } else { if (error == SOCKET_ERROR) /* Don't do a Socket_getpeer in the case of SOCKET_ERROR - * otherwise another SOCKET_ERROR will be hit */ Log(LOG_WARNING, 20, NULL, sock, "unknown"); else Log(LOG_WARNING, 21, NULL, sock, Socket_getpeer(sock)); Socket_close(sock); } } } else if (handle_packets[pack->header.bits.type] == NULL) Log(LOG_WARNING, 22, NULL, pack->header.bits.type, sock); else { if (client == NULL && pack->header.bits.type != CONNECT) { Log(LOG_WARNING, 23, NULL, sock, Socket_getpeer(sock), MQTTPacket_name(pack->header.bits.type)); MQTTPacket_free_packet(pack); Socket_close(sock); } else { Node* elem = NULL; /* incoming publish at QoS 0 does not result in outgoing communication, so we don't want to count it as contact, for PING processing on outbound connections */ int update_time = (pack->header.bits.type == PUBLISH && pack->header.bits.qos == 0) ? 0 : 1; if (client && (update_time || (client->outbound == 0))) time(&(client->lastContact)); if ((*handle_packets[pack->header.bits.type])(pack, sock, client) == SOCKET_ERROR) { /* the client could have been closed during handle_packet, so check to see if it's still in the client list */ elem = TreeFind(bstate->clients, &sock); if (elem == NULL && client && client->clientID) elem = TreeFind(bstate->disconnected_clients, client->clientID); if (elem != NULL) { client = (Clients*)(elem->content); client->good = 0; /* make sure we don't try and send messages to ourselves */ Log(LOG_WARNING, 18, NULL, client->clientID, sock, Socket_getpeer(sock)); MQTTProtocol_closeSession(client, 1); } else { Log(LOG_WARNING, 20, NULL, sock, Socket_getpeer(sock)); Socket_close(sock); } } } } /*MQTTProtocol_housekeeping(); move to Protocol_timeslice*/ FUNC_EXIT; }