diff --git a/examples/include.am b/examples/include.am index c3963187f..dd0548eae 100644 --- a/examples/include.am +++ b/examples/include.am @@ -11,11 +11,13 @@ noinst_PROGRAMS += examples/mqttclient/mqttclient \ examples/wiot/wiot \ examples/nbclient/nbclient \ examples/multithread/multithread \ - examples/sn-client/sn-client \ - examples/sn-client/sn-client_qos-1 \ - examples/sn-client/sn-multithread \ examples/pub-sub/mqtt-pub \ examples/pub-sub/mqtt-sub +if BUILD_SN +noinst_PROGRAMS += examples/sn-client/sn-client \ + examples/sn-client/sn-client_qos-1 \ + examples/sn-client/sn-multithread +endif noinst_HEADERS += examples/mqttclient/mqttclient.h \ examples/mqttsimple/mqttsimple.h \ @@ -30,8 +32,10 @@ noinst_HEADERS += examples/mqttclient/mqttclient.h \ examples/mqttport.h \ examples/nbclient/nbclient.h \ examples/multithread/multithread.h \ - examples/sn-client/sn-client.h \ examples/pub-sub/mqtt-pub-sub.h +if BUILD_SN +noinst_HEADERS += examples/sn-client/sn-client.h +endif # MQTT Client Example examples_mqttclient_mqttclient_SOURCES = examples/mqttclient/mqttclient.c \ @@ -108,7 +112,7 @@ examples_wiot_wiot_LDADD = src/libwolfmqtt.la examples_wiot_wiot_DEPENDENCIES = src/libwolfmqtt.la examples_wiot_wiot_CPPFLAGS = -I$(top_srcdir)/examples $(AM_CPPFLAGS) - +if BUILD_SN # MQTT-SN Examples examples_sn_client_sn_client_SOURCES = examples/sn-client/sn-client.c \ examples/mqttnet.c \ @@ -130,6 +134,7 @@ examples_sn_client_sn_multithread_SOURCES = examples/sn-client/sn-multithr examples_sn_client_sn_multithread_LDADD = src/libwolfmqtt.la examples_sn_client_sn_multithread_DEPENDENCIES = src/libwolfmqtt.la examples_sn_client_sn_multithread_CPPFLAGS = -I$(top_srcdir)/examples $(AM_CPPFLAGS) +endif # MQTT pub and sub clients examples_pub_sub_mqtt_pub_SOURCES = examples/pub-sub/mqtt-pub.c \ @@ -160,9 +165,11 @@ dist_example_DATA+= examples/mqttnet.c \ examples/wiot/wiot.c dist_example_DATA+= examples/nbclient/nbclient.c dist_example_DATA+= examples/multithread/multithread.c +if BUILD_SN dist_example_DATA+= examples/sn-client/sn-client.c dist_example_DATA+= examples/sn-client/sn-client_qos-1.c dist_example_DATA+= examples/sn-client/sn-multithread.c +endif dist_example_DATA+= examples/pub-sub/mqtt-pub.c dist_example_DATA+= examples/pub-sub/mqtt-sub.c @@ -174,11 +181,13 @@ DISTCLEANFILES+= examples/mqttclient/.libs/mqttclient \ examples/wiot/.libs/wiot \ examples/nbclient/.libs/nbclient \ examples/multithread/.libs/multithread \ - examples/sn-client/.libs/sn-client \ - examples/sn-client/.libs/sn-client_qos-1 \ - examples/sn-client/.libs/sn-multithread \ examples/pub-sub/mqtt-pub \ examples/pub-sub/mqtt-sub +if BUILD_SN +DISTCLEANFILES+= examples/sn-client/.libs/sn-client \ + examples/sn-client/.libs/sn-client_qos-1 \ + examples/sn-client/.libs/sn-multithread +endif EXTRA_DIST+= examples/mqttuart.c \ examples/publish.dat \ diff --git a/src/include.am b/src/include.am index c3b7bab37..08f9cd162 100644 --- a/src/include.am +++ b/src/include.am @@ -8,6 +8,11 @@ src_libwolfmqtt_la_SOURCES = src/mqtt_client.c \ src/mqtt_packet.c \ src/mqtt_socket.c +if BUILD_SN +src_libwolfmqtt_la_SOURCES += src/mqtt_sn_client.c \ + src/mqtt_sn_packet.c +endif + src_libwolfmqtt_la_CFLAGS = -DBUILDING_WOLFMQTT $(AM_CFLAGS) src_libwolfmqtt_la_CPPFLAGS = -DBUILDING_WOLFMQTT $(AM_CPPFLAGS) src_libwolfmqtt_la_LDFLAGS = ${AM_LDFLAGS} -no-undefined -version-info ${WOLFMQTT_LIBRARY_VERSION} diff --git a/src/mqtt_client.c b/src/mqtt_client.c index fe3c05f76..de2bc6c57 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -160,7 +160,7 @@ static int MqttClient_Publish_ReadPayload(MqttClient* client, #endif /* These RespList functions assume caller has locked client->lockClient mutex */ -static int MqttClient_RespList_Add(MqttClient *client, +int MqttClient_RespList_Add(MqttClient *client, MqttPacketType packet_type, word16 packet_id, MqttPendResp *newResp, void *packet_obj) { @@ -208,7 +208,7 @@ static int MqttClient_RespList_Add(MqttClient *client, return 0; } -static void MqttClient_RespList_Remove(MqttClient *client, MqttPendResp *rmResp) +void MqttClient_RespList_Remove(MqttClient *client, MqttPendResp *rmResp) { MqttPendResp *tmpResp; @@ -252,7 +252,7 @@ static void MqttClient_RespList_Remove(MqttClient *client, MqttPendResp *rmResp) #endif } -static int MqttClient_RespList_Find(MqttClient *client, +int MqttClient_RespList_Find(MqttClient *client, MqttPacketType packet_type, word16 packet_id, MqttPendResp **retResp) { int rc = 0; @@ -2840,1848 +2840,3 @@ word32 MqttClient_Flags(MqttClient *client, word32 mask, word32 flags) } return ret; } - -#ifdef WOLFMQTT_SN - -/* Private functions */ -static int SN_Client_HandlePacket(MqttClient* client, SN_MsgType packet_type, - void* packet_obj, int timeout) -{ - int rc = MQTT_CODE_SUCCESS; - word16 packet_id = 0; - - (void)timeout; - - switch ((int)packet_type) - { - case SN_MSG_TYPE_GWINFO: - { - SN_GwInfo info, *p_info = &info; - if (packet_obj) { - p_info = (SN_GwInfo*)packet_obj; - } - else { - XMEMSET(p_info, 0, sizeof(SN_GwInfo)); - } - - rc = SN_Decode_GWInfo(client->rx_buf, client->packet.buf_len, - p_info); - if (rc <= 0) { - return rc; - } - break; - } - case SN_MSG_TYPE_CONNACK: - { - /* Decode connect ack */ - SN_ConnectAck connect_ack, *p_connect_ack = &connect_ack; - if (packet_obj) { - p_connect_ack = (SN_ConnectAck*)packet_obj; - } - else { - XMEMSET(p_connect_ack, 0, sizeof(SN_ConnectAck)); - } - p_connect_ack->return_code = - client->rx_buf[client->packet.buf_len-1]; - - break; - } - case SN_MSG_TYPE_WILLTOPICREQ: - { - rc = SN_Decode_WillTopicReq(client->rx_buf, client->packet.buf_len); - break; - } - case SN_MSG_TYPE_WILLMSGREQ: - { - rc = SN_Decode_WillMsgReq(client->rx_buf, client->packet.buf_len); - break; - } - case SN_MSG_TYPE_REGISTER: - { - /* Decode register */ - SN_Register reg_s; - - XMEMSET(®_s, 0, sizeof(SN_Register)); - - rc = SN_Decode_Register(client->rx_buf, client->packet.buf_len, - ®_s); - - if (rc > 0) { - /* Initialize the regack */ - reg_s.regack.packet_id = reg_s.packet_id; - reg_s.regack.topicId = reg_s.topicId; - reg_s.regack.return_code = SN_RC_NOTSUPPORTED; - - /* Call the register callback to allow app to - handle new topic ID assignment. */ - if (client->reg_cb != NULL) { - rc = client->reg_cb(reg_s.topicId, - reg_s.topicName, client->reg_ctx); - /* Set the regack return code */ - reg_s.regack.return_code = (rc >= 0) ? SN_RC_ACCEPTED : - SN_RC_INVTOPICNAME; - } - - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the register acknowledgment */ - rc = SN_Encode_RegAck(client->tx_buf, client->tx_buf_len, - ®_s.regack); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_REGACK), - SN_MSG_TYPE_REGACK, reg_s.packet_id); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - /* Send regack packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - } - - break; - } - case SN_MSG_TYPE_REGACK: - { - /* Decode register ack */ - SN_RegAck regack_s, *p_regack = ®ack_s; - if (packet_obj) { - p_regack = (SN_RegAck*)packet_obj; - } - else { - XMEMSET(p_regack, 0, sizeof(SN_RegAck)); - } - - rc = SN_Decode_RegAck(client->rx_buf, client->packet.buf_len, - p_regack); - if (rc > 0) { - packet_id = p_regack->packet_id; - } - - break; - } - case SN_MSG_TYPE_PUBLISH: - { - SN_Publish pub, *p_pub = &pub; - if (packet_obj) { - p_pub = (SN_Publish*)packet_obj; - } - else { - XMEMSET(p_pub, 0, sizeof(SN_Publish)); - } - - /* Decode publish message */ - rc = SN_Decode_Publish(client->rx_buf, client->packet.buf_len, - p_pub); - if (rc <= 0) { - return rc; - } - - /* Issue callback for new message */ - if (client->msg_cb) { - /* if using the temp publish message buffer, - then populate message context with client context */ - if (&client->msgSN.publish == p_pub) - p_pub->ctx = client->ctx; - rc = client->msg_cb(client, (MqttMessage*)p_pub, 1, 1); - if (rc != MQTT_CODE_SUCCESS) { - return rc; - }; - } - - /* Handle Qos */ - if (p_pub->qos > MQTT_QOS_0) { - SN_MsgType type; - - packet_id = p_pub->packet_id; - - /* Determine packet type to write */ - type = (p_pub->qos == MQTT_QOS_1) ? - SN_MSG_TYPE_PUBACK : - SN_MSG_TYPE_PUBREC; - p_pub->resp.packet_id = packet_id; - - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode publish response */ - rc = SN_Encode_PublishResp(client->tx_buf, - client->tx_buf_len, type, &p_pub->resp); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d," - " QoS %d", - rc, SN_Packet_TypeDesc(type), type, packet_id, - p_pub->qos); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - /* Send packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - } - break; - } - case SN_MSG_TYPE_PUBACK: - case SN_MSG_TYPE_PUBCOMP: - case SN_MSG_TYPE_PUBREC: - case SN_MSG_TYPE_PUBREL: - { - SN_PublishResp publish_resp, *p_publish_resp = &publish_resp; - if (packet_obj) { - p_publish_resp = (SN_PublishResp*)packet_obj; - } - else { - XMEMSET(p_publish_resp, 0, sizeof(SN_PublishResp)); - } - - /* Decode publish response message */ - rc = SN_Decode_PublishResp(client->rx_buf, client->packet.buf_len, - packet_type, p_publish_resp); - if (rc <= 0) { - return rc; - } - packet_id = p_publish_resp->packet_id; - - /* If Qos then send response */ - if (packet_type == SN_MSG_TYPE_PUBREC || - packet_type == SN_MSG_TYPE_PUBREL) { - - byte resp_type = (packet_type == SN_MSG_TYPE_PUBREC) ? - SN_MSG_TYPE_PUBREL : SN_MSG_TYPE_PUBCOMP; - - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode publish response */ - p_publish_resp->packet_id = packet_id; - rc = SN_Encode_PublishResp(client->tx_buf, - client->tx_buf_len, resp_type, p_publish_resp); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d", - rc, MqttPacket_TypeDesc(resp_type), resp_type, packet_id); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - /* Send packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - } - break; - } - case SN_MSG_TYPE_SUBACK: - { - /* Decode subscribe ack */ - SN_SubAck subscribe_ack, *p_subscribe_ack = &subscribe_ack; - if (packet_obj) { - p_subscribe_ack = (SN_SubAck*)packet_obj; - } - else { - XMEMSET(p_subscribe_ack, 0, sizeof(SN_SubAck)); - } - - rc = SN_Decode_SubscribeAck(client->rx_buf, client->packet.buf_len, - p_subscribe_ack); - if (rc <= 0) { - return rc; - } - packet_id = p_subscribe_ack->packet_id; - - break; - } - case SN_MSG_TYPE_UNSUBACK: - { - /* Decode unsubscribe ack */ - SN_UnsubscribeAck unsubscribe_ack, - *p_unsubscribe_ack = &unsubscribe_ack; - if (packet_obj) { - p_unsubscribe_ack = (SN_UnsubscribeAck*)packet_obj; - } - else { - XMEMSET(p_unsubscribe_ack, 0, sizeof(SN_UnsubscribeAck)); - } - rc = SN_Decode_UnsubscribeAck(client->rx_buf, - client->packet.buf_len, p_unsubscribe_ack); - if (rc <= 0) { - return rc; - } - packet_id = p_unsubscribe_ack->packet_id; - - break; - } - case SN_MSG_TYPE_PING_RESP: - { - /* Decode ping */ - rc = SN_Decode_Ping(client->rx_buf, client->packet.buf_len); - break; - } - case SN_MSG_TYPE_PING_REQ: - { - /* Decode ping */ - rc = SN_Decode_Ping(client->rx_buf, client->packet.buf_len); - if (rc <= 0) { return rc; } - - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the ping packet as a response */ - rc = SN_Encode_Ping(client->tx_buf, client->tx_buf_len, NULL, - SN_MSG_TYPE_PING_RESP); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_PING_RESP), - SN_MSG_TYPE_PING_RESP); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - /* Send ping resp packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - break; - } - case SN_MSG_TYPE_WILLTOPICRESP: - { - /* Decode Will Topic Response */ - SN_WillTopicResp resp_s, *resp = &resp_s; - if (packet_obj) { - resp = (SN_WillTopicResp*)packet_obj; - } - else { - XMEMSET(resp, 0, sizeof(SN_WillTopicResp)); - } - rc = SN_Decode_WillTopicResponse(client->rx_buf, - client->packet.buf_len, &resp->return_code); - break; - } - case SN_MSG_TYPE_WILLMSGRESP: - { - /* Decode Will Message Response */ - SN_WillMsgResp resp_s, *resp = &resp_s; - if (packet_obj) { - resp = (SN_WillMsgResp*)packet_obj; - } - else { - XMEMSET(resp, 0, sizeof(SN_WillMsgResp)); - } - rc = SN_Decode_WillMsgResponse(client->rx_buf, - client->packet.buf_len, &resp->return_code); - break; - } - case SN_MSG_TYPE_DISCONNECT: - { - SN_Disconnect disc_s, *disc = &disc_s; - if (packet_obj) { - disc = (SN_Disconnect*)packet_obj; - } - else { - XMEMSET(disc, 0, sizeof(SN_Disconnect)); - } - /* Decode Disconnect */ - rc = SN_Decode_Disconnect(client->rx_buf, client->packet.buf_len); - -#ifdef WOLFMQTT_DISCONNECT_CB - /* Call disconnect callback to allow handling broker disconnect */ - if ((client->disconnect_cb != NULL) && (disc->sleepTmr == 0)) { - client->disconnect_cb(client, rc, client->disconnect_ctx); - } -#endif - break; - } - - default: - { - /* Other types are server side only, ignore */ - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("SN_Client_HandlePacket: Invalid client packet type %u!", - packet_type); - #endif - break; - } - } /* switch (packet_type) */ - - (void)packet_id; - - return rc; -} - -static int SN_Client_WaitType(MqttClient *client, void* packet_obj, - byte wait_type, word16 wait_packet_id, int timeout_ms) -{ - int rc; - word16 packet_id; - SN_MsgType packet_type; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp *pendResp; -#endif - MqttMsgStat* mms_stat; - int waitMatchFound; - void* use_packet_obj = NULL; - - if (client == NULL || packet_obj == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* all packet type structures must have MqttMsgStat at top */ - mms_stat = (MqttMsgStat*)packet_obj; - -wait_again: - - /* initialize variables */ - packet_id = 0; - packet_type = SN_MSG_TYPE_RESERVED; -#ifdef WOLFMQTT_MULTITHREAD - pendResp = NULL; -#endif - waitMatchFound = 0; - -#ifdef WOLFMQTT_DEBUG_CLIENT - #ifdef WOLFMQTT_NONBLOCK - if (client->lastRc != MQTT_CODE_CONTINUE) - #endif - { - PRINTF("SN_Client_WaitType: Type %s (%d), ID %d", - SN_Packet_TypeDesc((SN_MsgType)wait_type), - wait_type, wait_packet_id); - } -#endif - - switch (mms_stat->read) - { - case MQTT_MSG_BEGIN: - { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock recv socket mutex */ - rc = wm_SemLock(&client->lockRecv); - if (rc != 0) { - PRINTF("SN_Client_WaitType recv lock error"); - return rc; - } - mms_stat->isReadLocked = 1; - MQTT_TRACE_MSG("SN lockRecv"); - #endif - - /* reset the packet state used by SN_Packet_Read */ - client->packet.stat = MQTT_PK_BEGIN; - } - FALL_THROUGH; - - case MQTT_MSG_WAIT: - { - #ifdef WOLFMQTT_MULTITHREAD - /* Check to see if packet type and id have already completed */ - pendResp = NULL; - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - if (MqttClient_RespList_Find(client, (MqttPacketType)wait_type, - wait_packet_id, &pendResp)) { - if (pendResp->packetDone) { - /* pending response is already done, so return */ - rc = pendResp->packet_ret; - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("PendResp already Done %p: Rc %d", pendResp, rc); - #endif - MqttClient_RespList_Remove(client, pendResp); - wm_SemUnlock(&client->lockClient); - MQTT_TRACE_MSG("SN unlockRecv"); - wm_SemUnlock(&client->lockRecv); - return rc; - } - } - wm_SemUnlock(&client->lockClient); - } - else { - break; /* error */ - } - #endif /* WOLFMQTT_MULTITHREAD */ - - mms_stat->read = MQTT_MSG_WAIT; - - /* Wait for packet */ - rc = SN_Packet_Read(client, client->rx_buf, client->rx_buf_len, - timeout_ms); - if (rc <= 0) { - break; - } - - client->packet.buf_len = rc; - - /* Decode header */ - rc = SN_Decode_Header(client->rx_buf, client->packet.buf_len, - &packet_type, &packet_id); - if (rc < 0) { - break; - } - - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("Read Packet: Len %d, Type %d, ID %d", - client->packet.buf_len, packet_type, packet_id); - #endif - - mms_stat->read = MQTT_MSG_HEADER; - } - FALL_THROUGH; - - case MQTT_MSG_HEADER: - case MQTT_MSG_PAYLOAD: - case MQTT_MSG_PAYLOAD2: - { - SN_MsgType use_packet_type; - - /* Determine if we received data for this request */ - if ((wait_type == SN_MSG_TYPE_ANY || wait_type == packet_type) && - (wait_packet_id == 0 || wait_packet_id == packet_id)) - { - use_packet_obj = packet_obj; - waitMatchFound = 1; - } - else { - /* use generic packet object */ - use_packet_obj = &client->msgSN; - } - use_packet_type = packet_type; - - #ifdef WOLFMQTT_MULTITHREAD - /* Check to see if we have a pending response for this packet */ - pendResp = NULL; - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - if (MqttClient_RespList_Find(client, - (MqttPacketType)packet_type, packet_id, &pendResp)) { - /* we found packet match this incoming read packet */ - pendResp->packetProcessing = 1; - if (pendResp->packet_obj != packet_obj) { - use_packet_obj = pendResp->packet_obj; - use_packet_type = (SN_MsgType)pendResp->packet_type; - /* req from another thread... not a match */ - waitMatchFound = 0; - } - } - wm_SemUnlock(&client->lockClient); - } - else { - break; /* error */ - } - #endif /* WOLFMQTT_MULTITHREAD */ - - rc = SN_Client_HandlePacket(client, use_packet_type, use_packet_obj, - timeout_ms); - - #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) { - break; - } - #endif - - /* handle success case */ - if (rc >= 0) { - rc = MQTT_CODE_SUCCESS; - } - - #ifdef WOLFMQTT_MULTITHREAD - if (pendResp) { - /* Mark pending response entry done */ - if (wm_SemLock(&client->lockClient) == 0) { - pendResp->packetDone = 1; - pendResp->packet_ret = rc; - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("PendResp Marked Done %p", pendResp); - #endif - pendResp = NULL; - wm_SemUnlock(&client->lockClient); - } - } - #endif /* WOLFMQTT_MULTITHREAD */ - break; - } - - case MQTT_MSG_ACK: /* ack handled in SN_Client_HandlePacket */ - case MQTT_MSG_AUTH: - default: - { - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("SN_Client_WaitType: Invalid state %d!", mms_stat->read); - #endif - rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_STAT); - break; - } - } /* switch (msg->stat) */ - -#ifdef WOLFMQTT_DEBUG_CLIENT - if (rc != MQTT_CODE_CONTINUE) { - PRINTF("SN_Client_WaitType: rc %d, state %d", rc, mms_stat->read); - } -#endif - - if (mms_stat->read == MQTT_MSG_WAIT || rc != MQTT_CODE_CONTINUE) { - /* reset state */ - mms_stat->read = MQTT_MSG_BEGIN; - - #ifdef WOLFMQTT_MULTITHREAD - if (mms_stat->isReadLocked) { - mms_stat->isReadLocked = 0; - wm_SemUnlock(&client->lockRecv); - } - #endif - } - -#ifdef WOLFMQTT_NONBLOCK - #ifdef WOLFMQTT_DEBUG_CLIENT - client->lastRc = rc; - #endif - if (rc == MQTT_CODE_CONTINUE) { - return rc; - } -#endif - - /* Clear shared union for next call */ - if ((MqttObject*)use_packet_obj == &client->msg) { - /* reset the members, but not the stat */ - XMEMSET(((byte*)&client->msg.stat) + sizeof(client->msg.stat), 0, - sizeof(client->msg)-sizeof(client->msg.stat)); - } - - if (rc < 0) { - #ifdef WOLFMQTT_DEBUG_CLIENT - if (rc != MQTT_CODE_CONTINUE) { - PRINTF("SN_Client_WaitType: Failure: %s (%d)", - MqttClient_ReturnCodeToString(rc), rc); - } - #endif - return rc; - } - - if (!waitMatchFound) { - /* if we get here, then the we are still waiting for a packet */ - goto wait_again; - } - - return rc; -} - -/* Public Functions */ - -int SN_Client_SetRegisterCallback(MqttClient *client, - SN_ClientRegisterCb regCb, - void* ctx) -{ - int rc = MQTT_CODE_SUCCESS; - - if (client == NULL) - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - -#ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { -#endif - - client->reg_cb = regCb; - client->reg_ctx = ctx; - -#ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockClient); - } -#endif - - return rc; -} - -int SN_Client_SearchGW(MqttClient *client, SN_SearchGw *search) -{ - int rc; - - /* Validate required arguments */ - if (client == NULL || search == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (search->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the search packet */ - rc = SN_Encode_SearchGW(client->tx_buf, client->tx_buf_len, - search->radius); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_SEARCHGW), - SN_MSG_TYPE_SEARCHGW); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_GWINFO, 0, - &search->pendResp, &search->gwInfo); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send search for gateway packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &search->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - return rc; - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - search->stat.write = MQTT_MSG_WAIT; - } - - /* Wait for gateway info packet */ - rc = SN_Client_WaitType(client, &search->gwInfo, SN_MSG_TYPE_GWINFO, 0, - client->cmd_timeout_ms); -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &search->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - /* reset state */ - search->stat.write = MQTT_MSG_BEGIN; - - return rc; -} - -static int SN_WillTopic(MqttClient *client, SN_Will *will) -{ - int rc; - - /* Validate required arguments */ - if (client == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - -#ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_WILLTOPICREQ, 0, - &will->pendResp, &will->resp.topicResp); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - return rc; /* Error locking client */ - } -#endif - - /* Wait for Will Topic Request packet */ - rc = SN_Client_WaitType(client, will, - SN_MSG_TYPE_WILLTOPICREQ, 0, client->cmd_timeout_ms); -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif - -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &will->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - if (rc == 0) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode Will Topic */ - rc = SN_Encode_WillTopic(client->tx_buf, client->tx_buf_len, - will); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLTOPIC), - SN_MSG_TYPE_WILLTOPIC); - #endif - if (rc > 0) { - /* Send Will Topic packet */ - client->write.len = rc; - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc == client->write.len) { - rc = 0; - } - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - } - - return rc; -} - -static int SN_WillMessage(MqttClient *client, SN_Will *will) -{ - int rc; - - /* Validate required arguments */ - if (client == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - -#ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_WILLMSGREQ, 0, - &will->pendResp, &will->resp.msgResp); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - return rc; /* Error locking client */ - } -#endif - - /* Wait for Will Message Request */ - rc = SN_Client_WaitType(client, &will->resp.msgResp, - SN_MSG_TYPE_WILLMSGREQ, 0, client->cmd_timeout_ms); - -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif - -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &will->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - if (rc == 0) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - /* Encode Will Message */ - rc = SN_Encode_WillMsg(client->tx_buf, - client->tx_buf_len, will); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLMSG), - SN_MSG_TYPE_WILLMSG); - #endif - if (rc > 0) { - /* Send Will Topic packet */ - client->write.len = rc; - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc == client->write.len) { - rc = 0; - } - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - } - - return rc; -} - -int SN_Client_Connect(MqttClient *client, SN_Connect *mc_connect) -{ - int rc = 0; - static byte will_done; - - /* Validate required arguments */ - if ((client == NULL) || (mc_connect == NULL)) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (mc_connect->stat.write == MQTT_MSG_BEGIN) { - - will_done = 0; - - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the connect packet */ - rc = SN_Encode_Connect(client->tx_buf, client->tx_buf_len, mc_connect); -#ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d, QoS %d", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_CONNECT), - SN_MSG_TYPE_CONNECT, 0, 0); -#endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_CONNACK, 0, - &mc_connect->pendResp, &mc_connect->ack); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send connect packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - mc_connect->stat.write = MQTT_MSG_WAIT; - } - - if ((mc_connect->enable_lwt == 1) && (will_done != 1)) { - /* If the will is enabled, then the gateway requests the topic and - message in separate packets. */ - rc = SN_WillTopic(client, &mc_connect->will); - if (rc != 0) { - return rc; - } - - rc = SN_WillMessage(client, &mc_connect->will); - if (rc != 0) { - return rc; - } - will_done = 1; - } - - /* Wait for connect ack packet */ - rc = SN_Client_WaitType(client, &mc_connect->ack, - SN_MSG_TYPE_CONNACK, 0, client->cmd_timeout_ms); -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif - -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &mc_connect->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - /* reset state */ - mc_connect->stat.write = MQTT_MSG_BEGIN; - - return rc; -} - -int SN_Client_WillTopicUpdate(MqttClient *client, SN_Will *will) -{ - int rc = 0; - - /* Validate required arguments */ - if (client == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (will->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode Will Topic Update */ - rc = SN_Encode_WillTopicUpdate(client->tx_buf, - client->tx_buf_len, will); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLTOPICUPD), - SN_MSG_TYPE_WILLTOPICUPD); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_WILLTOPICRESP, - 0, &will->pendResp, &will->resp.topicResp); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send Will Topic Update packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &will->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - will->stat.write = MQTT_MSG_WAIT; - } - - /* Wait for Will Topic Update Response packet */ - rc = SN_Client_WaitType(client, &will->resp.topicResp, - SN_MSG_TYPE_WILLTOPICRESP, 0, client->cmd_timeout_ms); -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &will->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - /* reset state */ - will->stat.write = MQTT_MSG_BEGIN; - - return rc; -} - -int SN_Client_WillMsgUpdate(MqttClient *client, SN_Will *will) -{ - int rc = 0; - - /* Validate required arguments */ - if ((client == NULL) || (will == NULL)) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (will->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - /* Encode Will Message Update */ - rc = SN_Encode_WillMsgUpdate(client->tx_buf, - client->tx_buf_len, will); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLTOPICUPD), - SN_MSG_TYPE_WILLTOPICUPD); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_WILLMSGRESP, - 0, &will->pendResp, &will->resp.msgResp); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send Will Message Update packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &will->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - will->stat.write = MQTT_MSG_WAIT; - } - - /* Wait for Will Message Update Response packet */ - rc = SN_Client_WaitType(client, &will->resp.msgResp, - SN_MSG_TYPE_WILLMSGRESP, 0, client->cmd_timeout_ms); -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &will->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - /* reset state */ - will->stat.write = MQTT_MSG_BEGIN; - - return rc; - -} - -int SN_Client_Subscribe(MqttClient *client, SN_Subscribe *subscribe) -{ - int rc = -1; - - /* Validate required arguments */ - if (client == NULL || subscribe == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (subscribe->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the subscribe packet */ - rc = SN_Encode_Subscribe(client->tx_buf, client->tx_buf_len, - subscribe); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), QoS %d", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_SUBSCRIBE), - SN_MSG_TYPE_SUBSCRIBE, subscribe->qos); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_SUBACK, subscribe->packet_id, - &subscribe->pendResp, &subscribe->subAck); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send subscribe packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &subscribe->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - return rc; - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - subscribe->stat.write = MQTT_MSG_WAIT; - } - - /* Wait for subscribe ack packet */ - rc = SN_Client_WaitType(client, &subscribe->subAck, - SN_MSG_TYPE_SUBACK, subscribe->packet_id, client->cmd_timeout_ms); - -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &subscribe->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - /* reset state */ - subscribe->stat.write = MQTT_MSG_BEGIN; - - return rc; -} - -int SN_Client_Publish(MqttClient *client, SN_Publish *publish) -{ - int rc = MQTT_CODE_SUCCESS; - SN_MsgType resp_type; - - /* Validate required arguments */ - if (client == NULL || publish == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - switch (publish->stat.write) - { - case MQTT_MSG_BEGIN: - { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the publish packet */ - rc = SN_Encode_Publish(client->tx_buf, client->tx_buf_len, - publish); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d," - " QoS %d", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_PUBLISH), - SN_MSG_TYPE_PUBLISH, publish->packet_id, - publish->qos); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - - client->write.len = rc; - publish->buffer_pos = 0; - - #ifdef WOLFMQTT_MULTITHREAD - if ((publish->qos == MQTT_QOS_1) || - (publish->qos == MQTT_QOS_2)) { - resp_type = (publish->qos == MQTT_QOS_1) ? - SN_MSG_TYPE_PUBACK : - SN_MSG_TYPE_PUBCOMP; - - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)resp_type, publish->packet_id, - &publish->pendResp, &publish->resp); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - } - #endif - - publish->stat.write = MQTT_MSG_HEADER; - } - FALL_THROUGH; - - case MQTT_MSG_HEADER: - case MQTT_MSG_PAYLOAD: - case MQTT_MSG_PAYLOAD2: - { - /* Send packet and payload */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; - #endif - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - if (rc < 0) { - #ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &publish->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - return rc; - } - - if (rc == client->write.len) { - rc = MQTT_CODE_SUCCESS; - } - else { - rc = -1; - } - - /* if not expecting a reply, the reset state and exit */ - if ((publish->qos == MQTT_QOS_0) || - (publish->qos == MQTT_QOS_3)) { - break; - } - - publish->stat.write = MQTT_MSG_WAIT; - } - FALL_THROUGH; - - case MQTT_MSG_WAIT: - { - /* Handle QoS */ - if ((publish->qos == MQTT_QOS_1) || - (publish->qos == MQTT_QOS_2)) { - - /* Determine packet type to wait for */ - resp_type = (publish->qos == MQTT_QOS_1) ? - SN_MSG_TYPE_PUBACK : - SN_MSG_TYPE_PUBCOMP; - - /* Wait for publish response packet */ - rc = SN_Client_WaitType(client, &publish->resp, - resp_type, publish->packet_id, client->cmd_timeout_ms); - #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - break; - #endif - #ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &publish->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - - publish->return_code = publish->resp.return_code; - } - - break; - } - - case MQTT_MSG_ACK: - case MQTT_MSG_AUTH: - default: - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("SN_Client_Publish: Invalid state %d!", - publish->stat.write); - #endif - rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_STAT); - break; - } /* switch (publish->stat) */ - - /* reset state */ -#ifdef WOLFMQTT_NONBLOCK - if (rc != MQTT_CODE_CONTINUE) -#endif - { - publish->stat.write = MQTT_MSG_BEGIN; - } - if (rc > 0) { - rc = MQTT_CODE_SUCCESS; - } - - return rc; -} - -int SN_Client_Unsubscribe(MqttClient *client, SN_Unsubscribe *unsubscribe) -{ - int rc; - - /* Validate required arguments */ - if (client == NULL || unsubscribe == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (unsubscribe->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the subscribe packet */ - rc = SN_Encode_Unsubscribe(client->tx_buf, client->tx_buf_len, - unsubscribe); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_UNSUBSCRIBE), - SN_MSG_TYPE_UNSUBSCRIBE); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_UNSUBACK, - 0, &unsubscribe->pendResp, &unsubscribe->ack); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send unsubscribe packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &unsubscribe->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - unsubscribe->stat.write = MQTT_MSG_WAIT; - } - - /* Wait for unsubscribe ack packet */ - rc = SN_Client_WaitType(client, &unsubscribe->ack, - SN_MSG_TYPE_UNSUBACK, unsubscribe->packet_id, - client->cmd_timeout_ms); - #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; - #endif - #ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &unsubscribe->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - - /* reset state */ - unsubscribe->stat.write = MQTT_MSG_BEGIN; - - return rc; -} - -int SN_Client_Register(MqttClient *client, SN_Register *regist) -{ - int rc; - - /* Validate required arguments */ - if (client == NULL || regist == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (regist->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the register packet */ - rc = SN_Encode_Register(client->tx_buf, client->tx_buf_len, regist); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_REGISTER), - SN_MSG_TYPE_REGISTER); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_REGACK, - regist->packet_id, ®ist->pendResp, ®ist->regack); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send register packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, ®ist->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - return rc; - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - regist->stat.write = MQTT_MSG_WAIT; - } - - /* Wait for register acknowledge packet */ - rc = SN_Client_WaitType(client, ®ist->regack, - SN_MSG_TYPE_REGACK, regist->packet_id, client->cmd_timeout_ms); -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, ®ist->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - /* reset state */ - regist->stat.write = MQTT_MSG_BEGIN; - - return rc; -} - -int SN_Client_Ping(MqttClient *client, SN_PingReq *ping) -{ - int rc; - SN_PingReq loc_ping; - - /* Validate required arguments */ - if (client == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (ping == NULL) { - XMEMSET(&loc_ping, 0, sizeof(SN_PingReq)); - ping = &loc_ping; - } - - if (ping->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the ping packet as a request */ - rc = SN_Encode_Ping(client->tx_buf, client->tx_buf_len, ping, - SN_MSG_TYPE_PING_REQ); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_PING_REQ), - SN_MSG_TYPE_PING_REQ); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_PING_RESP, 0, - &ping->pendResp, NULL); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send ping req packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &ping->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - return rc; - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - ping->stat.write = MQTT_MSG_WAIT; - } - - /* Wait for ping resp packet */ - rc = SN_Client_WaitType(client, ping, - SN_MSG_TYPE_PING_RESP, 0, client->cmd_timeout_ms); -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &ping->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - /* reset state */ - ping->stat.write = MQTT_MSG_BEGIN; - - return rc; -} - -int SN_Client_Disconnect(MqttClient *client) -{ - return SN_Client_Disconnect_ex(client, NULL); -} - -int SN_Client_Disconnect_ex(MqttClient *client, SN_Disconnect *disconnect) -{ - int rc; - - /* Validate required arguments */ - if (client == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - -#ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } -#endif - - /* Encode the disconnect packet */ - rc = SN_Encode_Disconnect(client->tx_buf, client->tx_buf_len, disconnect); -#ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_DISCONNECT), - SN_MSG_TYPE_DISCONNECT); -#endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - -#ifdef WOLFMQTT_MULTITHREAD - if ((disconnect != NULL) && (disconnect->sleepTmr != 0)) { - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_DISCONNECT, 0, - &disconnect->pendResp, NULL); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - } -#endif - - /* Send disconnect packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &disconnect->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - return rc; - } -#ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); -#endif - - rc = MQTT_CODE_SUCCESS; - - /* If sleep was set, wait for response disconnect packet */ - if ((disconnect != NULL) && (disconnect->sleepTmr != 0)) { - rc = SN_Client_WaitType(client, disconnect, - SN_MSG_TYPE_DISCONNECT, 0, client->cmd_timeout_ms); - #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; - #endif - #ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &disconnect->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - } - - return rc; -} - -int SN_Client_WaitMessage_ex(MqttClient *client, SN_Object* packet_obj, - int timeout_ms) -{ - return SN_Client_WaitType(client, packet_obj, - SN_MSG_TYPE_ANY, 0, timeout_ms); -} - -int SN_Client_WaitMessage(MqttClient *client, int timeout_ms) -{ - if (client == NULL) - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - return SN_Client_WaitMessage_ex(client, &client->msgSN, timeout_ms); -} - -#endif /* defined WOLFMQTT_SN */ diff --git a/src/mqtt_packet.c b/src/mqtt_packet.c index c84c66664..cafa8e842 100644 --- a/src/mqtt_packet.c +++ b/src/mqtt_packet.c @@ -1917,7 +1917,7 @@ int MqttProps_Free(MqttProp *head) #endif /* WOLFMQTT_V5 */ -static int MqttPacket_HandleNetError(MqttClient *client, int rc) +int MqttPacket_HandleNetError(MqttClient *client, int rc) { (void)client; #ifdef WOLFMQTT_DISCONNECT_CB @@ -2106,1551 +2106,3 @@ const char* MqttPacket_TypeDesc(MqttPacketType packet_type) } #endif - -#ifdef WOLFMQTT_SN -const char* SN_Packet_TypeDesc(SN_MsgType packet_type) -{ - switch (packet_type) { - case SN_MSG_TYPE_ADVERTISE: - return "Advertise"; - case SN_MSG_TYPE_SEARCHGW: - return "Search gateway"; - case SN_MSG_TYPE_GWINFO: - return "Gateway info"; - case SN_MSG_TYPE_CONNECT: - return "Connect"; - case SN_MSG_TYPE_CONNACK: - return "Connect Ack"; - case SN_MSG_TYPE_WILLTOPICREQ: - return "Will topic request"; - case SN_MSG_TYPE_WILLTOPIC: - return "Will topic set"; - case SN_MSG_TYPE_WILLMSGREQ: - return "Will message request"; - case SN_MSG_TYPE_WILLMSG: - return "Will message set"; - case SN_MSG_TYPE_REGISTER: - return "Register"; - case SN_MSG_TYPE_REGACK: - return "Register Ack"; - case SN_MSG_TYPE_PUBLISH: - return "Publish"; - case SN_MSG_TYPE_PUBACK: - return "Publish Ack"; - case SN_MSG_TYPE_PUBCOMP: - return "Publish complete"; - case SN_MSG_TYPE_PUBREC: - return "Publish Received"; - case SN_MSG_TYPE_PUBREL: - return "Publish Release"; - case SN_MSG_TYPE_SUBSCRIBE: - return "Subscribe"; - case SN_MSG_TYPE_SUBACK: - return "Subscribe Ack"; - case SN_MSG_TYPE_UNSUBSCRIBE: - return "Unsubscribe"; - case SN_MSG_TYPE_UNSUBACK: - return "Unsubscribe Ack"; - case SN_MSG_TYPE_PING_REQ: - return "Ping Req"; - case SN_MSG_TYPE_PING_RESP: - return "Ping Resp"; - case SN_MSG_TYPE_DISCONNECT: - return "Disconnect"; - case SN_MSG_TYPE_WILLTOPICUPD: - return "Will topic update"; - case SN_MSG_TYPE_WILLTOPICRESP: - return "WIll topic response"; - case SN_MSG_TYPE_WILLMSGUPD: - return "Will message update"; - case SN_MSG_TYPE_WILLMSGRESP: - return "Will message response"; - case SN_MSG_TYPE_ENCAPMSG: - return "Encapsulated message"; - case SN_MSG_TYPE_ANY: - return "Any"; - default: - break; - } - return "Unknown"; -} - -int SN_Decode_Header(byte *rx_buf, int rx_buf_len, - SN_MsgType* p_packet_type, word16* p_packet_id) -{ - SN_MsgType packet_type; - word16 total_len; - - if (rx_buf == NULL || rx_buf_len < MQTT_PACKET_HEADER_MIN_SIZE) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_buf++; - if (total_len == SN_PACKET_LEN_IND) { - /* The length is stored in the next two bytes */ - rx_buf += MqttDecode_Num(rx_buf, &total_len); - } - - if (total_len > rx_buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Message Type */ - packet_type = (SN_MsgType)*rx_buf++; - - if (p_packet_type) - *p_packet_type = packet_type; - - if (p_packet_id) { - switch(packet_type) { - case SN_MSG_TYPE_REGACK: - case SN_MSG_TYPE_PUBACK: - /* octet 4-5 */ - MqttDecode_Num(rx_buf + 2, p_packet_id); - break; - case SN_MSG_TYPE_PUBCOMP: - case SN_MSG_TYPE_PUBREC: - case SN_MSG_TYPE_PUBREL: - case SN_MSG_TYPE_UNSUBACK: - /* octet 2-3 */ - MqttDecode_Num(rx_buf, p_packet_id); - break; - case SN_MSG_TYPE_SUBACK: - /* octet 5-6 */ - MqttDecode_Num(rx_buf + 3, p_packet_id); - break; - case SN_MSG_TYPE_ADVERTISE: - case SN_MSG_TYPE_SEARCHGW: - case SN_MSG_TYPE_GWINFO: - case SN_MSG_TYPE_CONNECT: - case SN_MSG_TYPE_CONNACK: - case SN_MSG_TYPE_WILLTOPICREQ: - case SN_MSG_TYPE_WILLTOPIC: - case SN_MSG_TYPE_WILLMSGREQ: - case SN_MSG_TYPE_WILLMSG: - case SN_MSG_TYPE_REGISTER: - case SN_MSG_TYPE_PUBLISH: - case SN_MSG_TYPE_SUBSCRIBE: - case SN_MSG_TYPE_UNSUBSCRIBE: - case SN_MSG_TYPE_PING_REQ: - case SN_MSG_TYPE_PING_RESP: - case SN_MSG_TYPE_DISCONNECT: - case SN_MSG_TYPE_WILLTOPICUPD: - case SN_MSG_TYPE_WILLTOPICRESP: - case SN_MSG_TYPE_WILLMSGUPD: - case SN_MSG_TYPE_WILLMSGRESP: - case SN_MSG_TYPE_ENCAPMSG: - case SN_MSG_TYPE_RESERVED: - default: - *p_packet_id = 0; - break; - } - } - - return (int)total_len; -} - -int SN_Decode_Advertise(byte *rx_buf, int rx_buf_len, SN_Advertise *gw_info) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - - /* Check message type */ - type = *rx_payload++; - if (total_len != 5) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - if (type != SN_MSG_TYPE_ADVERTISE) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - /* Decode gateway info */ - if (gw_info != NULL) { - gw_info->gwId = *rx_payload++; - - rx_payload += MqttDecode_Num(rx_payload, &gw_info->duration); - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_SearchGW(byte *tx_buf, int tx_buf_len, byte hops) -{ - int total_len; - byte *tx_payload = tx_buf; - - /* Packet length is not variable */ - total_len = 3; - - /* Validate required arguments */ - if (tx_buf == NULL || tx_buf_len < total_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Encode length */ - *tx_payload++ = total_len; - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_SEARCHGW; - - /* Encode radius */ - *tx_payload++ = hops; - (void)tx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_GWInfo(byte *rx_buf, int rx_buf_len, SN_GwInfo *gw_info) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len == SN_PACKET_LEN_IND) { - /* The length is stored in the next two bytes */ - rx_payload += MqttDecode_Num(rx_payload, (word16*)&total_len); - } - - if (total_len > rx_buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - if (total_len < 3) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - /* Check message type */ - type = *rx_payload++; - if (type != SN_MSG_TYPE_GWINFO) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - /* Decode gateway info */ - if (gw_info != NULL) { - gw_info->gwId = *rx_payload++; - - /* TODO: validate size of gwAddr */ - if (total_len - 3 > 0) { - /* The gateway address is only present if sent by a client */ - XMEMCPY(gw_info->gwAddr, rx_payload, total_len - 3); - } - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -/* Packet Type Encoders/Decoders */ -int SN_Encode_Connect(byte *tx_buf, int tx_buf_len, SN_Connect *mc_connect) -{ - word16 total_len, id_len; - byte flags = 0; - byte *tx_payload = tx_buf; - - /* Validate required arguments */ - if ((tx_buf == NULL) || (mc_connect == NULL) || - (mc_connect->client_id == NULL) || (mc_connect->protocol_level == 0)) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Determine packet length */ - total_len = 6; /* Len + Message Type + Flags + ProtocolID + Duration(2) */ - - /* Client ID size */ - id_len = (word16)XSTRLEN(mc_connect->client_id); - id_len = (id_len <= SN_CLIENTID_MAX_LEN) ? id_len : SN_CLIENTID_MAX_LEN; - - total_len += id_len; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - total_len += 2; /* Store len in three bytes */ - } - - if (total_len > tx_buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode length */ - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = (byte)total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_CONNECT; - - /* Encode flags */ - if (mc_connect->clean_session) { - flags |= SN_PACKET_FLAG_CLEANSESSION; - } - if (mc_connect->enable_lwt) { - flags |= SN_PACKET_FLAG_WILL; - } - *tx_payload++ = flags; - - /* Protocol version */ - *tx_payload++ = mc_connect->protocol_level; - - /* Encode duration (keep-alive) */ - tx_payload += MqttEncode_Num(tx_payload, mc_connect->keep_alive_sec); - - /* Encode Client ID */ - XMEMCPY(tx_payload, mc_connect->client_id, id_len); - tx_payload += id_len; - (void)tx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_WillTopicReq(byte *rx_buf, int rx_buf_len) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Length and MsgType */ - total_len = *rx_payload++; - if (total_len != 2) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_WILLTOPICREQ) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -/* An empty WILLTOPIC message is a WILLTOPIC message without Flags and - WillTopic field (i.e. it is exactly 2 octets long). It is used by a client - to delete the Will topic and the Will message stored in the server */ -int SN_Encode_WillTopic(byte *tx_buf, int tx_buf_len, SN_Will *willTopic) -{ - int total_len; - byte *tx_payload, flags = 0; - - /* Validate required arguments */ - if (tx_buf == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Length and MsgType */ - total_len = 2; - - /* Determine packet length */ - if (willTopic != NULL) { - /* Will Topic is a string */ - total_len += (int)XSTRLEN(willTopic->willTopic); - - /* Flags */ - total_len++; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode length */ - tx_payload = tx_buf; - - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_WILLTOPIC; - - if (willTopic != NULL) { - int will_len; - - /* Encode flags */ - flags |= ((willTopic->qos << SN_PACKET_FLAG_QOS_SHIFT) & - SN_PACKET_FLAG_QOS_MASK); - flags |= (willTopic->retain != 0) ? SN_PACKET_FLAG_RETAIN : 0; - *tx_payload++ = flags; - - /* Encode Will Topic */ - will_len = (int)XSTRLEN(willTopic->willTopic); - XMEMCPY(tx_payload, willTopic->willTopic, will_len); - tx_payload += will_len; - } - (void)tx_payload; - - return total_len; -} - -int SN_Decode_WillMsgReq(byte *rx_buf, int rx_buf_len) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - - /* Length and MsgType */ - if (total_len != 2){ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - /* Message Type */ - type = *rx_payload++; - if (type != SN_MSG_TYPE_WILLMSGREQ) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_WillMsg(byte *tx_buf, int tx_buf_len, SN_Will *willMsg) -{ - int total_len; - byte *tx_payload; - - /* Validate required arguments */ - if ((tx_buf == NULL) || (willMsg == NULL)) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Length and MsgType */ - total_len = 2; - - /* Determine packet length */ - /* Add Will Message len */ - total_len += willMsg->willMsgLen; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode length */ - tx_payload = tx_buf; - - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_WILLMSG; - - /* Encode Will Message */ - XMEMCPY(tx_payload, willMsg->willMsg, willMsg->willMsgLen); - tx_payload += willMsg->willMsgLen; - (void)tx_payload; - - return total_len; -} - -int SN_Encode_WillTopicUpdate(byte *tx_buf, int tx_buf_len, SN_Will *willTopic) -{ - int total_len; - byte *tx_payload, flags = 0; - - /* Validate required arguments */ - if (tx_buf == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Length and MsgType */ - total_len = 2; - - /* Determine packet length */ - if (willTopic != NULL) { - /* Will Topic is a string */ - total_len += (int)XSTRLEN(willTopic->willTopic); - - /* Flags */ - total_len++; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode length */ - tx_payload = tx_buf; - - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_WILLTOPICUPD; - - if (willTopic != NULL) { - int will_len; - - /* Encode flags */ - flags |= ((willTopic->qos << SN_PACKET_FLAG_QOS_SHIFT) & - SN_PACKET_FLAG_QOS_MASK); - flags |= (willTopic->retain != 0) ? SN_PACKET_FLAG_RETAIN : 0; - *tx_payload++ = flags; - - /* Encode Will Topic */ - will_len = (int)XSTRLEN(willTopic->willTopic); - XMEMCPY(tx_payload, willTopic->willTopic, will_len); - tx_payload += will_len; - } - (void)tx_payload; - - return total_len; - -} - -int SN_Decode_WillTopicResponse(byte *rx_buf, int rx_buf_len, byte *ret_code) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len != 3) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_WILLTOPICRESP) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - /* Return Code */ - *ret_code = *rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_WillMsgUpdate(byte *tx_buf, int tx_buf_len, SN_Will *willMsg) -{ - int total_len; - byte *tx_payload; - - /* Validate required arguments */ - if ((tx_buf == NULL) || (willMsg == NULL)) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Length and MsgType */ - total_len = 2; - - /* Determine packet length */ - /* Add Will Message len */ - total_len += willMsg->willMsgLen; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode length */ - tx_payload = tx_buf; - - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_WILLMSGUPD; - - /* Encode Will Message */ - XMEMCPY(tx_payload, willMsg->willMsg, willMsg->willMsgLen); - tx_payload += willMsg->willMsgLen; - (void)tx_payload; - - return total_len; -} - -int SN_Decode_WillMsgResponse(byte *rx_buf, int rx_buf_len, byte *ret_code) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len != 3) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_WILLMSGRESP) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - /* Return Code */ - *ret_code = *rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_ConnectAck(byte *rx_buf, int rx_buf_len, - SN_ConnectAck *connect_ack) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len != 3) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_CONNACK) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - /* Decode variable header */ - if (connect_ack) { - connect_ack->return_code = *rx_payload++; - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_Register(byte *tx_buf, int tx_buf_len, SN_Register *regist) -{ - int total_len, topic_len; - byte *tx_payload; - - /* Validate required arguments */ - if (tx_buf == NULL || regist == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Determine packet length */ - /* Topic name is a string */ - total_len = (int)XSTRLEN(regist->topicName); - - /* Length, MsgType, TopicID (2), and packet_id (2) */ - total_len += 6; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode length */ - tx_payload = tx_buf; - - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_REGISTER; - - /* Encode Topic ID */ - tx_payload += MqttEncode_Num(tx_payload, regist->topicId); - - /* Encode Packet ID */ - tx_payload += MqttEncode_Num(tx_payload, regist->packet_id); - - /* Encode Topic Name */ - topic_len = (int)XSTRLEN(regist->topicName); - XMEMCPY(tx_payload, regist->topicName, topic_len); - tx_payload += topic_len; - (void)tx_payload; - - return total_len; -} - -int SN_Decode_Register(byte *rx_buf, int rx_buf_len, SN_Register *regist) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len == SN_PACKET_LEN_IND) { - /* The length is stored in the next two bytes */ - rx_payload += MqttDecode_Num(rx_payload, (word16*)&total_len); - } - - if (total_len > rx_buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - if (total_len < 7) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - /* Check message type */ - type = *rx_payload++; - if (type != SN_MSG_TYPE_REGISTER) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - if (regist != NULL) { - /* Decode Topic ID assigned by GW */ - rx_payload += MqttDecode_Num(rx_payload, ®ist->topicId); - - /* Decode packet ID */ - rx_payload += MqttDecode_Num(rx_payload, ®ist->packet_id); - - /* Decode Topic Name */ - regist->topicName = (char*)rx_payload; - - /* Terminate the string */ - rx_payload[total_len-6] = '\0'; - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_RegAck(byte *tx_buf, int tx_buf_len, SN_RegAck *regack) -{ - int total_len; - byte *tx_payload; - - /* Validate required arguments */ - if (tx_buf == NULL || regack == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Determine packet length */ - /* Length, MsgType, TopicID (2), and MsgId (2), Return Code */ - total_len = 7; - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - tx_payload = tx_buf; - - /* Encode length */ - *tx_payload++ = total_len; - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_REGACK; - - /* Encode Topic ID */ - tx_payload += MqttEncode_Num(tx_payload, regack->topicId); - - /* Encode Message ID */ - tx_payload += MqttEncode_Num(tx_payload, regack->packet_id); - - /* Encode Return Code */ - *tx_payload += regack->return_code; - - (void)tx_payload; - - return total_len; -} - -int SN_Decode_RegAck(byte *rx_buf, int rx_buf_len, SN_RegAck *regack) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len != 7) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_REGACK) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - if (regack != NULL) { - /* Decode Topic ID assigned by GW */ - rx_payload += MqttDecode_Num(rx_payload, ®ack->topicId); - - /* Decode packet ID */ - rx_payload += MqttDecode_Num(rx_payload, ®ack->packet_id); - - /* Decode return code */ - regack->return_code = *rx_payload++; - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_Subscribe(byte *tx_buf, int tx_buf_len, SN_Subscribe *subscribe) -{ - int total_len; - byte *tx_payload, flags = 0x00; - - /* Validate required arguments */ - if (tx_buf == NULL || subscribe == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Determine packet length */ - if (subscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { - /* Topic name is a string */ - total_len = (int)XSTRLEN(subscribe->topicNameId); - } - else { - /* Topic ID or Short name */ - total_len = 2; - } - - /* Length, MsgType, Flags, and MsgID (2) */ - total_len += 5; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode length */ - tx_payload = tx_buf; - - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_SUBSCRIBE; - - /* Set flags */ - if (subscribe->duplicate) - flags |= SN_PACKET_FLAG_DUPLICATE; - flags |= (SN_PACKET_FLAG_QOS_MASK & - (subscribe->qos << SN_PACKET_FLAG_QOS_SHIFT)); - flags |= (SN_PACKET_FLAG_TOPICIDTYPE_MASK & subscribe->topic_type); - - *tx_payload++ = flags; - - /* Encode packet ID */ - tx_payload += MqttEncode_Num(tx_payload, subscribe->packet_id); - - /* Encode topic */ - if (subscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { - /* Topic name is a string */ - XMEMCPY(tx_payload, subscribe->topicNameId, XSTRLEN(subscribe->topicNameId)); - } - else { - /* Topic ID */ - XMEMCPY(tx_payload, subscribe->topicNameId, 2); - } - (void)tx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_SubscribeAck(byte* rx_buf, int rx_buf_len, - SN_SubAck *subscribe_ack) -{ - word16 total_len; - byte* rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len != 8) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_SUBACK) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Decode SubAck fields */ - if (subscribe_ack) { - subscribe_ack->flags = *rx_payload++; - rx_payload += MqttDecode_Num(rx_payload, &subscribe_ack->topicId); - rx_payload += MqttDecode_Num(rx_payload, &subscribe_ack->packet_id); - subscribe_ack->return_code = *rx_payload++; - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_Publish(byte *tx_buf, int tx_buf_len, SN_Publish *publish) -{ - word16 total_len; - byte *tx_payload = tx_buf; - byte flags = 0; - - /* Validate required arguments */ - if (tx_buf == NULL || publish == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Determine packet length */ - total_len = publish->total_len; - - /* Add length, msgType, flags, topic ID (2), and msgID (2) */ - total_len += 7; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode header */ - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = (byte)total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - *tx_payload++ = SN_MSG_TYPE_PUBLISH; - - /* Set flags */ - if (publish->duplicate) - flags |= SN_PACKET_FLAG_DUPLICATE; - flags |= (SN_PACKET_FLAG_QOS_MASK & - (publish->qos << SN_PACKET_FLAG_QOS_SHIFT)); - if (publish->retain) - flags |= SN_PACKET_FLAG_RETAIN; - flags |= (SN_PACKET_FLAG_TOPICIDTYPE_MASK & publish->topic_type); - - *tx_payload++ = flags; - - /* Encode topic */ - if ((publish->topic_type == SN_TOPIC_ID_TYPE_SHORT) || - (publish->topic_type == SN_TOPIC_ID_TYPE_PREDEF)) { - /* Short and predefined topic names are 2 chars */ - XMEMCPY(tx_payload, publish->topic_name, 2); - tx_payload += 2; - } - else { - /* Topic ID */ - tx_payload += MqttEncode_Num(tx_payload, *(word16*)publish->topic_name); - } - - tx_payload += MqttEncode_Num(tx_payload, publish->packet_id); - - /* Encode payload */ - XMEMCPY(tx_payload, publish->buffer, publish->total_len); - tx_payload += publish->total_len; - - (void)tx_payload; - - /* Return length of packet placed into tx_buf */ - return total_len; -} - -int SN_Decode_Publish(byte *rx_buf, int rx_buf_len, SN_Publish *publish) -{ - word16 total_len; - byte *rx_payload = rx_buf; - byte flags = 0, type; - - /* Validate required arguments */ - if (rx_buf == NULL || publish == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len == SN_PACKET_LEN_IND) { - /* The length is stored in the next two bytes */ - rx_payload += MqttDecode_Num(rx_payload, &total_len); - } - - if (total_len > rx_buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - if (total_len < 7) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - /* Message Type */ - type = *rx_payload++; - if (type != SN_MSG_TYPE_PUBLISH) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - flags = *rx_payload++; - - publish->topic_name = (char*)rx_payload; - rx_payload += MQTT_DATA_LEN_SIZE; - publish->topic_name_len = MQTT_DATA_LEN_SIZE; - - rx_payload += MqttDecode_Num(rx_payload, &publish->packet_id); - - /* Set flags */ - publish->duplicate = flags & SN_PACKET_FLAG_DUPLICATE; - - publish->qos = (MqttQoS)((flags & SN_PACKET_FLAG_QOS_MASK) >> - SN_PACKET_FLAG_QOS_SHIFT); - - publish->retain = flags & SN_PACKET_FLAG_RETAIN; - - publish->topic_type = flags & SN_PACKET_FLAG_TOPICIDTYPE_MASK; - - /* Decode payload */ - - publish->total_len = total_len - 7; - publish->buffer = rx_payload; - publish->buffer_pos = 0; - publish->buffer_len = publish->total_len; - - /* Return length of packet read from rx_buf */ - return total_len; -} - -int SN_Encode_PublishResp(byte* tx_buf, int tx_buf_len, byte type, - SN_PublishResp *publish_resp) -{ - int total_len; - byte *tx_payload = tx_buf; - - /* Validate required arguments */ - if (tx_buf == NULL || publish_resp == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Determine packet length */ - total_len = (type == SN_MSG_TYPE_PUBACK) ? 7 : 4; - - if (total_len > tx_buf_len) - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - - /* Encode */ - *tx_payload++ = (byte)total_len; - - *tx_payload++ = type; - - if (type == SN_MSG_TYPE_PUBACK) { - tx_payload += MqttEncode_Num(tx_payload, publish_resp->topicId); - } - - tx_payload += MqttEncode_Num(tx_payload, publish_resp->packet_id); - - if (type == SN_MSG_TYPE_PUBACK) { - *tx_payload++ = publish_resp->return_code; - } - (void)tx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_PublishResp(byte* rx_buf, int rx_buf_len, byte type, - SN_PublishResp *publish_resp) -{ - int total_len; - byte rec_type, *rx_payload = rx_buf; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode */ - total_len = *rx_payload++; - - if(total_len > rx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Validate packet type */ - rec_type = *rx_payload++; - if (rec_type != type) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - if (publish_resp) { - if (type == SN_MSG_TYPE_PUBACK) { - rx_payload += MqttDecode_Num(rx_payload, &publish_resp->topicId); - } - - rx_payload += MqttDecode_Num(rx_payload, &publish_resp->packet_id); - - if (type == SN_MSG_TYPE_PUBACK) { - publish_resp->return_code = *rx_payload++; - } - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_Unsubscribe(byte *tx_buf, int tx_buf_len, - SN_Unsubscribe *unsubscribe) -{ - int total_len; - byte *tx_payload, flags = 0x00; - - /* Validate required arguments */ - if (tx_buf == NULL || unsubscribe == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Determine packet length */ - if (unsubscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { - /* Topic name is a string */ - total_len = (int)XSTRLEN(unsubscribe->topicNameId); - } - else { - /* Topic ID or Short name */ - total_len = 2; - } - - /* Length, MsgType, Flags, and MsgID (2) */ - total_len += 5; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode header */ - tx_payload = tx_buf; - - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - *tx_payload++ = SN_MSG_TYPE_UNSUBSCRIBE; - - /* Set flags */ - if (unsubscribe->duplicate) - flags |= SN_PACKET_FLAG_DUPLICATE; - flags |= (SN_PACKET_FLAG_QOS_MASK & - (unsubscribe->qos << SN_PACKET_FLAG_QOS_SHIFT)); - flags |= (SN_PACKET_FLAG_TOPICIDTYPE_MASK & unsubscribe->topic_type); - - *tx_payload++ = flags; - - tx_payload += MqttEncode_Num(tx_payload, unsubscribe->packet_id); - - /* Encode topic */ - if (unsubscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { - /* Topic name is a string */ - XMEMCPY(tx_payload, unsubscribe->topicNameId, - XSTRLEN(unsubscribe->topicNameId)); - } - else { - /* Topic ID or Short name */ - XMEMCPY(tx_payload, unsubscribe->topicNameId, 2); - } - - (void)tx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_UnsubscribeAck(byte *rx_buf, int rx_buf_len, - SN_UnsubscribeAck *unsubscribe_ack) -{ - word16 total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len != 4) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_UNSUBACK) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - /* Decode SubAck fields */ - if (unsubscribe_ack) { - rx_payload += MqttDecode_Num(rx_payload, &unsubscribe_ack->packet_id); - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_Disconnect(byte *tx_buf, int tx_buf_len, - SN_Disconnect* disconnect) -{ - int total_len = 2; /* length and message type */ - byte *tx_payload = tx_buf;; - - /* Validate required arguments */ - if (tx_buf == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if ((disconnect != NULL) && (disconnect->sleepTmr > 0)) { - total_len += 2; /* Sleep duration is set */ - } - - if (total_len > tx_buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode message */ - *tx_payload++ = total_len; - - *tx_payload++ = SN_MSG_TYPE_DISCONNECT; - - if ((disconnect != NULL) && (disconnect->sleepTmr > 0)) { - tx_payload += MqttEncode_Num(tx_payload, disconnect->sleepTmr); - } - (void)tx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_Disconnect(byte *rx_buf, int rx_buf_len) -{ - word16 total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len != 2) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_DISCONNECT) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_Ping(byte *tx_buf, int tx_buf_len, SN_PingReq *ping, byte type) -{ - int total_len = 2, clientId_len = 0; - byte *tx_payload = tx_buf; - - /* Validate required arguments */ - if ((tx_buf == NULL) || - ((type != SN_MSG_TYPE_PING_REQ) && (type != SN_MSG_TYPE_PING_RESP))) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if ((type == SN_MSG_TYPE_PING_REQ) && (ping != NULL) && - (ping->clientId != NULL)) { - total_len += clientId_len = (int)XSTRLEN(ping->clientId); - } - - if (total_len > tx_buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - *tx_payload++ = (byte)total_len; - - *tx_payload++ = type; - - if (clientId_len > 0) { - XMEMCPY(tx_payload, ping->clientId, clientId_len); - tx_payload += clientId_len; - } - (void)tx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_Ping(byte *rx_buf, int rx_buf_len) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - total_len = *rx_payload++; - if (total_len != 2) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if ((type != SN_MSG_TYPE_PING_REQ) && - (type != SN_MSG_TYPE_PING_RESP)) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - /* Return total length of packet */ - return total_len; -} - -/* Read return code is length when > 0 */ -int SN_Packet_Read(MqttClient *client, byte* rx_buf, int rx_buf_len, - int timeout_ms) -{ - int rc, len = 0, remain_read = 0; - word16 total_len = 0, idx = 0; - - switch (client->packet.stat) - { - case MQTT_PK_BEGIN: - { - /* Read first 2 bytes */ - if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { - rc = MqttSocket_Read(client, rx_buf, 2, timeout_ms); - } else { - rc = MqttSocket_Peek(client, rx_buf, 2, timeout_ms); - } - if (rc < 0) { - return MqttPacket_HandleNetError(client, rc); - } - else if (rc != 2) { - return MqttPacket_HandleNetError(client, - MQTT_TRACE_ERROR(MQTT_CODE_ERROR_NETWORK)); - } - - len = rc; - - if (rx_buf[0] == SN_PACKET_LEN_IND){ - /* Read length stored in first three bytes, type in fourth */ - if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { - rc = MqttSocket_Read(client, rx_buf+len, 2, timeout_ms); - if (rc < 0) { - return MqttPacket_HandleNetError(client, rc); - } - else if (rc != 2) { - return MqttPacket_HandleNetError(client, - MQTT_TRACE_ERROR(MQTT_CODE_ERROR_NETWORK)); - } - rc += len; - } - else { - rc = MqttSocket_Peek(client, rx_buf, 4, timeout_ms); - if (rc < 0) { - return MqttPacket_HandleNetError(client, rc); - } - else if (rc != 4) { - return MqttPacket_HandleNetError(client, - MQTT_TRACE_ERROR(MQTT_CODE_ERROR_NETWORK)); - } - len = rc; - } - - (void)MqttDecode_Num(&rx_buf[1], &total_len); - client->packet.header_len = len; - } - else { - /* Length is stored in first byte, type in second */ - total_len = rx_buf[0]; - client->packet.header_len = len; - } - } - FALL_THROUGH; - - case MQTT_PK_READ_HEAD: - { - client->packet.stat = MQTT_PK_READ_HEAD; - } - FALL_THROUGH; - - case MQTT_PK_READ: - { - client->packet.stat = MQTT_PK_READ; - - if (total_len > len) { - client->packet.remain_len = total_len - len; - } - else if ((total_len == 2) || (total_len == 4)) { - /* Handle peek */ - if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { - client->packet.remain_len = total_len - len; - } - else { - client->packet.remain_len = total_len; - } - } - else { - client->packet.remain_len = 0; - } - - /* Make sure it does not overflow rx_buf */ - if (client->packet.remain_len > - (rx_buf_len - client->packet.header_len)) { - client->packet.remain_len = rx_buf_len - - client->packet.header_len; - } - if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { - total_len -= client->packet.header_len; - idx = client->packet.header_len; - } - /* Read whole message */ - if (client->packet.remain_len > 0) { - rc = MqttSocket_Read(client, &rx_buf[idx], - total_len, timeout_ms); - if (rc <= 0) { - return MqttPacket_HandleNetError(client, rc); - } - remain_read = rc; - } - if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { - remain_read += client->packet.header_len; - } - - break; - } - } /* switch (client->packet.stat) */ - - /* reset state */ - client->packet.stat = MQTT_PK_BEGIN; - - /* Return read length */ - return remain_read; -} - -#endif /* WOLFMQTT_SN */ diff --git a/src/mqtt_sn_client.c b/src/mqtt_sn_client.c new file mode 100644 index 000000000..40b7c20c4 --- /dev/null +++ b/src/mqtt_sn_client.c @@ -0,0 +1,1872 @@ +/* mqtt_sn_client.c + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Include the autoconf generated config.h */ +#ifdef HAVE_CONFIG_H + #include +#endif + +#include "wolfmqtt/mqtt_sn_client.h" + +#ifdef WOLFMQTT_SN + +/* Private functions */ +static int SN_Client_HandlePacket(MqttClient* client, SN_MsgType packet_type, + void* packet_obj, int timeout) +{ + int rc = MQTT_CODE_SUCCESS; + word16 packet_id = 0; + + (void)timeout; + + switch ((int)packet_type) + { + case SN_MSG_TYPE_GWINFO: + { + SN_GwInfo info, *p_info = &info; + if (packet_obj) { + p_info = (SN_GwInfo*)packet_obj; + } + else { + XMEMSET(p_info, 0, sizeof(SN_GwInfo)); + } + + rc = SN_Decode_GWInfo(client->rx_buf, client->packet.buf_len, + p_info); + if (rc <= 0) { + return rc; + } + break; + } + case SN_MSG_TYPE_CONNACK: + { + /* Decode connect ack */ + SN_ConnectAck connect_ack, *p_connect_ack = &connect_ack; + if (packet_obj) { + p_connect_ack = (SN_ConnectAck*)packet_obj; + } + else { + XMEMSET(p_connect_ack, 0, sizeof(SN_ConnectAck)); + } + p_connect_ack->return_code = + client->rx_buf[client->packet.buf_len-1]; + + break; + } + case SN_MSG_TYPE_WILLTOPICREQ: + { + rc = SN_Decode_WillTopicReq(client->rx_buf, client->packet.buf_len); + break; + } + case SN_MSG_TYPE_WILLMSGREQ: + { + rc = SN_Decode_WillMsgReq(client->rx_buf, client->packet.buf_len); + break; + } + case SN_MSG_TYPE_REGISTER: + { + /* Decode register */ + SN_Register reg_s; + + XMEMSET(®_s, 0, sizeof(SN_Register)); + + rc = SN_Decode_Register(client->rx_buf, client->packet.buf_len, + ®_s); + + if (rc > 0) { + /* Initialize the regack */ + reg_s.regack.packet_id = reg_s.packet_id; + reg_s.regack.topicId = reg_s.topicId; + reg_s.regack.return_code = SN_RC_NOTSUPPORTED; + + /* Call the register callback to allow app to + handle new topic ID assignment. */ + if (client->reg_cb != NULL) { + rc = client->reg_cb(reg_s.topicId, + reg_s.topicName, client->reg_ctx); + /* Set the regack return code */ + reg_s.regack.return_code = (rc >= 0) ? SN_RC_ACCEPTED : + SN_RC_INVTOPICNAME; + } + + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the register acknowledgment */ + rc = SN_Encode_RegAck(client->tx_buf, client->tx_buf_len, + ®_s.regack); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_REGACK), + SN_MSG_TYPE_REGACK, reg_s.packet_id); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + /* Send regack packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + } + + break; + } + case SN_MSG_TYPE_REGACK: + { + /* Decode register ack */ + SN_RegAck regack_s, *p_regack = ®ack_s; + if (packet_obj) { + p_regack = (SN_RegAck*)packet_obj; + } + else { + XMEMSET(p_regack, 0, sizeof(SN_RegAck)); + } + + rc = SN_Decode_RegAck(client->rx_buf, client->packet.buf_len, + p_regack); + if (rc > 0) { + packet_id = p_regack->packet_id; + } + + break; + } + case SN_MSG_TYPE_PUBLISH: + { + SN_Publish pub, *p_pub = &pub; + if (packet_obj) { + p_pub = (SN_Publish*)packet_obj; + } + else { + XMEMSET(p_pub, 0, sizeof(SN_Publish)); + } + + /* Decode publish message */ + rc = SN_Decode_Publish(client->rx_buf, client->packet.buf_len, + p_pub); + if (rc <= 0) { + return rc; + } + + /* Issue callback for new message */ + if (client->msg_cb) { + /* if using the temp publish message buffer, + then populate message context with client context */ + if (&client->msgSN.publish == p_pub) + p_pub->ctx = client->ctx; + rc = client->msg_cb(client, (MqttMessage*)p_pub, 1, 1); + if (rc != MQTT_CODE_SUCCESS) { + return rc; + }; + } + + /* Handle Qos */ + if (p_pub->qos > MQTT_QOS_0) { + SN_MsgType type; + + packet_id = p_pub->packet_id; + + /* Determine packet type to write */ + type = (p_pub->qos == MQTT_QOS_1) ? + SN_MSG_TYPE_PUBACK : + SN_MSG_TYPE_PUBREC; + p_pub->resp.packet_id = packet_id; + + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode publish response */ + rc = SN_Encode_PublishResp(client->tx_buf, + client->tx_buf_len, type, &p_pub->resp); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d," + " QoS %d", + rc, SN_Packet_TypeDesc(type), type, packet_id, + p_pub->qos); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + /* Send packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + } + break; + } + case SN_MSG_TYPE_PUBACK: + case SN_MSG_TYPE_PUBCOMP: + case SN_MSG_TYPE_PUBREC: + case SN_MSG_TYPE_PUBREL: + { + SN_PublishResp publish_resp, *p_publish_resp = &publish_resp; + if (packet_obj) { + p_publish_resp = (SN_PublishResp*)packet_obj; + } + else { + XMEMSET(p_publish_resp, 0, sizeof(SN_PublishResp)); + } + + /* Decode publish response message */ + rc = SN_Decode_PublishResp(client->rx_buf, client->packet.buf_len, + packet_type, p_publish_resp); + if (rc <= 0) { + return rc; + } + packet_id = p_publish_resp->packet_id; + + /* If Qos then send response */ + if (packet_type == SN_MSG_TYPE_PUBREC || + packet_type == SN_MSG_TYPE_PUBREL) { + + byte resp_type = (packet_type == SN_MSG_TYPE_PUBREC) ? + SN_MSG_TYPE_PUBREL : SN_MSG_TYPE_PUBCOMP; + + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode publish response */ + p_publish_resp->packet_id = packet_id; + rc = SN_Encode_PublishResp(client->tx_buf, + client->tx_buf_len, resp_type, p_publish_resp); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d", + rc, MqttPacket_TypeDesc(resp_type), resp_type, packet_id); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + /* Send packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + } + break; + } + case SN_MSG_TYPE_SUBACK: + { + /* Decode subscribe ack */ + SN_SubAck subscribe_ack, *p_subscribe_ack = &subscribe_ack; + if (packet_obj) { + p_subscribe_ack = (SN_SubAck*)packet_obj; + } + else { + XMEMSET(p_subscribe_ack, 0, sizeof(SN_SubAck)); + } + + rc = SN_Decode_SubscribeAck(client->rx_buf, client->packet.buf_len, + p_subscribe_ack); + if (rc <= 0) { + return rc; + } + packet_id = p_subscribe_ack->packet_id; + + break; + } + case SN_MSG_TYPE_UNSUBACK: + { + /* Decode unsubscribe ack */ + SN_UnsubscribeAck unsubscribe_ack, + *p_unsubscribe_ack = &unsubscribe_ack; + if (packet_obj) { + p_unsubscribe_ack = (SN_UnsubscribeAck*)packet_obj; + } + else { + XMEMSET(p_unsubscribe_ack, 0, sizeof(SN_UnsubscribeAck)); + } + rc = SN_Decode_UnsubscribeAck(client->rx_buf, + client->packet.buf_len, p_unsubscribe_ack); + if (rc <= 0) { + return rc; + } + packet_id = p_unsubscribe_ack->packet_id; + + break; + } + case SN_MSG_TYPE_PING_RESP: + { + /* Decode ping */ + rc = SN_Decode_Ping(client->rx_buf, client->packet.buf_len); + break; + } + case SN_MSG_TYPE_PING_REQ: + { + /* Decode ping */ + rc = SN_Decode_Ping(client->rx_buf, client->packet.buf_len); + if (rc <= 0) { return rc; } + + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the ping packet as a response */ + rc = SN_Encode_Ping(client->tx_buf, client->tx_buf_len, NULL, + SN_MSG_TYPE_PING_RESP); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_PING_RESP), + SN_MSG_TYPE_PING_RESP); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + /* Send ping resp packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + break; + } + case SN_MSG_TYPE_WILLTOPICRESP: + { + /* Decode Will Topic Response */ + SN_WillTopicResp resp_s, *resp = &resp_s; + if (packet_obj) { + resp = (SN_WillTopicResp*)packet_obj; + } + else { + XMEMSET(resp, 0, sizeof(SN_WillTopicResp)); + } + rc = SN_Decode_WillTopicResponse(client->rx_buf, + client->packet.buf_len, &resp->return_code); + break; + } + case SN_MSG_TYPE_WILLMSGRESP: + { + /* Decode Will Message Response */ + SN_WillMsgResp resp_s, *resp = &resp_s; + if (packet_obj) { + resp = (SN_WillMsgResp*)packet_obj; + } + else { + XMEMSET(resp, 0, sizeof(SN_WillMsgResp)); + } + rc = SN_Decode_WillMsgResponse(client->rx_buf, + client->packet.buf_len, &resp->return_code); + break; + } + case SN_MSG_TYPE_DISCONNECT: + { + SN_Disconnect disc_s, *disc = &disc_s; + if (packet_obj) { + disc = (SN_Disconnect*)packet_obj; + } + else { + XMEMSET(disc, 0, sizeof(SN_Disconnect)); + } + /* Decode Disconnect */ + rc = SN_Decode_Disconnect(client->rx_buf, client->packet.buf_len); + +#ifdef WOLFMQTT_DISCONNECT_CB + /* Call disconnect callback to allow handling broker disconnect */ + if ((client->disconnect_cb != NULL) && (disc->sleepTmr == 0)) { + client->disconnect_cb(client, rc, client->disconnect_ctx); + } +#endif + break; + } + + default: + { + /* Other types are server side only, ignore */ + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("SN_Client_HandlePacket: Invalid client packet type %u!", + packet_type); + #endif + break; + } + } /* switch (packet_type) */ + + (void)packet_id; + + return rc; +} + +static int SN_Client_WaitType(MqttClient *client, void* packet_obj, + byte wait_type, word16 wait_packet_id, int timeout_ms) +{ + int rc; + word16 packet_id; + SN_MsgType packet_type; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp *pendResp; +#endif + MqttMsgStat* mms_stat; + int waitMatchFound; + void* use_packet_obj = NULL; + + if (client == NULL || packet_obj == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* all packet type structures must have MqttMsgStat at top */ + mms_stat = (MqttMsgStat*)packet_obj; + +wait_again: + + /* initialize variables */ + packet_id = 0; + packet_type = SN_MSG_TYPE_RESERVED; +#ifdef WOLFMQTT_MULTITHREAD + pendResp = NULL; +#endif + waitMatchFound = 0; + +#ifdef WOLFMQTT_DEBUG_CLIENT + #ifdef WOLFMQTT_NONBLOCK + if (client->lastRc != MQTT_CODE_CONTINUE) + #endif + { + PRINTF("SN_Client_WaitType: Type %s (%d), ID %d", + SN_Packet_TypeDesc((SN_MsgType)wait_type), + wait_type, wait_packet_id); + } +#endif + + switch (mms_stat->read) + { + case MQTT_MSG_BEGIN: + { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock recv socket mutex */ + rc = wm_SemLock(&client->lockRecv); + if (rc != 0) { + PRINTF("SN_Client_WaitType recv lock error"); + return rc; + } + mms_stat->isReadLocked = 1; + MQTT_TRACE_MSG("SN lockRecv"); + #endif + + /* reset the packet state used by SN_Packet_Read */ + client->packet.stat = MQTT_PK_BEGIN; + } + FALL_THROUGH; + + case MQTT_MSG_WAIT: + { + #ifdef WOLFMQTT_MULTITHREAD + /* Check to see if packet type and id have already completed */ + pendResp = NULL; + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + if (MqttClient_RespList_Find(client, (MqttPacketType)wait_type, + wait_packet_id, &pendResp)) { + if (pendResp->packetDone) { + /* pending response is already done, so return */ + rc = pendResp->packet_ret; + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("PendResp already Done %p: Rc %d", pendResp, rc); + #endif + MqttClient_RespList_Remove(client, pendResp); + wm_SemUnlock(&client->lockClient); + MQTT_TRACE_MSG("SN unlockRecv"); + wm_SemUnlock(&client->lockRecv); + return rc; + } + } + wm_SemUnlock(&client->lockClient); + } + else { + break; /* error */ + } + #endif /* WOLFMQTT_MULTITHREAD */ + + mms_stat->read = MQTT_MSG_WAIT; + + /* Wait for packet */ + rc = SN_Packet_Read(client, client->rx_buf, client->rx_buf_len, + timeout_ms); + if (rc <= 0) { + break; + } + + client->packet.buf_len = rc; + + /* Decode header */ + rc = SN_Decode_Header(client->rx_buf, client->packet.buf_len, + &packet_type, &packet_id); + if (rc < 0) { + break; + } + + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("Read Packet: Len %d, Type %d, ID %d", + client->packet.buf_len, packet_type, packet_id); + #endif + + mms_stat->read = MQTT_MSG_HEADER; + } + FALL_THROUGH; + + case MQTT_MSG_HEADER: + case MQTT_MSG_PAYLOAD: + case MQTT_MSG_PAYLOAD2: + { + SN_MsgType use_packet_type; + + /* Determine if we received data for this request */ + if ((wait_type == SN_MSG_TYPE_ANY || wait_type == packet_type) && + (wait_packet_id == 0 || wait_packet_id == packet_id)) + { + use_packet_obj = packet_obj; + waitMatchFound = 1; + } + else { + /* use generic packet object */ + use_packet_obj = &client->msgSN; + } + use_packet_type = packet_type; + + #ifdef WOLFMQTT_MULTITHREAD + /* Check to see if we have a pending response for this packet */ + pendResp = NULL; + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + if (MqttClient_RespList_Find(client, + (MqttPacketType)packet_type, packet_id, &pendResp)) { + /* we found packet match this incoming read packet */ + pendResp->packetProcessing = 1; + if (pendResp->packet_obj != packet_obj) { + use_packet_obj = pendResp->packet_obj; + use_packet_type = (SN_MsgType)pendResp->packet_type; + /* req from another thread... not a match */ + waitMatchFound = 0; + } + } + wm_SemUnlock(&client->lockClient); + } + else { + break; /* error */ + } + #endif /* WOLFMQTT_MULTITHREAD */ + + rc = SN_Client_HandlePacket(client, use_packet_type, use_packet_obj, + timeout_ms); + + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) { + break; + } + #endif + + /* handle success case */ + if (rc >= 0) { + rc = MQTT_CODE_SUCCESS; + } + + #ifdef WOLFMQTT_MULTITHREAD + if (pendResp) { + /* Mark pending response entry done */ + if (wm_SemLock(&client->lockClient) == 0) { + pendResp->packetDone = 1; + pendResp->packet_ret = rc; + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("PendResp Marked Done %p", pendResp); + #endif + pendResp = NULL; + wm_SemUnlock(&client->lockClient); + } + } + #endif /* WOLFMQTT_MULTITHREAD */ + break; + } + + case MQTT_MSG_ACK: /* ack handled in SN_Client_HandlePacket */ + case MQTT_MSG_AUTH: + default: + { + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("SN_Client_WaitType: Invalid state %d!", mms_stat->read); + #endif + rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_STAT); + break; + } + } /* switch (msg->stat) */ + +#ifdef WOLFMQTT_DEBUG_CLIENT + if (rc != MQTT_CODE_CONTINUE) { + PRINTF("SN_Client_WaitType: rc %d, state %d", rc, mms_stat->read); + } +#endif + + if (mms_stat->read == MQTT_MSG_WAIT || rc != MQTT_CODE_CONTINUE) { + /* reset state */ + mms_stat->read = MQTT_MSG_BEGIN; + + #ifdef WOLFMQTT_MULTITHREAD + if (mms_stat->isReadLocked) { + mms_stat->isReadLocked = 0; + wm_SemUnlock(&client->lockRecv); + } + #endif + } + +#ifdef WOLFMQTT_NONBLOCK + #ifdef WOLFMQTT_DEBUG_CLIENT + client->lastRc = rc; + #endif + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } +#endif + + /* Clear shared union for next call */ + if ((MqttObject*)use_packet_obj == &client->msg) { + /* reset the members, but not the stat */ + XMEMSET(((byte*)&client->msg.stat) + sizeof(client->msg.stat), 0, + sizeof(client->msg)-sizeof(client->msg.stat)); + } + + if (rc < 0) { + #ifdef WOLFMQTT_DEBUG_CLIENT + if (rc != MQTT_CODE_CONTINUE) { + PRINTF("SN_Client_WaitType: Failure: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + #endif + return rc; + } + + if (!waitMatchFound) { + /* if we get here, then the we are still waiting for a packet */ + goto wait_again; + } + + return rc; +} + +/* Public Functions */ + +int SN_Client_SetRegisterCallback(MqttClient *client, + SN_ClientRegisterCb regCb, + void* ctx) +{ + int rc = MQTT_CODE_SUCCESS; + + if (client == NULL) + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + +#ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { +#endif + + client->reg_cb = regCb; + client->reg_ctx = ctx; + +#ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockClient); + } +#endif + + return rc; +} + +int SN_Client_SearchGW(MqttClient *client, SN_SearchGw *search) +{ + int rc; + + /* Validate required arguments */ + if (client == NULL || search == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (search->stat.write == MQTT_MSG_BEGIN) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the search packet */ + rc = SN_Encode_SearchGW(client->tx_buf, client->tx_buf_len, + search->radius); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_SEARCHGW), + SN_MSG_TYPE_SEARCHGW); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_GWINFO, 0, + &search->pendResp, &search->gwInfo); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send search for gateway packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &search->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + return rc; + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + search->stat.write = MQTT_MSG_WAIT; + } + + /* Wait for gateway info packet */ + rc = SN_Client_WaitType(client, &search->gwInfo, SN_MSG_TYPE_GWINFO, 0, + client->cmd_timeout_ms); +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &search->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + /* reset state */ + search->stat.write = MQTT_MSG_BEGIN; + + return rc; +} + +static int SN_WillTopic(MqttClient *client, SN_Will *will) +{ + int rc; + + /* Validate required arguments */ + if (client == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + +#ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_WILLTOPICREQ, 0, + &will->pendResp, &will->resp.topicResp); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + return rc; /* Error locking client */ + } +#endif + + /* Wait for Will Topic Request packet */ + rc = SN_Client_WaitType(client, will, + SN_MSG_TYPE_WILLTOPICREQ, 0, client->cmd_timeout_ms); +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif + +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &will->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + if (rc == 0) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode Will Topic */ + rc = SN_Encode_WillTopic(client->tx_buf, client->tx_buf_len, + will); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLTOPIC), + SN_MSG_TYPE_WILLTOPIC); + #endif + if (rc > 0) { + /* Send Will Topic packet */ + client->write.len = rc; + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc == client->write.len) { + rc = 0; + } + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + } + + return rc; +} + +static int SN_WillMessage(MqttClient *client, SN_Will *will) +{ + int rc; + + /* Validate required arguments */ + if (client == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + +#ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_WILLMSGREQ, 0, + &will->pendResp, &will->resp.msgResp); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + return rc; /* Error locking client */ + } +#endif + + /* Wait for Will Message Request */ + rc = SN_Client_WaitType(client, &will->resp.msgResp, + SN_MSG_TYPE_WILLMSGREQ, 0, client->cmd_timeout_ms); + +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif + +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &will->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + if (rc == 0) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + /* Encode Will Message */ + rc = SN_Encode_WillMsg(client->tx_buf, + client->tx_buf_len, will); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLMSG), + SN_MSG_TYPE_WILLMSG); + #endif + if (rc > 0) { + /* Send Will Topic packet */ + client->write.len = rc; + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc == client->write.len) { + rc = 0; + } + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + } + + return rc; +} + +int SN_Client_Connect(MqttClient *client, SN_Connect *mc_connect) +{ + int rc = 0; + static byte will_done; + + /* Validate required arguments */ + if ((client == NULL) || (mc_connect == NULL)) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (mc_connect->stat.write == MQTT_MSG_BEGIN) { + + will_done = 0; + + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the connect packet */ + rc = SN_Encode_Connect(client->tx_buf, client->tx_buf_len, mc_connect); +#ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d, QoS %d", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_CONNECT), + SN_MSG_TYPE_CONNECT, 0, 0); +#endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_CONNACK, 0, + &mc_connect->pendResp, &mc_connect->ack); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send connect packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + mc_connect->stat.write = MQTT_MSG_WAIT; + } + + if ((mc_connect->enable_lwt == 1) && (will_done != 1)) { + /* If the will is enabled, then the gateway requests the topic and + message in separate packets. */ + rc = SN_WillTopic(client, &mc_connect->will); + if (rc != 0) { + return rc; + } + + rc = SN_WillMessage(client, &mc_connect->will); + if (rc != 0) { + return rc; + } + will_done = 1; + } + + /* Wait for connect ack packet */ + rc = SN_Client_WaitType(client, &mc_connect->ack, + SN_MSG_TYPE_CONNACK, 0, client->cmd_timeout_ms); +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif + +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &mc_connect->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + /* reset state */ + mc_connect->stat.write = MQTT_MSG_BEGIN; + + return rc; +} + +int SN_Client_WillTopicUpdate(MqttClient *client, SN_Will *will) +{ + int rc = 0; + + /* Validate required arguments */ + if (client == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (will->stat.write == MQTT_MSG_BEGIN) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode Will Topic Update */ + rc = SN_Encode_WillTopicUpdate(client->tx_buf, + client->tx_buf_len, will); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLTOPICUPD), + SN_MSG_TYPE_WILLTOPICUPD); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_WILLTOPICRESP, + 0, &will->pendResp, &will->resp.topicResp); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send Will Topic Update packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &will->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + will->stat.write = MQTT_MSG_WAIT; + } + + /* Wait for Will Topic Update Response packet */ + rc = SN_Client_WaitType(client, &will->resp.topicResp, + SN_MSG_TYPE_WILLTOPICRESP, 0, client->cmd_timeout_ms); +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &will->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + /* reset state */ + will->stat.write = MQTT_MSG_BEGIN; + + return rc; +} + +int SN_Client_WillMsgUpdate(MqttClient *client, SN_Will *will) +{ + int rc = 0; + + /* Validate required arguments */ + if ((client == NULL) || (will == NULL)) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (will->stat.write == MQTT_MSG_BEGIN) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + /* Encode Will Message Update */ + rc = SN_Encode_WillMsgUpdate(client->tx_buf, + client->tx_buf_len, will); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLTOPICUPD), + SN_MSG_TYPE_WILLTOPICUPD); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_WILLMSGRESP, + 0, &will->pendResp, &will->resp.msgResp); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send Will Message Update packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &will->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + will->stat.write = MQTT_MSG_WAIT; + } + + /* Wait for Will Message Update Response packet */ + rc = SN_Client_WaitType(client, &will->resp.msgResp, + SN_MSG_TYPE_WILLMSGRESP, 0, client->cmd_timeout_ms); +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &will->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + /* reset state */ + will->stat.write = MQTT_MSG_BEGIN; + + return rc; + +} + +int SN_Client_Subscribe(MqttClient *client, SN_Subscribe *subscribe) +{ + int rc = -1; + + /* Validate required arguments */ + if (client == NULL || subscribe == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (subscribe->stat.write == MQTT_MSG_BEGIN) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the subscribe packet */ + rc = SN_Encode_Subscribe(client->tx_buf, client->tx_buf_len, + subscribe); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), QoS %d", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_SUBSCRIBE), + SN_MSG_TYPE_SUBSCRIBE, subscribe->qos); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_SUBACK, subscribe->packet_id, + &subscribe->pendResp, &subscribe->subAck); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send subscribe packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &subscribe->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + return rc; + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + subscribe->stat.write = MQTT_MSG_WAIT; + } + + /* Wait for subscribe ack packet */ + rc = SN_Client_WaitType(client, &subscribe->subAck, + SN_MSG_TYPE_SUBACK, subscribe->packet_id, client->cmd_timeout_ms); + +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &subscribe->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + /* reset state */ + subscribe->stat.write = MQTT_MSG_BEGIN; + + return rc; +} + +int SN_Client_Publish(MqttClient *client, SN_Publish *publish) +{ + int rc = MQTT_CODE_SUCCESS; + SN_MsgType resp_type; + + /* Validate required arguments */ + if (client == NULL || publish == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + switch (publish->stat.write) + { + case MQTT_MSG_BEGIN: + { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the publish packet */ + rc = SN_Encode_Publish(client->tx_buf, client->tx_buf_len, + publish); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d," + " QoS %d", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_PUBLISH), + SN_MSG_TYPE_PUBLISH, publish->packet_id, + publish->qos); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + + client->write.len = rc; + publish->buffer_pos = 0; + + #ifdef WOLFMQTT_MULTITHREAD + if ((publish->qos == MQTT_QOS_1) || + (publish->qos == MQTT_QOS_2)) { + resp_type = (publish->qos == MQTT_QOS_1) ? + SN_MSG_TYPE_PUBACK : + SN_MSG_TYPE_PUBCOMP; + + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)resp_type, publish->packet_id, + &publish->pendResp, &publish->resp); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + } + #endif + + publish->stat.write = MQTT_MSG_HEADER; + } + FALL_THROUGH; + + case MQTT_MSG_HEADER: + case MQTT_MSG_PAYLOAD: + case MQTT_MSG_PAYLOAD2: + { + /* Send packet and payload */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; + #endif + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + if (rc < 0) { + #ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &publish->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + return rc; + } + + if (rc == client->write.len) { + rc = MQTT_CODE_SUCCESS; + } + else { + rc = -1; + } + + /* if not expecting a reply, the reset state and exit */ + if ((publish->qos == MQTT_QOS_0) || + (publish->qos == MQTT_QOS_3)) { + break; + } + + publish->stat.write = MQTT_MSG_WAIT; + } + FALL_THROUGH; + + case MQTT_MSG_WAIT: + { + /* Handle QoS */ + if ((publish->qos == MQTT_QOS_1) || + (publish->qos == MQTT_QOS_2)) { + + /* Determine packet type to wait for */ + resp_type = (publish->qos == MQTT_QOS_1) ? + SN_MSG_TYPE_PUBACK : + SN_MSG_TYPE_PUBCOMP; + + /* Wait for publish response packet */ + rc = SN_Client_WaitType(client, &publish->resp, + resp_type, publish->packet_id, client->cmd_timeout_ms); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + break; + #endif + #ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &publish->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + + publish->return_code = publish->resp.return_code; + } + + break; + } + + case MQTT_MSG_ACK: + case MQTT_MSG_AUTH: + default: + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("SN_Client_Publish: Invalid state %d!", + publish->stat.write); + #endif + rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_STAT); + break; + } /* switch (publish->stat) */ + + /* reset state */ +#ifdef WOLFMQTT_NONBLOCK + if (rc != MQTT_CODE_CONTINUE) +#endif + { + publish->stat.write = MQTT_MSG_BEGIN; + } + if (rc > 0) { + rc = MQTT_CODE_SUCCESS; + } + + return rc; +} + +int SN_Client_Unsubscribe(MqttClient *client, SN_Unsubscribe *unsubscribe) +{ + int rc; + + /* Validate required arguments */ + if (client == NULL || unsubscribe == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (unsubscribe->stat.write == MQTT_MSG_BEGIN) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the subscribe packet */ + rc = SN_Encode_Unsubscribe(client->tx_buf, client->tx_buf_len, + unsubscribe); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_UNSUBSCRIBE), + SN_MSG_TYPE_UNSUBSCRIBE); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_UNSUBACK, + 0, &unsubscribe->pendResp, &unsubscribe->ack); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send unsubscribe packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &unsubscribe->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + unsubscribe->stat.write = MQTT_MSG_WAIT; + } + + /* Wait for unsubscribe ack packet */ + rc = SN_Client_WaitType(client, &unsubscribe->ack, + SN_MSG_TYPE_UNSUBACK, unsubscribe->packet_id, + client->cmd_timeout_ms); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; + #endif + #ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &unsubscribe->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + + /* reset state */ + unsubscribe->stat.write = MQTT_MSG_BEGIN; + + return rc; +} + +int SN_Client_Register(MqttClient *client, SN_Register *regist) +{ + int rc; + + /* Validate required arguments */ + if (client == NULL || regist == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (regist->stat.write == MQTT_MSG_BEGIN) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the register packet */ + rc = SN_Encode_Register(client->tx_buf, client->tx_buf_len, regist); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_REGISTER), + SN_MSG_TYPE_REGISTER); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_REGACK, + regist->packet_id, ®ist->pendResp, ®ist->regack); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send register packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, ®ist->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + return rc; + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + regist->stat.write = MQTT_MSG_WAIT; + } + + /* Wait for register acknowledge packet */ + rc = SN_Client_WaitType(client, ®ist->regack, + SN_MSG_TYPE_REGACK, regist->packet_id, client->cmd_timeout_ms); +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, ®ist->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + /* reset state */ + regist->stat.write = MQTT_MSG_BEGIN; + + return rc; +} + +int SN_Client_Ping(MqttClient *client, SN_PingReq *ping) +{ + int rc; + SN_PingReq loc_ping; + + /* Validate required arguments */ + if (client == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (ping == NULL) { + XMEMSET(&loc_ping, 0, sizeof(SN_PingReq)); + ping = &loc_ping; + } + + if (ping->stat.write == MQTT_MSG_BEGIN) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the ping packet as a request */ + rc = SN_Encode_Ping(client->tx_buf, client->tx_buf_len, ping, + SN_MSG_TYPE_PING_REQ); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_PING_REQ), + SN_MSG_TYPE_PING_REQ); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_PING_RESP, 0, + &ping->pendResp, NULL); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send ping req packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &ping->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + return rc; + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + ping->stat.write = MQTT_MSG_WAIT; + } + + /* Wait for ping resp packet */ + rc = SN_Client_WaitType(client, ping, + SN_MSG_TYPE_PING_RESP, 0, client->cmd_timeout_ms); +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &ping->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + /* reset state */ + ping->stat.write = MQTT_MSG_BEGIN; + + return rc; +} + +int SN_Client_Disconnect(MqttClient *client) +{ + return SN_Client_Disconnect_ex(client, NULL); +} + +int SN_Client_Disconnect_ex(MqttClient *client, SN_Disconnect *disconnect) +{ + int rc; + + /* Validate required arguments */ + if (client == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + +#ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } +#endif + + /* Encode the disconnect packet */ + rc = SN_Encode_Disconnect(client->tx_buf, client->tx_buf_len, disconnect); +#ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_DISCONNECT), + SN_MSG_TYPE_DISCONNECT); +#endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + +#ifdef WOLFMQTT_MULTITHREAD + if ((disconnect != NULL) && (disconnect->sleepTmr != 0)) { + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_DISCONNECT, 0, + &disconnect->pendResp, NULL); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + } +#endif + + /* Send disconnect packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &disconnect->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + return rc; + } +#ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); +#endif + + rc = MQTT_CODE_SUCCESS; + + /* If sleep was set, wait for response disconnect packet */ + if ((disconnect != NULL) && (disconnect->sleepTmr != 0)) { + rc = SN_Client_WaitType(client, disconnect, + SN_MSG_TYPE_DISCONNECT, 0, client->cmd_timeout_ms); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; + #endif + #ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &disconnect->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + } + + return rc; +} + +int SN_Client_WaitMessage_ex(MqttClient *client, SN_Object* packet_obj, + int timeout_ms) +{ + return SN_Client_WaitType(client, packet_obj, + SN_MSG_TYPE_ANY, 0, timeout_ms); +} + +int SN_Client_WaitMessage(MqttClient *client, int timeout_ms) +{ + if (client == NULL) + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + return SN_Client_WaitMessage_ex(client, &client->msgSN, timeout_ms); +} + +#endif /* defined WOLFMQTT_SN */ diff --git a/src/mqtt_sn_packet.c b/src/mqtt_sn_packet.c new file mode 100644 index 000000000..258980ed0 --- /dev/null +++ b/src/mqtt_sn_packet.c @@ -0,0 +1,1576 @@ +/* mqtt_sn_packet.c + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Include the autoconf generated config.h */ +#ifdef HAVE_CONFIG_H + #include +#endif + +#include "wolfmqtt/mqtt_sn_client.h" +#include "wolfmqtt/mqtt_sn_packet.h" + +#ifdef WOLFMQTT_SN +const char* SN_Packet_TypeDesc(SN_MsgType packet_type) +{ + switch (packet_type) { + case SN_MSG_TYPE_ADVERTISE: + return "Advertise"; + case SN_MSG_TYPE_SEARCHGW: + return "Search gateway"; + case SN_MSG_TYPE_GWINFO: + return "Gateway info"; + case SN_MSG_TYPE_CONNECT: + return "Connect"; + case SN_MSG_TYPE_CONNACK: + return "Connect Ack"; + case SN_MSG_TYPE_WILLTOPICREQ: + return "Will topic request"; + case SN_MSG_TYPE_WILLTOPIC: + return "Will topic set"; + case SN_MSG_TYPE_WILLMSGREQ: + return "Will message request"; + case SN_MSG_TYPE_WILLMSG: + return "Will message set"; + case SN_MSG_TYPE_REGISTER: + return "Register"; + case SN_MSG_TYPE_REGACK: + return "Register Ack"; + case SN_MSG_TYPE_PUBLISH: + return "Publish"; + case SN_MSG_TYPE_PUBACK: + return "Publish Ack"; + case SN_MSG_TYPE_PUBCOMP: + return "Publish complete"; + case SN_MSG_TYPE_PUBREC: + return "Publish Received"; + case SN_MSG_TYPE_PUBREL: + return "Publish Release"; + case SN_MSG_TYPE_SUBSCRIBE: + return "Subscribe"; + case SN_MSG_TYPE_SUBACK: + return "Subscribe Ack"; + case SN_MSG_TYPE_UNSUBSCRIBE: + return "Unsubscribe"; + case SN_MSG_TYPE_UNSUBACK: + return "Unsubscribe Ack"; + case SN_MSG_TYPE_PING_REQ: + return "Ping Req"; + case SN_MSG_TYPE_PING_RESP: + return "Ping Resp"; + case SN_MSG_TYPE_DISCONNECT: + return "Disconnect"; + case SN_MSG_TYPE_WILLTOPICUPD: + return "Will topic update"; + case SN_MSG_TYPE_WILLTOPICRESP: + return "WIll topic response"; + case SN_MSG_TYPE_WILLMSGUPD: + return "Will message update"; + case SN_MSG_TYPE_WILLMSGRESP: + return "Will message response"; + case SN_MSG_TYPE_ENCAPMSG: + return "Encapsulated message"; + case SN_MSG_TYPE_ANY: + return "Any"; + default: + break; + } + return "Unknown"; +} + +int SN_Decode_Header(byte *rx_buf, int rx_buf_len, + SN_MsgType* p_packet_type, word16* p_packet_id) +{ + SN_MsgType packet_type; + word16 total_len; + + if (rx_buf == NULL || rx_buf_len < MQTT_PACKET_HEADER_MIN_SIZE) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_buf++; + if (total_len == SN_PACKET_LEN_IND) { + /* The length is stored in the next two bytes */ + rx_buf += MqttDecode_Num(rx_buf, &total_len); + } + + if (total_len > rx_buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Message Type */ + packet_type = (SN_MsgType)*rx_buf++; + + if (p_packet_type) + *p_packet_type = packet_type; + + if (p_packet_id) { + switch(packet_type) { + case SN_MSG_TYPE_REGACK: + case SN_MSG_TYPE_PUBACK: + /* octet 4-5 */ + MqttDecode_Num(rx_buf + 2, p_packet_id); + break; + case SN_MSG_TYPE_PUBCOMP: + case SN_MSG_TYPE_PUBREC: + case SN_MSG_TYPE_PUBREL: + case SN_MSG_TYPE_UNSUBACK: + /* octet 2-3 */ + MqttDecode_Num(rx_buf, p_packet_id); + break; + case SN_MSG_TYPE_SUBACK: + /* octet 5-6 */ + MqttDecode_Num(rx_buf + 3, p_packet_id); + break; + case SN_MSG_TYPE_ADVERTISE: + case SN_MSG_TYPE_SEARCHGW: + case SN_MSG_TYPE_GWINFO: + case SN_MSG_TYPE_CONNECT: + case SN_MSG_TYPE_CONNACK: + case SN_MSG_TYPE_WILLTOPICREQ: + case SN_MSG_TYPE_WILLTOPIC: + case SN_MSG_TYPE_WILLMSGREQ: + case SN_MSG_TYPE_WILLMSG: + case SN_MSG_TYPE_REGISTER: + case SN_MSG_TYPE_PUBLISH: + case SN_MSG_TYPE_SUBSCRIBE: + case SN_MSG_TYPE_UNSUBSCRIBE: + case SN_MSG_TYPE_PING_REQ: + case SN_MSG_TYPE_PING_RESP: + case SN_MSG_TYPE_DISCONNECT: + case SN_MSG_TYPE_WILLTOPICUPD: + case SN_MSG_TYPE_WILLTOPICRESP: + case SN_MSG_TYPE_WILLMSGUPD: + case SN_MSG_TYPE_WILLMSGRESP: + case SN_MSG_TYPE_ENCAPMSG: + case SN_MSG_TYPE_RESERVED: + default: + *p_packet_id = 0; + break; + } + } + + return (int)total_len; +} + +int SN_Decode_Advertise(byte *rx_buf, int rx_buf_len, SN_Advertise *gw_info) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + + /* Check message type */ + type = *rx_payload++; + if (total_len != 5) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + if (type != SN_MSG_TYPE_ADVERTISE) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + /* Decode gateway info */ + if (gw_info != NULL) { + gw_info->gwId = *rx_payload++; + + rx_payload += MqttDecode_Num(rx_payload, &gw_info->duration); + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_SearchGW(byte *tx_buf, int tx_buf_len, byte hops) +{ + int total_len; + byte *tx_payload = tx_buf; + + /* Packet length is not variable */ + total_len = 3; + + /* Validate required arguments */ + if (tx_buf == NULL || tx_buf_len < total_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Encode length */ + *tx_payload++ = total_len; + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_SEARCHGW; + + /* Encode radius */ + *tx_payload++ = hops; + (void)tx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_GWInfo(byte *rx_buf, int rx_buf_len, SN_GwInfo *gw_info) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len == SN_PACKET_LEN_IND) { + /* The length is stored in the next two bytes */ + rx_payload += MqttDecode_Num(rx_payload, (word16*)&total_len); + } + + if (total_len > rx_buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + if (total_len < 3) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + /* Check message type */ + type = *rx_payload++; + if (type != SN_MSG_TYPE_GWINFO) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + /* Decode gateway info */ + if (gw_info != NULL) { + gw_info->gwId = *rx_payload++; + + /* TODO: validate size of gwAddr */ + if (total_len - 3 > 0) { + /* The gateway address is only present if sent by a client */ + XMEMCPY(gw_info->gwAddr, rx_payload, total_len - 3); + } + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +/* Packet Type Encoders/Decoders */ +int SN_Encode_Connect(byte *tx_buf, int tx_buf_len, SN_Connect *mc_connect) +{ + word16 total_len, id_len; + byte flags = 0; + byte *tx_payload = tx_buf; + + /* Validate required arguments */ + if ((tx_buf == NULL) || (mc_connect == NULL) || + (mc_connect->client_id == NULL) || (mc_connect->protocol_level == 0)) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Determine packet length */ + total_len = 6; /* Len + Message Type + Flags + ProtocolID + Duration(2) */ + + /* Client ID size */ + id_len = (word16)XSTRLEN(mc_connect->client_id); + id_len = (id_len <= SN_CLIENTID_MAX_LEN) ? id_len : SN_CLIENTID_MAX_LEN; + + total_len += id_len; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + total_len += 2; /* Store len in three bytes */ + } + + if (total_len > tx_buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode length */ + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = (byte)total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_CONNECT; + + /* Encode flags */ + if (mc_connect->clean_session) { + flags |= SN_PACKET_FLAG_CLEANSESSION; + } + if (mc_connect->enable_lwt) { + flags |= SN_PACKET_FLAG_WILL; + } + *tx_payload++ = flags; + + /* Protocol version */ + *tx_payload++ = mc_connect->protocol_level; + + /* Encode duration (keep-alive) */ + tx_payload += MqttEncode_Num(tx_payload, mc_connect->keep_alive_sec); + + /* Encode Client ID */ + XMEMCPY(tx_payload, mc_connect->client_id, id_len); + tx_payload += id_len; + (void)tx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_WillTopicReq(byte *rx_buf, int rx_buf_len) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Length and MsgType */ + total_len = *rx_payload++; + if (total_len != 2) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_WILLTOPICREQ) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +/* An empty WILLTOPIC message is a WILLTOPIC message without Flags and + WillTopic field (i.e. it is exactly 2 octets long). It is used by a client + to delete the Will topic and the Will message stored in the server */ +int SN_Encode_WillTopic(byte *tx_buf, int tx_buf_len, SN_Will *willTopic) +{ + int total_len; + byte *tx_payload, flags = 0; + + /* Validate required arguments */ + if (tx_buf == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Length and MsgType */ + total_len = 2; + + /* Determine packet length */ + if (willTopic != NULL) { + /* Will Topic is a string */ + total_len += (int)XSTRLEN(willTopic->willTopic); + + /* Flags */ + total_len++; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode length */ + tx_payload = tx_buf; + + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_WILLTOPIC; + + if (willTopic != NULL) { + int will_len; + + /* Encode flags */ + flags |= ((willTopic->qos << SN_PACKET_FLAG_QOS_SHIFT) & + SN_PACKET_FLAG_QOS_MASK); + flags |= (willTopic->retain != 0) ? SN_PACKET_FLAG_RETAIN : 0; + *tx_payload++ = flags; + + /* Encode Will Topic */ + will_len = (int)XSTRLEN(willTopic->willTopic); + XMEMCPY(tx_payload, willTopic->willTopic, will_len); + tx_payload += will_len; + } + (void)tx_payload; + + return total_len; +} + +int SN_Decode_WillMsgReq(byte *rx_buf, int rx_buf_len) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + + /* Length and MsgType */ + if (total_len != 2){ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + /* Message Type */ + type = *rx_payload++; + if (type != SN_MSG_TYPE_WILLMSGREQ) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_WillMsg(byte *tx_buf, int tx_buf_len, SN_Will *willMsg) +{ + int total_len; + byte *tx_payload; + + /* Validate required arguments */ + if ((tx_buf == NULL) || (willMsg == NULL)) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Length and MsgType */ + total_len = 2; + + /* Determine packet length */ + /* Add Will Message len */ + total_len += willMsg->willMsgLen; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode length */ + tx_payload = tx_buf; + + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_WILLMSG; + + /* Encode Will Message */ + XMEMCPY(tx_payload, willMsg->willMsg, willMsg->willMsgLen); + tx_payload += willMsg->willMsgLen; + (void)tx_payload; + + return total_len; +} + +int SN_Encode_WillTopicUpdate(byte *tx_buf, int tx_buf_len, SN_Will *willTopic) +{ + int total_len; + byte *tx_payload, flags = 0; + + /* Validate required arguments */ + if (tx_buf == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Length and MsgType */ + total_len = 2; + + /* Determine packet length */ + if (willTopic != NULL) { + /* Will Topic is a string */ + total_len += (int)XSTRLEN(willTopic->willTopic); + + /* Flags */ + total_len++; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode length */ + tx_payload = tx_buf; + + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_WILLTOPICUPD; + + if (willTopic != NULL) { + int will_len; + + /* Encode flags */ + flags |= ((willTopic->qos << SN_PACKET_FLAG_QOS_SHIFT) & + SN_PACKET_FLAG_QOS_MASK); + flags |= (willTopic->retain != 0) ? SN_PACKET_FLAG_RETAIN : 0; + *tx_payload++ = flags; + + /* Encode Will Topic */ + will_len = (int)XSTRLEN(willTopic->willTopic); + XMEMCPY(tx_payload, willTopic->willTopic, will_len); + tx_payload += will_len; + } + (void)tx_payload; + + return total_len; + +} + +int SN_Decode_WillTopicResponse(byte *rx_buf, int rx_buf_len, byte *ret_code) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len != 3) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_WILLTOPICRESP) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + /* Return Code */ + *ret_code = *rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_WillMsgUpdate(byte *tx_buf, int tx_buf_len, SN_Will *willMsg) +{ + int total_len; + byte *tx_payload; + + /* Validate required arguments */ + if ((tx_buf == NULL) || (willMsg == NULL)) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Length and MsgType */ + total_len = 2; + + /* Determine packet length */ + /* Add Will Message len */ + total_len += willMsg->willMsgLen; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode length */ + tx_payload = tx_buf; + + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_WILLMSGUPD; + + /* Encode Will Message */ + XMEMCPY(tx_payload, willMsg->willMsg, willMsg->willMsgLen); + tx_payload += willMsg->willMsgLen; + (void)tx_payload; + + return total_len; +} + +int SN_Decode_WillMsgResponse(byte *rx_buf, int rx_buf_len, byte *ret_code) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len != 3) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_WILLMSGRESP) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + /* Return Code */ + *ret_code = *rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_ConnectAck(byte *rx_buf, int rx_buf_len, + SN_ConnectAck *connect_ack) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len != 3) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_CONNACK) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + /* Decode variable header */ + if (connect_ack) { + connect_ack->return_code = *rx_payload++; + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_Register(byte *tx_buf, int tx_buf_len, SN_Register *regist) +{ + int total_len, topic_len; + byte *tx_payload; + + /* Validate required arguments */ + if (tx_buf == NULL || regist == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Determine packet length */ + /* Topic name is a string */ + total_len = (int)XSTRLEN(regist->topicName); + + /* Length, MsgType, TopicID (2), and packet_id (2) */ + total_len += 6; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode length */ + tx_payload = tx_buf; + + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_REGISTER; + + /* Encode Topic ID */ + tx_payload += MqttEncode_Num(tx_payload, regist->topicId); + + /* Encode Packet ID */ + tx_payload += MqttEncode_Num(tx_payload, regist->packet_id); + + /* Encode Topic Name */ + topic_len = (int)XSTRLEN(regist->topicName); + XMEMCPY(tx_payload, regist->topicName, topic_len); + tx_payload += topic_len; + (void)tx_payload; + + return total_len; +} + +int SN_Decode_Register(byte *rx_buf, int rx_buf_len, SN_Register *regist) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len == SN_PACKET_LEN_IND) { + /* The length is stored in the next two bytes */ + rx_payload += MqttDecode_Num(rx_payload, (word16*)&total_len); + } + + if (total_len > rx_buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + if (total_len < 7) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + /* Check message type */ + type = *rx_payload++; + if (type != SN_MSG_TYPE_REGISTER) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + if (regist != NULL) { + /* Decode Topic ID assigned by GW */ + rx_payload += MqttDecode_Num(rx_payload, ®ist->topicId); + + /* Decode packet ID */ + rx_payload += MqttDecode_Num(rx_payload, ®ist->packet_id); + + /* Decode Topic Name */ + regist->topicName = (char*)rx_payload; + + /* Terminate the string */ + rx_payload[total_len-6] = '\0'; + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_RegAck(byte *tx_buf, int tx_buf_len, SN_RegAck *regack) +{ + int total_len; + byte *tx_payload; + + /* Validate required arguments */ + if (tx_buf == NULL || regack == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Determine packet length */ + /* Length, MsgType, TopicID (2), and MsgId (2), Return Code */ + total_len = 7; + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + tx_payload = tx_buf; + + /* Encode length */ + *tx_payload++ = total_len; + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_REGACK; + + /* Encode Topic ID */ + tx_payload += MqttEncode_Num(tx_payload, regack->topicId); + + /* Encode Message ID */ + tx_payload += MqttEncode_Num(tx_payload, regack->packet_id); + + /* Encode Return Code */ + *tx_payload += regack->return_code; + + (void)tx_payload; + + return total_len; +} + +int SN_Decode_RegAck(byte *rx_buf, int rx_buf_len, SN_RegAck *regack) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len != 7) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_REGACK) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + if (regack != NULL) { + /* Decode Topic ID assigned by GW */ + rx_payload += MqttDecode_Num(rx_payload, ®ack->topicId); + + /* Decode packet ID */ + rx_payload += MqttDecode_Num(rx_payload, ®ack->packet_id); + + /* Decode return code */ + regack->return_code = *rx_payload++; + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_Subscribe(byte *tx_buf, int tx_buf_len, SN_Subscribe *subscribe) +{ + int total_len; + byte *tx_payload, flags = 0x00; + + /* Validate required arguments */ + if (tx_buf == NULL || subscribe == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Determine packet length */ + if (subscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { + /* Topic name is a string */ + total_len = (int)XSTRLEN(subscribe->topicNameId); + } + else { + /* Topic ID or Short name */ + total_len = 2; + } + + /* Length, MsgType, Flags, and MsgID (2) */ + total_len += 5; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode length */ + tx_payload = tx_buf; + + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_SUBSCRIBE; + + /* Set flags */ + if (subscribe->duplicate) + flags |= SN_PACKET_FLAG_DUPLICATE; + flags |= (SN_PACKET_FLAG_QOS_MASK & + (subscribe->qos << SN_PACKET_FLAG_QOS_SHIFT)); + flags |= (SN_PACKET_FLAG_TOPICIDTYPE_MASK & subscribe->topic_type); + + *tx_payload++ = flags; + + /* Encode packet ID */ + tx_payload += MqttEncode_Num(tx_payload, subscribe->packet_id); + + /* Encode topic */ + if (subscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { + /* Topic name is a string */ + XMEMCPY(tx_payload, subscribe->topicNameId, XSTRLEN(subscribe->topicNameId)); + } + else { + /* Topic ID */ + XMEMCPY(tx_payload, subscribe->topicNameId, 2); + } + (void)tx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_SubscribeAck(byte* rx_buf, int rx_buf_len, + SN_SubAck *subscribe_ack) +{ + word16 total_len; + byte* rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len != 8) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_SUBACK) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Decode SubAck fields */ + if (subscribe_ack) { + subscribe_ack->flags = *rx_payload++; + rx_payload += MqttDecode_Num(rx_payload, &subscribe_ack->topicId); + rx_payload += MqttDecode_Num(rx_payload, &subscribe_ack->packet_id); + subscribe_ack->return_code = *rx_payload++; + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_Publish(byte *tx_buf, int tx_buf_len, SN_Publish *publish) +{ + word16 total_len; + byte *tx_payload = tx_buf; + byte flags = 0; + + /* Validate required arguments */ + if (tx_buf == NULL || publish == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Determine packet length */ + total_len = publish->total_len; + + /* Add length, msgType, flags, topic ID (2), and msgID (2) */ + total_len += 7; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode header */ + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = (byte)total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + *tx_payload++ = SN_MSG_TYPE_PUBLISH; + + /* Set flags */ + if (publish->duplicate) + flags |= SN_PACKET_FLAG_DUPLICATE; + flags |= (SN_PACKET_FLAG_QOS_MASK & + (publish->qos << SN_PACKET_FLAG_QOS_SHIFT)); + if (publish->retain) + flags |= SN_PACKET_FLAG_RETAIN; + flags |= (SN_PACKET_FLAG_TOPICIDTYPE_MASK & publish->topic_type); + + *tx_payload++ = flags; + + /* Encode topic */ + if ((publish->topic_type == SN_TOPIC_ID_TYPE_SHORT) || + (publish->topic_type == SN_TOPIC_ID_TYPE_PREDEF)) { + /* Short and predefined topic names are 2 chars */ + XMEMCPY(tx_payload, publish->topic_name, 2); + tx_payload += 2; + } + else { + /* Topic ID */ + tx_payload += MqttEncode_Num(tx_payload, *(word16*)publish->topic_name); + } + + tx_payload += MqttEncode_Num(tx_payload, publish->packet_id); + + /* Encode payload */ + XMEMCPY(tx_payload, publish->buffer, publish->total_len); + tx_payload += publish->total_len; + + (void)tx_payload; + + /* Return length of packet placed into tx_buf */ + return total_len; +} + +int SN_Decode_Publish(byte *rx_buf, int rx_buf_len, SN_Publish *publish) +{ + word16 total_len; + byte *rx_payload = rx_buf; + byte flags = 0, type; + + /* Validate required arguments */ + if (rx_buf == NULL || publish == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len == SN_PACKET_LEN_IND) { + /* The length is stored in the next two bytes */ + rx_payload += MqttDecode_Num(rx_payload, &total_len); + } + + if (total_len > rx_buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + if (total_len < 7) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + /* Message Type */ + type = *rx_payload++; + if (type != SN_MSG_TYPE_PUBLISH) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + flags = *rx_payload++; + + publish->topic_name = (char*)rx_payload; + rx_payload += MQTT_DATA_LEN_SIZE; + publish->topic_name_len = MQTT_DATA_LEN_SIZE; + + rx_payload += MqttDecode_Num(rx_payload, &publish->packet_id); + + /* Set flags */ + publish->duplicate = flags & SN_PACKET_FLAG_DUPLICATE; + + publish->qos = (MqttQoS)((flags & SN_PACKET_FLAG_QOS_MASK) >> + SN_PACKET_FLAG_QOS_SHIFT); + + publish->retain = flags & SN_PACKET_FLAG_RETAIN; + + publish->topic_type = flags & SN_PACKET_FLAG_TOPICIDTYPE_MASK; + + /* Decode payload */ + + publish->total_len = total_len - 7; + publish->buffer = rx_payload; + publish->buffer_pos = 0; + publish->buffer_len = publish->total_len; + + /* Return length of packet read from rx_buf */ + return total_len; +} + +int SN_Encode_PublishResp(byte* tx_buf, int tx_buf_len, byte type, + SN_PublishResp *publish_resp) +{ + int total_len; + byte *tx_payload = tx_buf; + + /* Validate required arguments */ + if (tx_buf == NULL || publish_resp == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Determine packet length */ + total_len = (type == SN_MSG_TYPE_PUBACK) ? 7 : 4; + + if (total_len > tx_buf_len) + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + + /* Encode */ + *tx_payload++ = (byte)total_len; + + *tx_payload++ = type; + + if (type == SN_MSG_TYPE_PUBACK) { + tx_payload += MqttEncode_Num(tx_payload, publish_resp->topicId); + } + + tx_payload += MqttEncode_Num(tx_payload, publish_resp->packet_id); + + if (type == SN_MSG_TYPE_PUBACK) { + *tx_payload++ = publish_resp->return_code; + } + (void)tx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_PublishResp(byte* rx_buf, int rx_buf_len, byte type, + SN_PublishResp *publish_resp) +{ + int total_len; + byte rec_type, *rx_payload = rx_buf; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode */ + total_len = *rx_payload++; + + if(total_len > rx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Validate packet type */ + rec_type = *rx_payload++; + if (rec_type != type) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + if (publish_resp) { + if (type == SN_MSG_TYPE_PUBACK) { + rx_payload += MqttDecode_Num(rx_payload, &publish_resp->topicId); + } + + rx_payload += MqttDecode_Num(rx_payload, &publish_resp->packet_id); + + if (type == SN_MSG_TYPE_PUBACK) { + publish_resp->return_code = *rx_payload++; + } + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_Unsubscribe(byte *tx_buf, int tx_buf_len, + SN_Unsubscribe *unsubscribe) +{ + int total_len; + byte *tx_payload, flags = 0x00; + + /* Validate required arguments */ + if (tx_buf == NULL || unsubscribe == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Determine packet length */ + if (unsubscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { + /* Topic name is a string */ + total_len = (int)XSTRLEN(unsubscribe->topicNameId); + } + else { + /* Topic ID or Short name */ + total_len = 2; + } + + /* Length, MsgType, Flags, and MsgID (2) */ + total_len += 5; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode header */ + tx_payload = tx_buf; + + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + *tx_payload++ = SN_MSG_TYPE_UNSUBSCRIBE; + + /* Set flags */ + if (unsubscribe->duplicate) + flags |= SN_PACKET_FLAG_DUPLICATE; + flags |= (SN_PACKET_FLAG_QOS_MASK & + (unsubscribe->qos << SN_PACKET_FLAG_QOS_SHIFT)); + flags |= (SN_PACKET_FLAG_TOPICIDTYPE_MASK & unsubscribe->topic_type); + + *tx_payload++ = flags; + + tx_payload += MqttEncode_Num(tx_payload, unsubscribe->packet_id); + + /* Encode topic */ + if (unsubscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { + /* Topic name is a string */ + XMEMCPY(tx_payload, unsubscribe->topicNameId, + XSTRLEN(unsubscribe->topicNameId)); + } + else { + /* Topic ID or Short name */ + XMEMCPY(tx_payload, unsubscribe->topicNameId, 2); + } + + (void)tx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_UnsubscribeAck(byte *rx_buf, int rx_buf_len, + SN_UnsubscribeAck *unsubscribe_ack) +{ + word16 total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len != 4) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_UNSUBACK) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + /* Decode SubAck fields */ + if (unsubscribe_ack) { + rx_payload += MqttDecode_Num(rx_payload, &unsubscribe_ack->packet_id); + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_Disconnect(byte *tx_buf, int tx_buf_len, + SN_Disconnect* disconnect) +{ + int total_len = 2; /* length and message type */ + byte *tx_payload = tx_buf;; + + /* Validate required arguments */ + if (tx_buf == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if ((disconnect != NULL) && (disconnect->sleepTmr > 0)) { + total_len += 2; /* Sleep duration is set */ + } + + if (total_len > tx_buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode message */ + *tx_payload++ = total_len; + + *tx_payload++ = SN_MSG_TYPE_DISCONNECT; + + if ((disconnect != NULL) && (disconnect->sleepTmr > 0)) { + tx_payload += MqttEncode_Num(tx_payload, disconnect->sleepTmr); + } + (void)tx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_Disconnect(byte *rx_buf, int rx_buf_len) +{ + word16 total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len != 2) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_DISCONNECT) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_Ping(byte *tx_buf, int tx_buf_len, SN_PingReq *ping, byte type) +{ + int total_len = 2, clientId_len = 0; + byte *tx_payload = tx_buf; + + /* Validate required arguments */ + if ((tx_buf == NULL) || + ((type != SN_MSG_TYPE_PING_REQ) && (type != SN_MSG_TYPE_PING_RESP))) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if ((type == SN_MSG_TYPE_PING_REQ) && (ping != NULL) && + (ping->clientId != NULL)) { + total_len += clientId_len = (int)XSTRLEN(ping->clientId); + } + + if (total_len > tx_buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + *tx_payload++ = (byte)total_len; + + *tx_payload++ = type; + + if (clientId_len > 0) { + XMEMCPY(tx_payload, ping->clientId, clientId_len); + tx_payload += clientId_len; + } + (void)tx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_Ping(byte *rx_buf, int rx_buf_len) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + total_len = *rx_payload++; + if (total_len != 2) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if ((type != SN_MSG_TYPE_PING_REQ) && + (type != SN_MSG_TYPE_PING_RESP)) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + /* Return total length of packet */ + return total_len; +} + +/* Read return code is length when > 0 */ +int SN_Packet_Read(MqttClient *client, byte* rx_buf, int rx_buf_len, + int timeout_ms) +{ + int rc, len = 0, remain_read = 0; + word16 total_len = 0, idx = 0; + + switch (client->packet.stat) + { + case MQTT_PK_BEGIN: + { + /* Read first 2 bytes */ + if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { + rc = MqttSocket_Read(client, rx_buf, 2, timeout_ms); + } else { + rc = MqttSocket_Peek(client, rx_buf, 2, timeout_ms); + } + if (rc < 0) { + return MqttPacket_HandleNetError(client, rc); + } + else if (rc != 2) { + return MqttPacket_HandleNetError(client, + MQTT_TRACE_ERROR(MQTT_CODE_ERROR_NETWORK)); + } + + len = rc; + + if (rx_buf[0] == SN_PACKET_LEN_IND){ + /* Read length stored in first three bytes, type in fourth */ + if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { + rc = MqttSocket_Read(client, rx_buf+len, 2, timeout_ms); + if (rc < 0) { + return MqttPacket_HandleNetError(client, rc); + } + else if (rc != 2) { + return MqttPacket_HandleNetError(client, + MQTT_TRACE_ERROR(MQTT_CODE_ERROR_NETWORK)); + } + rc += len; + } + else { + rc = MqttSocket_Peek(client, rx_buf, 4, timeout_ms); + if (rc < 0) { + return MqttPacket_HandleNetError(client, rc); + } + else if (rc != 4) { + return MqttPacket_HandleNetError(client, + MQTT_TRACE_ERROR(MQTT_CODE_ERROR_NETWORK)); + } + len = rc; + } + + (void)MqttDecode_Num(&rx_buf[1], &total_len); + client->packet.header_len = len; + } + else { + /* Length is stored in first byte, type in second */ + total_len = rx_buf[0]; + client->packet.header_len = len; + } + } + FALL_THROUGH; + + case MQTT_PK_READ_HEAD: + { + client->packet.stat = MQTT_PK_READ_HEAD; + } + FALL_THROUGH; + + case MQTT_PK_READ: + { + client->packet.stat = MQTT_PK_READ; + + if (total_len > len) { + client->packet.remain_len = total_len - len; + } + else if ((total_len == 2) || (total_len == 4)) { + /* Handle peek */ + if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { + client->packet.remain_len = total_len - len; + } + else { + client->packet.remain_len = total_len; + } + } + else { + client->packet.remain_len = 0; + } + + /* Make sure it does not overflow rx_buf */ + if (client->packet.remain_len > + (rx_buf_len - client->packet.header_len)) { + client->packet.remain_len = rx_buf_len - + client->packet.header_len; + } + if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { + total_len -= client->packet.header_len; + idx = client->packet.header_len; + } + /* Read whole message */ + if (client->packet.remain_len > 0) { + rc = MqttSocket_Read(client, &rx_buf[idx], + total_len, timeout_ms); + if (rc <= 0) { + return MqttPacket_HandleNetError(client, rc); + } + remain_read = rc; + } + if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { + remain_read += client->packet.header_len; + } + + break; + } + } /* switch (client->packet.stat) */ + + /* reset state */ + client->packet.stat = MQTT_PK_BEGIN; + + /* Return read length */ + return remain_read; +} + +#endif /* WOLFMQTT_SN */ diff --git a/wolfmqtt.vcxproj b/wolfmqtt.vcxproj index 9f79e47e0..406d8842b 100755 --- a/wolfmqtt.vcxproj +++ b/wolfmqtt.vcxproj @@ -145,10 +145,14 @@ + + + + diff --git a/wolfmqtt/include.am b/wolfmqtt/include.am index 0096389b2..77dfcc9da 100644 --- a/wolfmqtt/include.am +++ b/wolfmqtt/include.am @@ -11,3 +11,8 @@ nobase_include_HEADERS+= \ wolfmqtt/visibility.h \ wolfmqtt/options.h \ wolfmqtt/vs_settings.h + +if BUILD_SN +nobase_include_HEADERS+= wolfmqtt/mqtt_sn_client.h \ + wolfmqtt/mqtt_sn_packet.h +endif diff --git a/wolfmqtt/mqtt_client.h b/wolfmqtt/mqtt_client.h index 8c8346067..f381d2f8f 100644 --- a/wolfmqtt/mqtt_client.h +++ b/wolfmqtt/mqtt_client.h @@ -42,6 +42,11 @@ #include "wolfmqtt/mqtt_packet.h" #include "wolfmqtt/mqtt_socket.h" +#ifdef WOLFMQTT_SN +#include "wolfmqtt/mqtt_sn_packet.h" +#endif + + /* This macro allows the disconnect callback to be triggered when * MqttClient_Disconnect_ex is called. Normally the CB is only used to handle * errors from MqttPacket_HandleNetError. @@ -133,6 +138,8 @@ typedef struct _MqttSk { #ifdef WOLFMQTT_PROPERTY_CB typedef int (*MqttPropertyCb)(struct _MqttClient* client, MqttProp* head, void* ctx); #endif + + #ifdef WOLFMQTT_SN /*! \brief Mqtt-SN Register Callback. * A GW sends a REGISTER message to a client if it wants to @@ -206,6 +213,9 @@ typedef struct _MqttClient { #endif } MqttClient; +#ifdef WOLFMQTT_SN +#include "wolfmqtt/mqtt_sn_client.h" +#endif /* Application Interfaces */ @@ -567,190 +577,17 @@ WOLFMQTT_API const char* MqttClient_ReturnCodeToString( "not compiled in" #endif /* WOLFMQTT_NO_ERROR_STRINGS */ -#ifdef WOLFMQTT_SN -/*! \brief Encodes and sends the a message to search for a gateway and - waits for the gateway info response message. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param search Pointer to SN_SearchGW structure initialized - with hop radius. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_SearchGW( - MqttClient *client, - SN_SearchGw *search); - -/*! \brief Encodes and sends the Connect packet and waits for the - Connect Acknowledgment packet. If Will is enabled, the gateway - prompts for LWT Topic and Message. Sending an empty will topic - indicates that the client wishes to delete the Will topic and - the Will message stored in the server. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param connect Pointer to SN_Connect structure initialized - with connect parameters - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Connect( - MqttClient *client, - SN_Connect *connect); - -/*! \brief Encodes and sends the MQTT-SN Will Topic Update packet. Sending - a NULL 'will' indicates that the client wishes to delete the - Will topic and the Will message stored in the server. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param will Pointer to SN_Will structure initialized - with topic and message parameters. NULL is valid. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_WillTopicUpdate(MqttClient *client, SN_Will *will); - -/*! \brief Encodes and sends the MQTT-SN Will Message Update packet. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param will Pointer to SN_Will structure initialized - with topic and message parameters. NULL is valid. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_WillMsgUpdate(MqttClient *client, SN_Will *will); - -/*! \brief Encodes and sends the MQTT-SN Register packet and waits for the - Register Acknowledge packet. The Register packet is sent by a - client to a GW for requesting a topic id value for the included - topic name. It is also sent by a GW to inform a client about - the topic id value it has assigned to the included topic name. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param regist Pointer to SN_Register structure - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Register( - MqttClient *client, - SN_Register *regist); - - -/*! \brief Sets a register callback with custom context - * \param client Pointer to MqttClient structure - (uninitialized is okay) - * \param regCb Pointer to register callback function - * \param ctx Pointer to your own context - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_BAD_ARG - */ -WOLFMQTT_API int SN_Client_SetRegisterCallback( - MqttClient *client, - SN_ClientRegisterCb regCb, - void* ctx); - - -/*! \brief Encodes and sends the MQTT-SN Publish packet and waits for the - Publish response (if QoS > 0). - * \note This is a blocking function that will wait for MqttNet.read - * If QoS level = 1 then will wait for PUBLISH_ACK. - * If QoS level = 2 then will wait for PUBLISH_REC then send - PUBLISH_REL and wait for PUBLISH_COMP. - * \param client Pointer to MqttClient structure - * \param publish Pointer to SN_Publish structure initialized - with message data - * Note: SN_Publish and MqttMessage are same - structure. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Publish( - MqttClient *client, - SN_Publish *publish); - -/*! \brief Encodes and sends the MQTT-SN Subscribe packet and waits for the - Subscribe Acknowledgment packet containing the assigned - topic ID. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param subscribe Pointer to SN_Subscribe structure initialized with - subscription topic list and desired QoS. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Subscribe( - MqttClient *client, - SN_Subscribe *subscribe); - -/*! \brief Encodes and sends the MQTT-SN Unsubscribe packet and waits for - the Unsubscribe Acknowledgment packet - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param unsubscribe Pointer to SN_Unsubscribe structure initialized - with topic ID. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Unsubscribe( - MqttClient *client, - SN_Unsubscribe *unsubscribe); - -/*! \brief Encodes and sends the MQTT-SN Disconnect packet. Client may - send the disconnect with a duration to indicate the client is - entering the "asleep" state. - * \note This is a non-blocking function that will try and send using - MqttNet.write - * \param client Pointer to MqttClient structure - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Disconnect( - MqttClient *client); - -/*! \brief Encodes and sends the MQTT-SN Disconnect packet. Client may - send the disconnect with a duration to indicate the client is - entering the "asleep" state. - * \note This is a non-blocking function that will try and send using - MqttNet.write - * \param client Pointer to MqttClient structure - * \param disconnect Pointer to SN_Disconnect structure. NULL is valid. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Disconnect_ex( - MqttClient *client, - SN_Disconnect *disconnect); - - -/*! \brief Encodes and sends the MQTT-SN Ping Request packet and waits - for the Ping Response packet. If client is in the "asleep" - state and wants to notify the gateway that it is entering the - "awake" state, it should add it's client ID to the ping - request. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param ping Pointer to SN_PingReq structure. NULL is valid. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Ping( - MqttClient *client, - SN_PingReq *ping); - -/*! \brief Waits for packets to arrive. Incoming publish messages - will arrive via callback provided in MqttClient_Init. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param timeout_ms Milliseconds until read timeout - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_WaitMessage( - MqttClient *client, - int timeout_ms); - -WOLFMQTT_API int SN_Client_WaitMessage_ex(MqttClient *client, SN_Object* packet_obj, - int timeout_ms); - -#endif /* WOLFMQTT_SN */ +/* Internal functions */ +#ifdef WOLFMQTT_MULTITHREAD +WOLFMQTT_LOCAL int MqttClient_RespList_Find(MqttClient *client, + MqttPacketType packet_type, word16 packet_id, MqttPendResp **retResp); +WOLFMQTT_LOCAL void MqttClient_RespList_Remove(MqttClient *client, + MqttPendResp *rmResp); +WOLFMQTT_LOCAL int MqttClient_RespList_Add(MqttClient *client, + MqttPacketType packet_type, word16 packet_id, MqttPendResp *newResp, + void *packet_obj); +#endif +WOLFMQTT_LOCAL int MqttPacket_HandleNetError(MqttClient *client, int rc); #ifdef __cplusplus } /* extern "C" */ diff --git a/wolfmqtt/mqtt_packet.h b/wolfmqtt/mqtt_packet.h index 73e6e0a96..251a7c6d2 100644 --- a/wolfmqtt/mqtt_packet.h +++ b/wolfmqtt/mqtt_packet.h @@ -720,463 +720,6 @@ WOLFMQTT_LOCAL MqttProp* MqttProps_FindType(MqttProp *head, #define MqttPacket_TypeDesc(x) "not compiled in" #endif - -#ifdef WOLFMQTT_SN - -/* Note that because MQTT-SN does not support message fragmentation and - reassembly, the maximum message length that could be used in a network is - governed by the maximum packet size that is supported by that network, - and not by the maximum length that could be encoded by MQTT-SN. */ -#ifndef WOLFMQTT_SN_MAXPACKET_SIZE -#define WOLFMQTT_SN_MAXPACKET_SIZE 1024 -#endif - -/* The SN_GwAddr field has a variable length and contains the address of a GW. - Its depends on the network over which MQTT-SN operates and is indicated in - the first octet of this field. For example, in a ZigBee network the network - address is 2-octet long. */ -typedef word16 SN_GwAddr ; - -/* RETURN CODE values */ -enum SN_ReturnCodes { - SN_RC_ACCEPTED = 0x00, - SN_RC_CONGESTION = 0x01, - SN_RC_INVTOPICNAME = 0x02, - SN_RC_NOTSUPPORTED = 0x03 - /* 0x04 - 0xFF reserved */ -}; - -/* MESSAGE HEADER */ -/* Message types: Located in last byte of header */ -typedef enum _SN_MsgType { - SN_MSG_TYPE_ADVERTISE = 0x00, - SN_MSG_TYPE_SEARCHGW = 0x01, - SN_MSG_TYPE_GWINFO = 0x02, - /* 0x03 reserved */ - SN_MSG_TYPE_CONNECT = 0x04, - SN_MSG_TYPE_CONNACK = 0x05, - SN_MSG_TYPE_WILLTOPICREQ = 0x06, - SN_MSG_TYPE_WILLTOPIC = 0x07, - SN_MSG_TYPE_WILLMSGREQ = 0x08, - SN_MSG_TYPE_WILLMSG = 0x09, - SN_MSG_TYPE_REGISTER = 0x0A, - SN_MSG_TYPE_REGACK = 0x0B, - SN_MSG_TYPE_PUBLISH = 0x0C, - SN_MSG_TYPE_PUBACK = 0x0D, - SN_MSG_TYPE_PUBCOMP = 0x0E, - SN_MSG_TYPE_PUBREC = 0x0F, - SN_MSG_TYPE_PUBREL = 0x10, - /* 0x11 reserved */ - SN_MSG_TYPE_SUBSCRIBE = 0x12, - SN_MSG_TYPE_SUBACK = 0x13, - SN_MSG_TYPE_UNSUBSCRIBE = 0x14, - SN_MSG_TYPE_UNSUBACK = 0x15, - SN_MSG_TYPE_PING_REQ = 0x16, - SN_MSG_TYPE_PING_RESP = 0x17, - SN_MSG_TYPE_DISCONNECT = 0x18, - /* 0x19 reserved */ - SN_MSG_TYPE_WILLTOPICUPD = 0x1A, - SN_MSG_TYPE_WILLTOPICRESP = 0x1B, - SN_MSG_TYPE_WILLMSGUPD = 0x1C, - SN_MSG_TYPE_WILLMSGRESP = 0x1D, - /* 0x1E - 0xFD reserved */ - SN_MSG_TYPE_ENCAPMSG = 0xFE, /* Encapsulated message */ - /* 0xFF reserved */ - SN_MSG_TYPE_RESERVED = 0xFF, - SN_MSG_TYPE_ANY = 0xFF -} SN_MsgType; - -/* Topic ID types */ -enum SN_TopicId_Types { - SN_TOPIC_ID_TYPE_NORMAL = 0x0, - SN_TOPIC_ID_TYPE_PREDEF = 0x1, - SN_TOPIC_ID_TYPE_SHORT = 0x2 -}; - -enum SN_PacketFlags { - SN_PACKET_FLAG_TOPICIDTYPE_MASK = 0x3, - SN_PACKET_FLAG_CLEANSESSION = 0x4, - SN_PACKET_FLAG_WILL = 0x8, - SN_PACKET_FLAG_RETAIN = 0x10, - SN_PACKET_FLAG_QOS_MASK = 0x60, - SN_PACKET_FLAG_QOS_SHIFT = 0x5, - SN_PACKET_FLAG_DUPLICATE = 0x80 -}; - -/* Message Header: Size is variable 2 - 4 bytes */ - -/* If the first byte of the packet len is 0x01, then the packet size is - greater than 0xFF and is stored in the next two bytes */ -#define SN_PACKET_LEN_IND 0x01 - -#define SN_PACKET_MAX_SMALL_SIZE 0xFF - -/* Gateway (GW) messages */ -/* Advertise message */ -typedef struct _SN_AdvertiseMsg { - MqttMsgStat stat; - - byte gwId; /* ID of the gateway that sent this message */ - word16 duration; /* Seconds until next Advertise - is broadcast by this gateway */ -} SN_Advertise; - -typedef struct _SN_GwInfo { - MqttMsgStat stat; - - byte gwId; /* ID of the gateway that sent this message */ - SN_GwAddr* gwAddr; /* Address of the indicated gateway */ -} SN_GwInfo; - -typedef struct _SN_SearchGw { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - byte radius; /* Broadcast radius (in hops) */ - SN_GwInfo gwInfo; -} SN_SearchGw; - -/* Connect Protocol */ -#define SN_PROTOCOL_ID_1 0x01 -#define SN_PROTOCOL_ID SN_PROTOCOL_ID_1 - -#define SN_CLIENTID_MAX_LEN 23 - -/* Connect Ack message structure */ -typedef struct _SN_ConnectAck { - MqttMsgStat stat; - - byte return_code; -} SN_ConnectAck; - -/* WILL TOPIC */ -typedef struct _SN_WillTopicUpd { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - byte flags; - char* willTopic; /* contains the Will topic name */ -} SN_WillTopicUpd; - -typedef struct _SN_WillMsgUpd { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - char* willMsg; -} SN_WillMsgUpd; - -typedef struct _SN_WillTopicResp { - MqttMsgStat stat; - - byte return_code; -} SN_WillTopicResp; - -typedef SN_WillTopicResp SN_WillMsgResp; - -typedef union _SN_WillResp { - SN_WillMsgResp msgResp; - SN_WillMsgUpd msgUpd; - SN_WillTopicResp topicResp; - SN_WillTopicUpd topicUpd; -} SN_WillResp; - -typedef struct _SN_Will { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - byte qos; - byte retain; - const char* willTopic; - byte* willMsg; - word16 willMsgLen; - - SN_WillResp resp; -} SN_Will; - -/* Connect */ -typedef struct _SN_Connect { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - word16 keep_alive_sec; - byte clean_session; - const char *client_id; - - /* Protocol version: 1=v1.2 (default) */ - byte protocol_level; - - /* Optional Last will and testament */ - byte enable_lwt; - SN_Will will; - - /* Ack data */ - SN_ConnectAck ack; -} SN_Connect; - -/* REGISTER protocol */ -typedef struct _SN_RegAck { - MqttMsgStat stat; - - word16 topicId; - word16 packet_id; - byte return_code; -} SN_RegAck; - -typedef struct _SN_Register { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - word16 topicId; - word16 packet_id; - const char* topicName; - SN_RegAck regack; -} SN_Register; - -/* PUBLISH RESPONSE */ -/* This is the response struct for PUBREC, PUBREL, and PUBCOMP */ -/* If QoS = 0: No response */ -/* If QoS = 1: Expect response packet with type = SN_MSG_TYPE_PUBACK */ -/* If QoS = 2: Expect response packet with type = SN_MSG_TYPE_PUBREC */ -/* Message ID required if QoS is 1 or 2 */ -/* If QoS = 2: Send SN_MSG_TYPE_PUBREL with msgId to complete - QoS2 protocol exchange */ -/* Expect response packet with type = SN_MSG_TYPE_PUBCOMP */ -typedef struct _SN_PublishResp { - MqttMsgStat stat; - - word16 packet_id; - word16 topicId; /* PUBACK Only */ - byte return_code; /* PUBACK Only */ -} SN_PublishResp; - -/* PUBLISH protocol */ -typedef struct _SN_Publish { - MqttMsgStat stat; /* must be first member at top */ -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - /* BEGIN: THIS SECTION NEEDS TO MATCH MqttMessage */ - word16 packet_id; - byte type; - MqttQoS qos; - byte retain; - byte duplicate; - byte topic_type; - byte return_code; - - const char *topic_name; /* Pointer is valid only when - msg_new set in callback */ - word16 topic_name_len; - word32 total_len; /* Payload total length */ - byte *buffer; /* Payload buffer */ - word32 buffer_len; /* Payload buffer length */ - word32 buffer_pos; /* Payload buffer position */ - - /* Used internally for TX/RX buffers */ - byte buffer_new; /* flag to indicate new message */ - word32 intBuf_len; /* Buffer length */ - word32 intBuf_pos; /* Buffer position */ - - void* ctx; /* user supplied context for publish callbacks */ - /* END: THIS SECTION NEEDS TO MATCH MqttMessage */ - - SN_PublishResp resp; -} SN_Publish; - -/* SUBSCRIBE ACK */ -typedef struct _SN_SubAck { - MqttMsgStat stat; - - byte flags; - word16 topicId; - word16 packet_id; - byte return_code; -} SN_SubAck; - -/* SUBSCRIBE */ -typedef struct _SN_Subscribe { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - byte duplicate; - byte qos; - word16 packet_id; - byte topic_type; - /* 5.3.12 TopicName - The TopicName field has a variable length and contains an UTF8-encoded - string that specifies the topic name.*/ - const char* topicNameId; /* Contains topic name, ID, - or short name as indicated in topic type */ - SN_SubAck subAck; -} SN_Subscribe; - -/* UNSUBSCRIBE RESPONSE ACK */ -typedef struct _SN_UnsubscribeAck { - MqttMsgStat stat; /* must be first member at top */ - - word16 packet_id; -} SN_UnsubscribeAck; - -/* UNSUBSCRIBE */ -typedef struct _SN_Unsubscribe { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - byte duplicate; - byte qos; - word16 packet_id; - byte topic_type; - /* 5.3.12 TopicName - The TopicName field has a variable length and contains an UTF8-encoded - string that specifies the topic name.*/ - const char* topicNameId; /* Contains topic name, ID, - or short name as indicated in topic type */ - SN_UnsubscribeAck ack; -} SN_Unsubscribe; - -/* PING REQUEST / PING RESPONSE */ -typedef struct _SN_PingReq { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - /* clientId is optional and is included by a “sleeping” client when it - goes to the “awake” state and is waiting for messages sent by the - server/gateway. */ - char *clientId; -} SN_PingReq; - -/* DISCONNECT */ -typedef struct _SN_Disconnect { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - /* sleepTmr is optional and is included by a “sleeping” client - that wants to go the “asleep” state. The receipt of this message - is also acknowledged by the gateway by means of a DISCONNECT message - (without a duration field).*/ - word16 sleepTmr; -} SN_Disconnect; - -typedef union _SN_Object { - SN_Advertise advertise; - SN_GwInfo gwInfo; - SN_SearchGw searchGw; - - SN_Will will; - SN_Connect connect; - SN_ConnectAck connectAck; - - SN_Register reg; - SN_RegAck regAck; - - SN_Publish publish; - SN_PublishResp publishResp; - - SN_Subscribe sub; - SN_SubAck subAck; - SN_Unsubscribe unSub; - SN_UnsubscribeAck unSubAck; - - SN_PingReq pingReq; - SN_Disconnect disconnect; - - SN_WillMsgUpd willMsgUpd; - SN_WillMsgResp willMsgResp; - - SN_WillTopicUpd willTopicUpd; - SN_WillTopicResp willTopicResp; -} SN_Object; - - -/* Forward Encapsulation */ -// TODO - -WOLFMQTT_LOCAL int SN_Decode_Header(byte *rx_buf, int rx_buf_len, - SN_MsgType* p_packet_type, word16* p_packet_id); -WOLFMQTT_LOCAL int SN_Decode_Advertise(byte *rx_buf, int rx_buf_len, - SN_Advertise *gw_info); -WOLFMQTT_LOCAL int SN_Encode_SearchGW(byte *tx_buf, int tx_buf_len, byte hops); -WOLFMQTT_LOCAL int SN_Decode_GWInfo(byte *rx_buf, int rx_buf_len, - SN_GwInfo *gw_info); -WOLFMQTT_LOCAL int SN_Encode_Connect(byte *tx_buf, int tx_buf_len, - SN_Connect *connect); -WOLFMQTT_LOCAL int SN_Decode_ConnectAck(byte *rx_buf, int rx_buf_len, - SN_ConnectAck *connect_ack); -WOLFMQTT_LOCAL int SN_Decode_WillTopicReq(byte *rx_buf, int rx_buf_len); -WOLFMQTT_LOCAL int SN_Encode_WillTopic(byte *tx_buf, int tx_buf_len, - SN_Will *willTopic); -WOLFMQTT_LOCAL int SN_Decode_WillMsgReq(byte *rx_buf, int rx_buf_len); -WOLFMQTT_LOCAL int SN_Encode_WillMsg(byte *tx_buf, int tx_buf_len, - SN_Will *willMsg); -WOLFMQTT_LOCAL int SN_Encode_WillTopicUpdate(byte *tx_buf, int tx_buf_len, - SN_Will *willTopic); -WOLFMQTT_LOCAL int SN_Decode_WillTopicResponse(byte *rx_buf, int rx_buf_len, - byte *ret_code); -WOLFMQTT_LOCAL int SN_Encode_WillMsgUpdate(byte *tx_buf, int tx_buf_len, - SN_Will *willMsg); -WOLFMQTT_LOCAL int SN_Decode_WillMsgResponse(byte *rx_buf, int rx_buf_len, - byte *ret_code); -WOLFMQTT_LOCAL int SN_Encode_Register(byte *tx_buf, int tx_buf_len, - SN_Register *regist); -WOLFMQTT_LOCAL int SN_Decode_Register(byte *rx_buf, int rx_buf_len, - SN_Register *regist); -WOLFMQTT_LOCAL int SN_Encode_RegAck(byte *tx_buf, int tx_buf_len, - SN_RegAck *regack); -WOLFMQTT_LOCAL int SN_Decode_RegAck(byte *rx_buf, int rx_buf_len, - SN_RegAck *regack); -WOLFMQTT_LOCAL int SN_Encode_Subscribe(byte *tx_buf, int tx_buf_len, - SN_Subscribe *subscribe); -WOLFMQTT_LOCAL int SN_Decode_SubscribeAck(byte* rx_buf, int rx_buf_len, - SN_SubAck *subscribe_ack); -WOLFMQTT_LOCAL int SN_Encode_Publish(byte *tx_buf, int tx_buf_len, - SN_Publish *publish); -WOLFMQTT_LOCAL int SN_Decode_Publish(byte *rx_buf, int rx_buf_len, - SN_Publish *publish); -WOLFMQTT_LOCAL int SN_Encode_PublishResp(byte* tx_buf, int tx_buf_len, - byte type, SN_PublishResp *publish_resp); -WOLFMQTT_LOCAL int SN_Decode_PublishResp(byte* rx_buf, int rx_buf_len, - byte type, SN_PublishResp *publish_resp); -WOLFMQTT_LOCAL int SN_Encode_Unsubscribe(byte *tx_buf, int tx_buf_len, - SN_Unsubscribe *unsubscribe); -WOLFMQTT_LOCAL int SN_Decode_UnsubscribeAck(byte *rx_buf, int rx_buf_len, - SN_UnsubscribeAck *unsubscribe_ack); -WOLFMQTT_LOCAL int SN_Encode_Disconnect(byte *tx_buf, int tx_buf_len, - SN_Disconnect* disconnect); -WOLFMQTT_LOCAL int SN_Decode_Disconnect(byte *rx_buf, int rx_buf_len); -WOLFMQTT_LOCAL int SN_Encode_Ping(byte *tx_buf, int tx_buf_len, - SN_PingReq *ping, byte type); -WOLFMQTT_LOCAL int SN_Decode_Ping(byte *rx_buf, int rx_buf_len); -WOLFMQTT_LOCAL int SN_Packet_Read(struct _MqttClient *client, byte* rx_buf, - int rx_buf_len, int timeout_ms); - -#ifndef WOLFMQTT_NO_ERROR_STRINGS - WOLFMQTT_LOCAL const char* SN_Packet_TypeDesc(SN_MsgType packet_type); -#else - #define SN_Packet_TypeDesc(x) "not compiled in" -#endif -#endif /* WOLFMQTT_SN */ - #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/wolfmqtt/mqtt_sn_client.h b/wolfmqtt/mqtt_sn_client.h new file mode 100644 index 000000000..68124afad --- /dev/null +++ b/wolfmqtt/mqtt_sn_client.h @@ -0,0 +1,236 @@ +/* mqtt_sn_client.h + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Implementation by: David Garske + * Based on specification for MQTT-SN v1.2 + * See http://mqtt.org/documentation for additional MQTT-SN documentation. + */ + +#ifndef WOLFMQTT_SN_CLIENT_H +#define WOLFMQTT_SN_CLIENT_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Windows uses the vs_settings.h file included vis mqtt_types.h */ +#if !defined(WOLFMQTT_USER_SETTINGS) && \ + !defined(_WIN32) && !defined(USE_WINDOWS_API) + /* If options.h is missing use the "./configure" script. Otherwise, copy + * the template "wolfmqtt/options.h.in" into "wolfmqtt/options.h" */ + #include +#endif +#include "wolfmqtt/mqtt_client.h" +#include "wolfmqtt/mqtt_types.h" +#include "wolfmqtt/mqtt_packet.h" +#include "wolfmqtt/mqtt_socket.h" + +#ifdef WOLFMQTT_SN +/*! \brief Encodes and sends the a message to search for a gateway and + waits for the gateway info response message. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param search Pointer to SN_SearchGW structure initialized + with hop radius. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_SearchGW( + MqttClient *client, + SN_SearchGw *search); + +/*! \brief Encodes and sends the Connect packet and waits for the + Connect Acknowledgment packet. If Will is enabled, the gateway + prompts for LWT Topic and Message. Sending an empty will topic + indicates that the client wishes to delete the Will topic and + the Will message stored in the server. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param connect Pointer to SN_Connect structure initialized + with connect parameters + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Connect( + MqttClient *client, + SN_Connect *connect); + +/*! \brief Encodes and sends the MQTT-SN Will Topic Update packet. Sending + a NULL 'will' indicates that the client wishes to delete the + Will topic and the Will message stored in the server. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param will Pointer to SN_Will structure initialized + with topic and message parameters. NULL is valid. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_WillTopicUpdate(MqttClient *client, SN_Will *will); + +/*! \brief Encodes and sends the MQTT-SN Will Message Update packet. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param will Pointer to SN_Will structure initialized + with topic and message parameters. NULL is valid. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_WillMsgUpdate(MqttClient *client, SN_Will *will); + +/*! \brief Encodes and sends the MQTT-SN Register packet and waits for the + Register Acknowledge packet. The Register packet is sent by a + client to a GW for requesting a topic id value for the included + topic name. It is also sent by a GW to inform a client about + the topic id value it has assigned to the included topic name. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param regist Pointer to SN_Register structure + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Register( + MqttClient *client, + SN_Register *regist); + + +/*! \brief Sets a register callback with custom context + * \param client Pointer to MqttClient structure + (uninitialized is okay) + * \param regCb Pointer to register callback function + * \param ctx Pointer to your own context + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_BAD_ARG + */ +WOLFMQTT_API int SN_Client_SetRegisterCallback( + MqttClient *client, + SN_ClientRegisterCb regCb, + void* ctx); + + +/*! \brief Encodes and sends the MQTT-SN Publish packet and waits for the + Publish response (if QoS > 0). + * \note This is a blocking function that will wait for MqttNet.read + * If QoS level = 1 then will wait for PUBLISH_ACK. + * If QoS level = 2 then will wait for PUBLISH_REC then send + PUBLISH_REL and wait for PUBLISH_COMP. + * \param client Pointer to MqttClient structure + * \param publish Pointer to SN_Publish structure initialized + with message data + * Note: SN_Publish and MqttMessage are same + structure. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Publish( + MqttClient *client, + SN_Publish *publish); + +/*! \brief Encodes and sends the MQTT-SN Subscribe packet and waits for the + Subscribe Acknowledgment packet containing the assigned + topic ID. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param subscribe Pointer to SN_Subscribe structure initialized with + subscription topic list and desired QoS. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Subscribe( + MqttClient *client, + SN_Subscribe *subscribe); + +/*! \brief Encodes and sends the MQTT-SN Unsubscribe packet and waits for + the Unsubscribe Acknowledgment packet + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param unsubscribe Pointer to SN_Unsubscribe structure initialized + with topic ID. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Unsubscribe( + MqttClient *client, + SN_Unsubscribe *unsubscribe); + +/*! \brief Encodes and sends the MQTT-SN Disconnect packet. Client may + send the disconnect with a duration to indicate the client is + entering the "asleep" state. + * \note This is a non-blocking function that will try and send using + MqttNet.write + * \param client Pointer to MqttClient structure + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Disconnect( + MqttClient *client); + +/*! \brief Encodes and sends the MQTT-SN Disconnect packet. Client may + send the disconnect with a duration to indicate the client is + entering the "asleep" state. + * \note This is a non-blocking function that will try and send using + MqttNet.write + * \param client Pointer to MqttClient structure + * \param disconnect Pointer to SN_Disconnect structure. NULL is valid. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Disconnect_ex( + MqttClient *client, + SN_Disconnect *disconnect); + + +/*! \brief Encodes and sends the MQTT-SN Ping Request packet and waits + for the Ping Response packet. If client is in the "asleep" + state and wants to notify the gateway that it is entering the + "awake" state, it should add it's client ID to the ping + request. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param ping Pointer to SN_PingReq structure. NULL is valid. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Ping( + MqttClient *client, + SN_PingReq *ping); + +/*! \brief Waits for packets to arrive. Incoming publish messages + will arrive via callback provided in MqttClient_Init. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param timeout_ms Milliseconds until read timeout + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_WaitMessage( + MqttClient *client, + int timeout_ms); + +WOLFMQTT_API int SN_Client_WaitMessage_ex(MqttClient *client, SN_Object* packet_obj, + int timeout_ms); + +#endif /* WOLFMQTT_SN */ + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* WOLFMQTT_SN_CLIENT_H */ + diff --git a/wolfmqtt/mqtt_sn_packet.h b/wolfmqtt/mqtt_sn_packet.h new file mode 100644 index 000000000..6729bc4f5 --- /dev/null +++ b/wolfmqtt/mqtt_sn_packet.h @@ -0,0 +1,497 @@ +/* mqtt_sn_packet.h + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Implementation by: David Garske + * Based on specification for MQTT-SN v1.2 + * See http://mqtt.org/documentation for additional MQTT documentation. + */ + +#ifndef WOLFMQTT_SN_PACKET_H +#define WOLFMQTT_SN_PACKET_H + +#ifdef __cplusplus + extern "C" { +#endif + +#include "wolfmqtt/mqtt_types.h" +#include "wolfmqtt/mqtt_socket.h" + +#ifdef WOLFMQTT_SN + +/* Note that because MQTT-SN does not support message fragmentation and + reassembly, the maximum message length that could be used in a network is + governed by the maximum packet size that is supported by that network, + and not by the maximum length that could be encoded by MQTT-SN. */ +#ifndef WOLFMQTT_SN_MAXPACKET_SIZE +#define WOLFMQTT_SN_MAXPACKET_SIZE 1024 +#endif + +/* The SN_GwAddr field has a variable length and contains the address of a GW. + Its depends on the network over which MQTT-SN operates and is indicated in + the first octet of this field. For example, in a ZigBee network the network + address is 2-octet long. */ +typedef word16 SN_GwAddr ; + +/* RETURN CODE values */ +enum SN_ReturnCodes { + SN_RC_ACCEPTED = 0x00, + SN_RC_CONGESTION = 0x01, + SN_RC_INVTOPICNAME = 0x02, + SN_RC_NOTSUPPORTED = 0x03 + /* 0x04 - 0xFF reserved */ +}; + +/* MESSAGE HEADER */ +/* Message types: Located in last byte of header */ +typedef enum _SN_MsgType { + SN_MSG_TYPE_ADVERTISE = 0x00, + SN_MSG_TYPE_SEARCHGW = 0x01, + SN_MSG_TYPE_GWINFO = 0x02, + /* 0x03 reserved */ + SN_MSG_TYPE_CONNECT = 0x04, + SN_MSG_TYPE_CONNACK = 0x05, + SN_MSG_TYPE_WILLTOPICREQ = 0x06, + SN_MSG_TYPE_WILLTOPIC = 0x07, + SN_MSG_TYPE_WILLMSGREQ = 0x08, + SN_MSG_TYPE_WILLMSG = 0x09, + SN_MSG_TYPE_REGISTER = 0x0A, + SN_MSG_TYPE_REGACK = 0x0B, + SN_MSG_TYPE_PUBLISH = 0x0C, + SN_MSG_TYPE_PUBACK = 0x0D, + SN_MSG_TYPE_PUBCOMP = 0x0E, + SN_MSG_TYPE_PUBREC = 0x0F, + SN_MSG_TYPE_PUBREL = 0x10, + /* 0x11 reserved */ + SN_MSG_TYPE_SUBSCRIBE = 0x12, + SN_MSG_TYPE_SUBACK = 0x13, + SN_MSG_TYPE_UNSUBSCRIBE = 0x14, + SN_MSG_TYPE_UNSUBACK = 0x15, + SN_MSG_TYPE_PING_REQ = 0x16, + SN_MSG_TYPE_PING_RESP = 0x17, + SN_MSG_TYPE_DISCONNECT = 0x18, + /* 0x19 reserved */ + SN_MSG_TYPE_WILLTOPICUPD = 0x1A, + SN_MSG_TYPE_WILLTOPICRESP = 0x1B, + SN_MSG_TYPE_WILLMSGUPD = 0x1C, + SN_MSG_TYPE_WILLMSGRESP = 0x1D, + /* 0x1E - 0xFD reserved */ + SN_MSG_TYPE_ENCAPMSG = 0xFE, /* Encapsulated message */ + /* 0xFF reserved */ + SN_MSG_TYPE_RESERVED = 0xFF, + SN_MSG_TYPE_ANY = 0xFF +} SN_MsgType; + +/* Topic ID types */ +enum SN_TopicId_Types { + SN_TOPIC_ID_TYPE_NORMAL = 0x0, + SN_TOPIC_ID_TYPE_PREDEF = 0x1, + SN_TOPIC_ID_TYPE_SHORT = 0x2 +}; + +enum SN_PacketFlags { + SN_PACKET_FLAG_TOPICIDTYPE_MASK = 0x3, + SN_PACKET_FLAG_CLEANSESSION = 0x4, + SN_PACKET_FLAG_WILL = 0x8, + SN_PACKET_FLAG_RETAIN = 0x10, + SN_PACKET_FLAG_QOS_MASK = 0x60, + SN_PACKET_FLAG_QOS_SHIFT = 0x5, + SN_PACKET_FLAG_DUPLICATE = 0x80 +}; + +/* Message Header: Size is variable 2 - 4 bytes */ + +/* If the first byte of the packet len is 0x01, then the packet size is + greater than 0xFF and is stored in the next two bytes */ +#define SN_PACKET_LEN_IND 0x01 + +#define SN_PACKET_MAX_SMALL_SIZE 0xFF + +/* Gateway (GW) messages */ +/* Advertise message */ +typedef struct _SN_AdvertiseMsg { + MqttMsgStat stat; + + byte gwId; /* ID of the gateway that sent this message */ + word16 duration; /* Seconds until next Advertise + is broadcast by this gateway */ +} SN_Advertise; + +typedef struct _SN_GwInfo { + MqttMsgStat stat; + + byte gwId; /* ID of the gateway that sent this message */ + SN_GwAddr* gwAddr; /* Address of the indicated gateway */ +} SN_GwInfo; + +typedef struct _SN_SearchGw { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + byte radius; /* Broadcast radius (in hops) */ + SN_GwInfo gwInfo; +} SN_SearchGw; + +/* Connect Protocol */ +#define SN_PROTOCOL_ID_1 0x01 +#define SN_PROTOCOL_ID SN_PROTOCOL_ID_1 + +#define SN_CLIENTID_MAX_LEN 23 + +/* Connect Ack message structure */ +typedef struct _SN_ConnectAck { + MqttMsgStat stat; + + byte return_code; +} SN_ConnectAck; + +/* WILL TOPIC */ +typedef struct _SN_WillTopicUpd { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + byte flags; + char* willTopic; /* contains the Will topic name */ +} SN_WillTopicUpd; + +typedef struct _SN_WillMsgUpd { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + char* willMsg; +} SN_WillMsgUpd; + +typedef struct _SN_WillTopicResp { + MqttMsgStat stat; + + byte return_code; +} SN_WillTopicResp; + +typedef SN_WillTopicResp SN_WillMsgResp; + +typedef union _SN_WillResp { + SN_WillMsgResp msgResp; + SN_WillMsgUpd msgUpd; + SN_WillTopicResp topicResp; + SN_WillTopicUpd topicUpd; +} SN_WillResp; + +typedef struct _SN_Will { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + byte qos; + byte retain; + const char* willTopic; + byte* willMsg; + word16 willMsgLen; + + SN_WillResp resp; +} SN_Will; + +/* Connect */ +typedef struct _SN_Connect { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + word16 keep_alive_sec; + byte clean_session; + const char *client_id; + + /* Protocol version: 1=v1.2 (default) */ + byte protocol_level; + + /* Optional Last will and testament */ + byte enable_lwt; + SN_Will will; + + /* Ack data */ + SN_ConnectAck ack; +} SN_Connect; + +/* REGISTER protocol */ +typedef struct _SN_RegAck { + MqttMsgStat stat; + + word16 topicId; + word16 packet_id; + byte return_code; +} SN_RegAck; + +typedef struct _SN_Register { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + word16 topicId; + word16 packet_id; + const char* topicName; + SN_RegAck regack; +} SN_Register; + +/* PUBLISH RESPONSE */ +/* This is the response struct for PUBREC, PUBREL, and PUBCOMP */ +/* If QoS = 0: No response */ +/* If QoS = 1: Expect response packet with type = SN_MSG_TYPE_PUBACK */ +/* If QoS = 2: Expect response packet with type = SN_MSG_TYPE_PUBREC */ +/* Message ID required if QoS is 1 or 2 */ +/* If QoS = 2: Send SN_MSG_TYPE_PUBREL with msgId to complete + QoS2 protocol exchange */ +/* Expect response packet with type = SN_MSG_TYPE_PUBCOMP */ +typedef struct _SN_PublishResp { + MqttMsgStat stat; + + word16 packet_id; + word16 topicId; /* PUBACK Only */ + byte return_code; /* PUBACK Only */ +} SN_PublishResp; + +/* PUBLISH protocol */ +typedef struct _SN_Publish { + MqttMsgStat stat; /* must be first member at top */ +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + /* BEGIN: THIS SECTION NEEDS TO MATCH MqttMessage */ + word16 packet_id; + byte type; + MqttQoS qos; + byte retain; + byte duplicate; + byte topic_type; + byte return_code; + + const char *topic_name; /* Pointer is valid only when + msg_new set in callback */ + word16 topic_name_len; + word32 total_len; /* Payload total length */ + byte *buffer; /* Payload buffer */ + word32 buffer_len; /* Payload buffer length */ + word32 buffer_pos; /* Payload buffer position */ + + /* Used internally for TX/RX buffers */ + byte buffer_new; /* flag to indicate new message */ + word32 intBuf_len; /* Buffer length */ + word32 intBuf_pos; /* Buffer position */ + + void* ctx; /* user supplied context for publish callbacks */ + /* END: THIS SECTION NEEDS TO MATCH MqttMessage */ + + SN_PublishResp resp; +} SN_Publish; + +/* SUBSCRIBE ACK */ +typedef struct _SN_SubAck { + MqttMsgStat stat; + + byte flags; + word16 topicId; + word16 packet_id; + byte return_code; +} SN_SubAck; + +/* SUBSCRIBE */ +typedef struct _SN_Subscribe { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + byte duplicate; + byte qos; + word16 packet_id; + byte topic_type; + /* 5.3.12 TopicName + The TopicName field has a variable length and contains an UTF8-encoded + string that specifies the topic name.*/ + const char* topicNameId; /* Contains topic name, ID, + or short name as indicated in topic type */ + SN_SubAck subAck; +} SN_Subscribe; + +/* UNSUBSCRIBE RESPONSE ACK */ +typedef struct _SN_UnsubscribeAck { + MqttMsgStat stat; /* must be first member at top */ + + word16 packet_id; +} SN_UnsubscribeAck; + +/* UNSUBSCRIBE */ +typedef struct _SN_Unsubscribe { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + byte duplicate; + byte qos; + word16 packet_id; + byte topic_type; + /* 5.3.12 TopicName + The TopicName field has a variable length and contains an UTF8-encoded + string that specifies the topic name.*/ + const char* topicNameId; /* Contains topic name, ID, + or short name as indicated in topic type */ + SN_UnsubscribeAck ack; +} SN_Unsubscribe; + +/* PING REQUEST / PING RESPONSE */ +typedef struct _SN_PingReq { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + /* clientId is optional and is included by a “sleeping” client when it + goes to the “awake” state and is waiting for messages sent by the + server/gateway. */ + char *clientId; +} SN_PingReq; + +/* DISCONNECT */ +typedef struct _SN_Disconnect { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + /* sleepTmr is optional and is included by a “sleeping” client + that wants to go the “asleep” state. The receipt of this message + is also acknowledged by the gateway by means of a DISCONNECT message + (without a duration field).*/ + word16 sleepTmr; +} SN_Disconnect; + +typedef union _SN_Object { + SN_Advertise advertise; + SN_GwInfo gwInfo; + SN_SearchGw searchGw; + + SN_Will will; + SN_Connect connect; + SN_ConnectAck connectAck; + + SN_Register reg; + SN_RegAck regAck; + + SN_Publish publish; + SN_PublishResp publishResp; + + SN_Subscribe sub; + SN_SubAck subAck; + SN_Unsubscribe unSub; + SN_UnsubscribeAck unSubAck; + + SN_PingReq pingReq; + SN_Disconnect disconnect; + + SN_WillMsgUpd willMsgUpd; + SN_WillMsgResp willMsgResp; + + SN_WillTopicUpd willTopicUpd; + SN_WillTopicResp willTopicResp; +} SN_Object; + + +/* Forward Encapsulation */ +// TODO + +WOLFMQTT_LOCAL int SN_Decode_Header(byte *rx_buf, int rx_buf_len, + SN_MsgType* p_packet_type, word16* p_packet_id); +WOLFMQTT_LOCAL int SN_Decode_Advertise(byte *rx_buf, int rx_buf_len, + SN_Advertise *gw_info); +WOLFMQTT_LOCAL int SN_Encode_SearchGW(byte *tx_buf, int tx_buf_len, byte hops); +WOLFMQTT_LOCAL int SN_Decode_GWInfo(byte *rx_buf, int rx_buf_len, + SN_GwInfo *gw_info); +WOLFMQTT_LOCAL int SN_Encode_Connect(byte *tx_buf, int tx_buf_len, + SN_Connect *connect); +WOLFMQTT_LOCAL int SN_Decode_ConnectAck(byte *rx_buf, int rx_buf_len, + SN_ConnectAck *connect_ack); +WOLFMQTT_LOCAL int SN_Decode_WillTopicReq(byte *rx_buf, int rx_buf_len); +WOLFMQTT_LOCAL int SN_Encode_WillTopic(byte *tx_buf, int tx_buf_len, + SN_Will *willTopic); +WOLFMQTT_LOCAL int SN_Decode_WillMsgReq(byte *rx_buf, int rx_buf_len); +WOLFMQTT_LOCAL int SN_Encode_WillMsg(byte *tx_buf, int tx_buf_len, + SN_Will *willMsg); +WOLFMQTT_LOCAL int SN_Encode_WillTopicUpdate(byte *tx_buf, int tx_buf_len, + SN_Will *willTopic); +WOLFMQTT_LOCAL int SN_Decode_WillTopicResponse(byte *rx_buf, int rx_buf_len, + byte *ret_code); +WOLFMQTT_LOCAL int SN_Encode_WillMsgUpdate(byte *tx_buf, int tx_buf_len, + SN_Will *willMsg); +WOLFMQTT_LOCAL int SN_Decode_WillMsgResponse(byte *rx_buf, int rx_buf_len, + byte *ret_code); +WOLFMQTT_LOCAL int SN_Encode_Register(byte *tx_buf, int tx_buf_len, + SN_Register *regist); +WOLFMQTT_LOCAL int SN_Decode_Register(byte *rx_buf, int rx_buf_len, + SN_Register *regist); +WOLFMQTT_LOCAL int SN_Encode_RegAck(byte *tx_buf, int tx_buf_len, + SN_RegAck *regack); +WOLFMQTT_LOCAL int SN_Decode_RegAck(byte *rx_buf, int rx_buf_len, + SN_RegAck *regack); +WOLFMQTT_LOCAL int SN_Encode_Subscribe(byte *tx_buf, int tx_buf_len, + SN_Subscribe *subscribe); +WOLFMQTT_LOCAL int SN_Decode_SubscribeAck(byte* rx_buf, int rx_buf_len, + SN_SubAck *subscribe_ack); +WOLFMQTT_LOCAL int SN_Encode_Publish(byte *tx_buf, int tx_buf_len, + SN_Publish *publish); +WOLFMQTT_LOCAL int SN_Decode_Publish(byte *rx_buf, int rx_buf_len, + SN_Publish *publish); +WOLFMQTT_LOCAL int SN_Encode_PublishResp(byte* tx_buf, int tx_buf_len, + byte type, SN_PublishResp *publish_resp); +WOLFMQTT_LOCAL int SN_Decode_PublishResp(byte* rx_buf, int rx_buf_len, + byte type, SN_PublishResp *publish_resp); +WOLFMQTT_LOCAL int SN_Encode_Unsubscribe(byte *tx_buf, int tx_buf_len, + SN_Unsubscribe *unsubscribe); +WOLFMQTT_LOCAL int SN_Decode_UnsubscribeAck(byte *rx_buf, int rx_buf_len, + SN_UnsubscribeAck *unsubscribe_ack); +WOLFMQTT_LOCAL int SN_Encode_Disconnect(byte *tx_buf, int tx_buf_len, + SN_Disconnect* disconnect); +WOLFMQTT_LOCAL int SN_Decode_Disconnect(byte *rx_buf, int rx_buf_len); +WOLFMQTT_LOCAL int SN_Encode_Ping(byte *tx_buf, int tx_buf_len, + SN_PingReq *ping, byte type); +WOLFMQTT_LOCAL int SN_Decode_Ping(byte *rx_buf, int rx_buf_len); +WOLFMQTT_LOCAL int SN_Packet_Read(struct _MqttClient *client, byte* rx_buf, + int rx_buf_len, int timeout_ms); + +#ifndef WOLFMQTT_NO_ERROR_STRINGS + WOLFMQTT_LOCAL const char* SN_Packet_TypeDesc(SN_MsgType packet_type); +#else + #define SN_Packet_TypeDesc(x) "not compiled in" +#endif +#endif /* WOLFMQTT_SN */ + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* WOLFMQTT_SN_PACKET_H */