update qcloud sdk

1. iot-hub sdk update to 3.2.0
2. iot-explorer update to 3.1.1
This commit is contained in:
daishengdong
2020-05-07 11:11:04 +08:00
parent c8e39739d3
commit 3e631cd96a
594 changed files with 47287 additions and 44165 deletions

View File

@@ -0,0 +1,360 @@
/*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright (C) 2018-2020 THL A29 Limited, a Tencent company. All rights reserved.
* Licensed under the MIT License (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "coap_client.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "qcloud_iot_ca.h"
#include "qcloud_iot_common.h"
#include "qcloud_iot_device.h"
#include "qcloud_iot_export.h"
#include "qcloud_iot_import.h"
#include "utils_base64.h"
#include "utils_param_check.h"
static uint16_t _get_random_start_packet_id(void)
{
srand((unsigned)time(NULL));
return rand() % 65536 + 1;
}
DeviceInfo *IOT_COAP_GetDeviceInfo(void *pClient)
{
POINTER_SANITY_CHECK(pClient, NULL);
CoAPClient *coap_client = (CoAPClient *)pClient;
return &coap_client->device_info;
}
void *IOT_COAP_Construct(CoAPInitParams *pParams)
{
POINTER_SANITY_CHECK(pParams, NULL);
STRING_PTR_SANITY_CHECK(pParams->product_id, NULL);
STRING_PTR_SANITY_CHECK(pParams->device_name, NULL);
CoAPClient *coap_client = NULL;
if ((coap_client = (CoAPClient *)HAL_Malloc(sizeof(CoAPClient))) == NULL) {
Log_e("memory not enough to malloc COAPClient");
return NULL;
}
int rc = qcloud_iot_coap_init(coap_client, pParams);
if (rc != QCLOUD_RET_SUCCESS) {
Log_e("coap init failed: %d", rc);
HAL_Free(coap_client);
return NULL;
}
if (coap_client->network_stack.connect(&coap_client->network_stack) != QCLOUD_RET_SUCCESS) {
Log_e("coap connect to host: %s:%d failed: %d", coap_client->network_stack.host,
coap_client->network_stack.port, rc);
HAL_Free(coap_client);
return NULL;
} else {
Log_i("coap connect to host: %s:%d success", coap_client->network_stack.host, coap_client->network_stack.port);
}
coap_client_auth(coap_client);
while (coap_client->is_authed == -1) {
IOT_COAP_Yield(coap_client, 200);
}
if (coap_client->is_authed == COAP_TRUE) {
Log_i("device auth successfully, connid: %s", coap_client->conn_id);
return coap_client;
} else {
Log_e("device auth failed, connid: %s", coap_client->conn_id);
void *client = coap_client;
IOT_COAP_Destroy(&client);
return NULL;
}
}
void IOT_COAP_Destroy(void **pClient)
{
POINTER_SANITY_CHECK_RTN(*pClient);
CoAPClient *coap_client = (CoAPClient *)(*pClient);
if ((coap_client)->network_stack.handle != 0) {
(coap_client)->network_stack.disconnect(&(coap_client)->network_stack);
}
list_destroy(coap_client->message_list);
HAL_MutexDestroy(coap_client->lock_send_buf);
HAL_MutexDestroy(coap_client->lock_list_wait_ack);
if (coap_client->auth_token != NULL) {
HAL_Free(coap_client->auth_token);
coap_client->auth_token = NULL;
}
coap_client->auth_token_len = 0;
coap_client->is_authed = -1;
HAL_Free(*pClient);
*pClient = NULL;
Log_i("coap release!");
}
int IOT_COAP_Yield(void *pClient, uint32_t timeout_ms)
{
IOT_FUNC_ENTRY;
POINTER_SANITY_CHECK(pClient, QCLOUD_ERR_INVAL);
CoAPClient *coap_client = (CoAPClient *)pClient;
return coap_message_cycle(coap_client, timeout_ms);
}
int IOT_COAP_SendMessage(void *pClient, char *topicName, SendMsgParams *sendParams)
{
IOT_FUNC_ENTRY
POINTER_SANITY_CHECK(pClient, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(topicName, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(sendParams, QCLOUD_ERR_INVAL);
if (strlen(topicName) > URI_PATH_MAX_LEN) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MAX_TOPIC_LENGTH);
}
int ret = QCLOUD_RET_SUCCESS;
CoAPClient *coap_client = (CoAPClient *)pClient;
CoAPMessage send_message = DEFAULT_COAP_MESSAGE;
coap_message_type_set(&send_message, COAP_MSG_CON);
coap_message_code_set(&send_message, COAP_MSG_REQ, COAP_MSG_POST);
coap_message_id_set(&send_message, get_next_coap_msg_id(coap_client));
char message_token[8] = {0};
int len = get_coap_message_token(pClient, message_token);
coap_message_token_set(&send_message, message_token, len);
send_message.pay_load = (char *)HAL_Malloc(sendParams->pay_load_len);
if (NULL == send_message.pay_load)
IOT_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
coap_message_payload_set(&send_message, sendParams->pay_load, sendParams->pay_load_len);
coap_message_option_add(&send_message, COAP_MSG_URI_PATH, strlen(topicName), topicName);
coap_message_option_add(&send_message, COAP_MSG_AUTH_TOKEN, coap_client->auth_token_len, coap_client->auth_token);
if (sendParams->need_resp == false) {
coap_message_option_add(&send_message, COAP_MSG_NEED_RESP, 1, "0");
coap_message_context_set(&send_message, sendParams->user_context);
} else {
coap_message_option_add(&send_message, COAP_MSG_NEED_RESP, 1, "1");
coap_message_callback_set(&send_message, sendParams->resp_callback);
coap_message_context_set(&send_message, sendParams->user_context);
}
ret = coap_message_send(coap_client, &send_message);
HAL_Free(send_message.pay_load);
if (ret != QCLOUD_RET_SUCCESS) {
IOT_FUNC_EXIT_RC(ret)
}
IOT_FUNC_EXIT_RC(send_message.msg_id)
}
int IOT_COAP_GetMessageId(void *pMessage)
{
IOT_FUNC_ENTRY
POINTER_SANITY_CHECK(pMessage, QCLOUD_ERR_INVAL);
CoAPMessage *message = (CoAPMessage *)pMessage;
IOT_FUNC_EXIT_RC(message->msg_id)
}
int IOT_COAP_GetMessagePayload(void *pMessage, char **payload, int *payloadLen)
{
IOT_FUNC_ENTRY
POINTER_SANITY_CHECK(pMessage, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(payload, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(payloadLen, QCLOUD_ERR_INVAL);
CoAPMessage *message = (CoAPMessage *)pMessage;
if (message->code_class != COAP_MSG_SUCCESS || message->code_detail != COAP_MSG_CODE_205_CONTENT) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE)
}
*payload = message->pay_load;
*payloadLen = message->pay_load_len;
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS)
}
int IOT_COAP_GetMessageCode(void *pMessage)
{
IOT_FUNC_ENTRY
POINTER_SANITY_CHECK(pMessage, QCLOUD_ERR_INVAL);
CoAPMessage *message = (CoAPMessage *)pMessage;
int rc = COAP_EVENT_ACK_TIMEOUT;
if (message->code_class == COAP_MSG_SUCCESS) {
rc = COAP_EVENT_RECEIVE_RESPCONTENT;
} else if (message->code_class == COAP_MSG_CLIENT_ERR) {
if (message->code_detail == COAP_MSG_CODE_401_UNAUTHORIZED) {
rc = COAP_EVENT_UNAUTHORIZED;
} else {
rc = COAP_EVENT_FORBIDDEN;
}
} else if (message->code_class == COAP_MSG_SERVER_ERR) {
rc = COAP_EVENT_INTERNAL_SERVER_ERROR;
} else if (message->code_class == COAP_MSG_SDKINTERNAL_ERR) {
rc = COAP_EVENT_SEPRESP_TIMEOUT;
} else {
/**
* no more error code
*/
Log_e("not supported code class: %d", message->code_class);
}
IOT_FUNC_EXIT_RC(rc)
}
int qcloud_iot_coap_init(CoAPClient *pClient, CoAPInitParams *pParams)
{
IOT_FUNC_ENTRY;
POINTER_SANITY_CHECK(pClient, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(pParams, QCLOUD_ERR_INVAL);
memset(pClient, 0x0, sizeof(CoAPClient));
int rc = iot_device_info_set(&(pClient->device_info), pParams->product_id, pParams->device_name);
if (rc != QCLOUD_RET_SUCCESS) {
Log_e("failed to set device info: %d", rc);
return rc;
}
int size =
HAL_Snprintf(pClient->host_addr, HOST_STR_LENGTH, "%s.%s", pParams->product_id, QCLOUD_IOT_COAP_DEIRECT_DOMAIN);
if (size < 0 || size > HOST_STR_LENGTH - 1) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
pClient->is_authed = -1;
if (pParams->command_timeout < MIN_COMMAND_TIMEOUT)
pParams->command_timeout = MIN_COMMAND_TIMEOUT;
if (pParams->command_timeout > MAX_COMMAND_TIMEOUT)
pParams->command_timeout = MAX_COMMAND_TIMEOUT;
pClient->command_timeout_ms = pParams->command_timeout;
#ifndef AUTH_WITH_NOTLS
#ifdef AUTH_MODE_CERT
Log_d("cert file: %s", pParams->cert_file);
Log_d("key file: %s", pParams->key_file);
strncpy(pClient->cert_file_path, pParams->cert_file, FILE_PATH_MAX_LEN - 1);
strncpy(pClient->key_file_path, pParams->key_file, FILE_PATH_MAX_LEN - 1);
// device param for TLS connection
pClient->network_stack.ssl_connect_params.cert_file = pParams->cert_file;
pClient->network_stack.ssl_connect_params.key_file = pParams->key_file;
pClient->network_stack.ssl_connect_params.ca_crt = iot_ca_get();
pClient->network_stack.ssl_connect_params.ca_crt_len = strlen(pClient->network_stack.ssl_connect_params.ca_crt);
#else
pClient->network_stack.ssl_connect_params.psk_id = pClient->device_info.client_id;
if (pParams->device_secret != NULL) {
size_t src_len = strlen(pParams->device_secret);
size_t len;
memset(pClient->psk_decode, 0x00, DECODE_PSK_LENGTH);
qcloud_iot_utils_base64decode(pClient->psk_decode, DECODE_PSK_LENGTH, &len,
(unsigned char *)pParams->device_secret, src_len);
pClient->network_stack.ssl_connect_params.psk = (char *)pClient->psk_decode;
pClient->network_stack.ssl_connect_params.psk_length = len;
pClient->network_stack.ssl_connect_params.ca_crt = NULL;
pClient->network_stack.ssl_connect_params.ca_crt_len = 0;
} else {
Log_e("psk is empty!");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
}
#endif
pClient->network_stack.host = pClient->host_addr;
pClient->network_stack.port = COAP_SERVER_PORT;
#else
pClient->network_stack.host = pClient->host_addr;
pClient->network_stack.port = COAP_SERVER_PORT;
#endif
pClient->auth_token = NULL;
pClient->auth_token_len = 0;
// next_msg_id, random: 1- 65536
pClient->next_msg_id = _get_random_start_packet_id();
pClient->read_buf_size = COAP_RECVMSG_MAX_BUFLEN;
pClient->send_buf_size = COAP_SENDMSG_MAX_BUFLEN;
pClient->lock_send_buf = HAL_MutexCreate();
if (pClient->lock_send_buf == NULL) {
Log_e("create send buf lock failed");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
pClient->lock_list_wait_ack = HAL_MutexCreate();
if (pClient->lock_list_wait_ack == NULL) {
Log_e("create send buf lock failed");
goto error;
}
pClient->message_list = list_new();
pClient->max_retry_count = pParams->max_retry_count;
pClient->event_handle = pParams->event_handle;
// init network stack
qcloud_iot_coap_network_init(&(pClient->network_stack));
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
error:
if (pClient->lock_send_buf != NULL) {
HAL_MutexDestroy(pClient->lock_send_buf);
pClient->lock_send_buf = NULL;
}
if (pClient->lock_list_wait_ack != NULL) {
HAL_MutexDestroy(pClient->lock_list_wait_ack);
pClient->lock_list_wait_ack = NULL;
}
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,147 @@
/*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright (C) 2018-2020 THL A29 Limited, a Tencent company. All rights reserved.
* Licensed under the MIT License (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <string.h>
#include <time.h>
#include "coap_client.h"
#include "qcloud_iot_common.h"
#include "qcloud_iot_device.h"
#include "qcloud_iot_export.h"
#include "qcloud_iot_import.h"
#include "utils_param_check.h"
static void _coap_client_auth_callback(void *message, void *userContext)
{
IOT_FUNC_ENTRY
POINTER_SANITY_CHECK_RTN(message);
POINTER_SANITY_CHECK_RTN(userContext);
CoAPClient * client = (CoAPClient *)userContext;
CoAPMessage *msg = (CoAPMessage *)message;
if (msg->code_class == COAP_MSG_SUCCESS && msg->code_detail == COAP_MSG_CODE_205_CONTENT) {
Log_i("auth token message success, code_class: %d code_detail: %d", msg->code_class, msg->code_detail);
if (msg->pay_load_len == 0 || msg->pay_load == NULL || strlen(msg->pay_load) == 0) {
client->is_authed = COAP_FALSE;
Log_e("auth token response empty");
} else {
client->auth_token_len = msg->pay_load_len;
client->auth_token = HAL_Malloc(client->auth_token_len);
strncpy(client->auth_token, msg->pay_load, client->auth_token_len);
client->is_authed = COAP_TRUE;
Log_d("auth_token_len = %d, auth_token = %.*s", client->auth_token_len, client->auth_token_len,
client->auth_token);
}
} else {
client->is_authed = COAP_FALSE;
Log_e("auth token message failed, code_class: %d code_detail: %d", msg->code_class, msg->code_detail);
}
IOT_FUNC_EXIT
}
static void get_coap_next_conn_id(CoAPClient *pclient)
{
int i = 0;
srand((unsigned)time(0));
for (i = 0; i < COAP_MAX_CONN_ID_LEN - 1; i++) {
int flag = rand() % 3;
switch (flag) {
case 0:
pclient->conn_id[i] = (rand() % 26) + 'a';
break;
case 1:
pclient->conn_id[i] = (rand() % 26) + 'A';
break;
case 2:
pclient->conn_id[i] = (rand() % 10) + '0';
break;
}
}
pclient->conn_id[COAP_MAX_CONN_ID_LEN - 1] = '\0';
return;
}
int coap_client_auth(CoAPClient *pclient)
{
IOT_FUNC_ENTRY
POINTER_SANITY_CHECK(pclient, QCLOUD_ERR_COAP_NULL);
int ret = QCLOUD_RET_SUCCESS;
CoAPClient *coap_client = (CoAPClient *)pclient;
CoAPMessage send_message = DEFAULT_COAP_MESSAGE;
coap_message_type_set(&send_message, COAP_MSG_CON);
coap_message_code_set(&send_message, COAP_MSG_REQ, COAP_MSG_POST);
coap_message_id_set(&send_message, get_next_coap_msg_id(coap_client));
char message_token[8] = {0};
int len = get_coap_message_token(pclient, message_token);
coap_message_token_set(&send_message, message_token, len);
len = MAX_SIZE_OF_PRODUCT_ID + strlen(pclient->device_info.device_name) + strlen(COAP_AUTH_URI) + 4;
char *auth_path = (char *)HAL_Malloc(len);
HAL_Snprintf(auth_path, len, "%s/%s/%s", pclient->device_info.product_id, pclient->device_info.device_name,
COAP_AUTH_URI);
coap_message_option_add(&send_message, COAP_MSG_URI_PATH, strlen(auth_path), auth_path);
HAL_Free(auth_path);
coap_message_option_add(&send_message, COAP_MSG_NEED_RESP, 1, "0");
coap_message_callback_set(&send_message, _coap_client_auth_callback);
coap_message_context_set(&send_message, pclient);
get_coap_next_conn_id(coap_client);
send_message.pay_load_len = strlen(QCLOUD_IOT_DEVICE_SDK_APPID) + COAP_MAX_CONN_ID_LEN + 2;
send_message.pay_load = (char *)HAL_Malloc(send_message.pay_load_len);
if (NULL == send_message.pay_load)
IOT_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
char *temp_pay_load = (char *)HAL_Malloc(send_message.pay_load_len);
if (NULL == temp_pay_load) {
HAL_Free(send_message.pay_load);
IOT_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
}
HAL_Snprintf(temp_pay_load, send_message.pay_load_len, "%s;%s", QCLOUD_IOT_DEVICE_SDK_APPID, coap_client->conn_id);
coap_message_payload_set(&send_message, temp_pay_load, send_message.pay_load_len);
ret = coap_message_send(coap_client, &send_message);
HAL_Free(temp_pay_load);
HAL_Free(send_message.pay_load);
IOT_FUNC_EXIT_RC(ret)
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,284 @@
/*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright (C) 2018-2020 THL A29 Limited, a Tencent company. All rights reserved.
* Licensed under the MIT License (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "coap_client.h"
#include "qcloud_iot_export.h"
#include "qcloud_iot_import.h"
#include "utils_param_check.h"
/**
* @brief Free an option structure that was allocated by coap_msg_op_new
*
* @param[in,out] op Pointer to the option structure
*/
static void _coap_msg_op_delete(CoAPMsgOption *option)
{
IOT_FUNC_ENTRY
HAL_Free(option->val);
HAL_Free(option);
IOT_FUNC_EXIT
}
/**
* @brief Deinitialise an option linked-list structure
*
* @param[in,out] list Pointer to an option linked-list structure
*/
static void _coap_msg_op_list_destroy(CoAPMsgOptionList *list)
{
IOT_FUNC_ENTRY
CoAPMsgOption *prev = NULL;
CoAPMsgOption *option = NULL;
option = list->first;
while (option != NULL) {
prev = option;
option = option->next;
_coap_msg_op_delete(prev);
}
memset(list, 0, sizeof(CoAPMsgOptionList));
IOT_FUNC_EXIT
}
uint16_t get_next_coap_msg_id(CoAPClient *pClient)
{
IOT_FUNC_ENTRY
POINTER_SANITY_CHECK(pClient, QCLOUD_ERR_INVAL);
unsigned int id = 0;
id = pClient->next_msg_id =
(uint16_t)((COAP_MSG_MAX_MSG_ID == pClient->next_msg_id) ? 1 : (pClient->next_msg_id + 1));
IOT_FUNC_EXIT_RC(id)
}
unsigned int get_coap_message_token(CoAPClient *client, char *tokenData)
{
unsigned int value = client->message_token;
tokenData[0] = ((value & 0x00FF) >> 0);
tokenData[1] = ((value & 0xFF00) >> 8);
tokenData[2] = ((value & 0xFF0000) >> 16);
tokenData[3] = ((value & 0xFF000000) >> 24);
client->message_token++;
return sizeof(unsigned int);
}
int coap_message_type_set(CoAPMessage *message, unsigned type)
{
IOT_FUNC_ENTRY
if ((type != COAP_MSG_CON) && (type != COAP_MSG_NON) && (type != COAP_MSG_ACK) && (type != COAP_MSG_RST)) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_INVAL)
}
message->type = type;
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS)
}
int coap_message_code_set(CoAPMessage *message, unsigned code_class, unsigned code_detail)
{
IOT_FUNC_ENTRY
if (code_class > COAP_MSG_MAX_CODE_CLASS) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_INVAL)
}
if (code_detail > COAP_MSG_MAX_CODE_DETAIL) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_INVAL)
}
message->code_class = code_class;
message->code_detail = code_detail;
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS)
}
int coap_message_id_set(CoAPMessage *message, unsigned msg_id)
{
IOT_FUNC_ENTRY
if (msg_id > COAP_MSG_MAX_MSG_ID) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_INVAL)
}
message->msg_id = msg_id;
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS)
}
int coap_message_token_set(CoAPMessage *message, char *buf, size_t len)
{
IOT_FUNC_ENTRY
if (len > COAP_MSG_MAX_TOKEN_LEN) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_INVAL)
}
memcpy(message->token, buf, len);
message->token_len = len;
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS)
}
int coap_message_payload_set(CoAPMessage *message, char *buf, size_t len)
{
IOT_FUNC_ENTRY
message->pay_load_len = 0;
if (len > 0) {
if (message->pay_load == NULL) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE)
}
memcpy(message->pay_load, buf, len);
message->pay_load_len = len;
}
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS)
}
int coap_message_option_add(CoAPMessage *message, unsigned num, unsigned len, const char *val)
{
IOT_FUNC_ENTRY
CoAPMsgOption * prev = NULL;
CoAPMsgOption * option = NULL;
CoAPMsgOptionList *list = &message->op_list;
if ((option = qcloud_iot_coap_option_init(num, len, val)) == NULL) {
Log_e("allocate new option failed.");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE)
}
if (list->first == NULL) {
/* empty list */
list->first = option;
list->last = option;
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS)
}
if (option->option_num < list->first->option_num) {
/* start of the list */
option->next = list->first;
list->first = option;
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS)
}
prev = list->first;
while (prev != list->last) {
/* middle of the list */
if ((prev->option_num <= option->option_num) && (option->option_num < prev->next->option_num)) {
option->next = prev->next;
prev->next = option;
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS)
}
prev = prev->next;
}
/* end of the list */
list->last->next = option;
list->last = option;
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS)
}
int coap_message_callback_set(CoAPMessage *message, OnRespCallback callback)
{
IOT_FUNC_ENTRY
message->handler = callback;
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS)
}
int coap_message_context_set(CoAPMessage *message, void *userContext)
{
IOT_FUNC_ENTRY
message->user_context = userContext;
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS)
}
CoAPMsgOption *qcloud_iot_coap_option_init(unsigned num, unsigned len, const char *val)
{
IOT_FUNC_ENTRY
CoAPMsgOption *option = NULL;
option = (CoAPMsgOption *)HAL_Malloc(sizeof(CoAPMsgOption));
if (option == NULL) {
Log_e("no space to malloc option");
return NULL;
}
option->option_num = num;
option->val_len = len;
option->val = (char *)HAL_Malloc(len);
if (option->val == NULL) {
HAL_Free(option);
option = NULL;
Log_e("no space to malloc option");
return NULL;
}
memcpy(option->val, val, len);
option->next = NULL;
return option;
}
void coap_message_destroy(CoAPMessage *message)
{
IOT_FUNC_ENTRY
_coap_msg_op_list_destroy(&message->op_list);
if (message->pay_load != NULL) {
HAL_Free(message->pay_load);
}
memset(message, 0, sizeof(CoAPMessage));
IOT_FUNC_EXIT
}
void coap_msg_dump(CoAPMessage *msg)
{
Log_i("msg->version = %u", msg->version);
Log_i("msg->type = %d", msg->type);
Log_i("msg->code_class = %u", msg->code_class);
Log_i("msg->code_detail = %u", msg->code_detail);
Log_i("msg->msg_id = %d", msg->msg_id);
Log_i("msg->pay_load_len = %d", msg->pay_load_len);
Log_i("msg->pay_load: %s", msg->pay_load);
Log_i("msg->token_len = %u", msg->token_len);
Log_i("msg->token: %s", msg->token);
return;
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,387 @@
/*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright (C) 2018-2020 THL A29 Limited, a Tencent company. All rights reserved.
* Licensed under the MIT License (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "coap_client.h"
#include "qcloud_iot_export.h"
#include "qcloud_iot_import.h"
#include "utils_param_check.h"
#define COAP_MSG_OPLIST_LAST(list) ((list)->last) /**< Get the last option in an option linked-list */
#define COAP_MSG_OPLIST_EMPTY(list) \
((list)->first == NULL) /**< Indicate whether or not an option linked-list is empty */
#define Swap16(A) ((((uint16_t)(A)&0xff00) >> 8) | (((uint16_t)(A)&0x00ff) << 8))
/**
* @brief Check a message for correctness
*
* The following checks from RFC7252 are performed:
*
* An Empty message has the Code field set to 0.00. The Token Length
* field MUST be set to 0 and bytes of data MUST NOT be present after
* the Message ID field. If there are any bytes, they MUST be processed
* as a message format error.
*
* The Reset message MUST echo the Message ID of the Confirmable message
* and MUST be Empty.
*
* A Non-confirmable message always carries either a request or response
* and MUST NOT be Empty.
*
* @param[in] msg Pointer to a message structure
* @returns Operation status
* @retval 0 Success
* @retval <0 Error
*/
static int _coap_msg_check(CoAPMessage *msg)
{
IOT_FUNC_ENTRY
if ((msg->code_class == 0) && (msg->code_detail == 0)) {
/* empty message */
if ((msg->type == COAP_MSG_NON) || (msg->token_len != 0) || (!COAP_MSG_OPLIST_EMPTY(&msg->op_list)) ||
(msg->pay_load_len != 0)) {
Log_e("coap msg op list is not empty");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
}
} else {
/* non-empty message */
if (msg->type == COAP_MSG_RST) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
}
}
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS)
}
/**
* @brief Allocate an option structure and add it to the end of an option linked-list structure
*
* @param[in,out] list Pointer to an option linked-list structure
* @param[in] num Option number
* @param[in] len Option length
* @param[in] val Pointer to a buffer containing the option value
*
* @returns Operation status
* @retval 0 Success
* @retval <0 Error
*/
static int _coap_msg_op_list_add_last(CoAPMsgOptionList *list, unsigned num, unsigned len, const char *val)
{
IOT_FUNC_ENTRY
CoAPMsgOption *option = NULL;
if ((option = qcloud_iot_coap_option_init(num, len, val)) == NULL) {
Log_e("allocate new option failed.");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE)
}
if (list->first == NULL) {
list->first = option;
list->last = option;
} else {
list->last->next = option;
list->last = option;
}
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS)
}
/**
* @brief Parse the header in a message
*
* @param[out] msg Pointer to a message structure
* @param[in] buf Pointer to a buffer containing the message
* @param[in] len Length of the buffer
*
* @returns Number of bytes parsed or error code
* @retval >0 Number of bytes parsed
* @retval <0 Error
*/
static ssize_t _coap_message_deserialize_header(CoAPMessage *msg, char *buf, size_t len)
{
IOT_FUNC_ENTRY
char *p = buf;
if (len < 4) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
}
msg->version = (p[0] >> 6) & 0x03;
if (msg->version != COAP_MSG_VER) {
return -EINVAL;
}
msg->type = (p[0] >> 4) & 0x03;
msg->token_len = p[0] & 0x0f;
if (msg->token_len > sizeof(msg->token)) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
}
msg->code_detail = p[1] & 0x1f;
msg->code_class = (p[1] >> 5) & 0x07;
if ((msg->code_class != COAP_MSG_REQ) && (msg->code_class != COAP_MSG_SUCCESS) &&
(msg->code_class != COAP_MSG_CLIENT_ERR) && (msg->code_class != COAP_MSG_SERVER_ERR)) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_COAP_BADMSG)
}
msg->msg_id = Swap16(*((uint16_t *)(&p[2])));
p += 4;
IOT_FUNC_EXIT_RC(p - buf)
}
/**
* @brief Parse the token in a message
*
* @param[out] msg Pointer to a message structure
* @param[in] buf Pointer to a buffer containing the message
* @param[in] len Length of the buffer
*
* @returns Number of bytes parsed or error code
* @retval >0 Number of bytes parsed
* @retval <0 Error
*/
static ssize_t _coap_message_deserialize_token(CoAPMessage *msg, char *buf, size_t len)
{
IOT_FUNC_ENTRY
if (len < msg->token_len) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
}
memcpy(msg->token, buf, msg->token_len);
IOT_FUNC_EXIT_RC(msg->token_len)
}
/**
* @brief Parse an option in a message
*
* @param[in,out] msg Pointer to a message structure
* @param[in] buf Pointer to a buffer containing the message
* @param[in] len Length of the buffer
*
* @returns Number of bytes parsed or error code
* @retval >0 Number of bytes parsed
* @retval <0 Error
*/
static ssize_t _coap_message_deserialize_option(CoAPMessage *msg, char *buf, size_t len)
{
IOT_FUNC_ENTRY
CoAPMsgOption *prev = NULL;
unsigned op_delta = 0;
unsigned op_len = 0;
unsigned op_num = 0;
char * p = buf;
int ret = 0;
if (len < 1) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
}
op_delta = (p[0] >> 4) & 0x0f;
op_len = p[0] & 0x0f;
if ((op_delta == 15) || (op_len == 15)) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
}
p++;
len--;
if (op_delta == 13) {
if (len < 1) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
}
op_delta += p[0];
p++;
len--;
} else if (op_delta == 14) {
if (len < 2) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
}
op_delta = 269 + Swap16(*((uint16_t *)(&p[0])));
p += 2;
len -= 2;
}
if (op_len == 13) {
if (len < 1) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
}
op_len += p[0];
p++;
len--;
} else if (op_len == 14) {
if (len < 2) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
}
op_len = 269 + Swap16(*((uint16_t *)(&p[0])));
p += 2;
len -= 2;
}
if (len < op_len) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
}
prev = COAP_MSG_OPLIST_LAST(&msg->op_list);
if (prev == NULL) {
op_num = op_delta;
} else {
op_num = COAP_MSG_OPTION_NUM(prev) + op_delta;
}
ret = _coap_msg_op_list_add_last(&msg->op_list, op_num, op_len, p);
if (ret < 0) {
IOT_FUNC_EXIT_RC(ret)
}
p += op_len;
IOT_FUNC_EXIT_RC(p - buf)
}
/**
* @brief Parse the options in a message
*
* @param[in,out] msg Pointer to a message structure
* @param[in] buf Pointer to a buffer containing the message
* @param[in] len Length of the buffer
*
* @returns Number of bytes parsed or error code
* @retval >0 Number of bytes parsed
* @retval <0 Error
*/
static ssize_t _coap_message_deserialize_options(CoAPMessage *msg, char *buf, size_t len)
{
IOT_FUNC_ENTRY
ssize_t num = 0;
char * p = buf;
while (1) {
if (((p[0] & 0xff) == 0xff) || (len == 0)) {
break;
}
num = _coap_message_deserialize_option(msg, p, len);
if (num < 0) {
return num;
}
p += num;
len -= num;
}
IOT_FUNC_EXIT_RC(p - buf)
}
/**
* @brief Parse the payload in a message
*
* @param[out] msg Pointer to a message structure
* @param[in] buf Pointer to a buffer containing the message
* @param[in] len Length of the buffer
*
* @returns Number of bytes parsed or error code
* @retval >0 Number of bytes parsed
* @retval <0 Error
*/
static ssize_t _coap_message_deserialize_payload(CoAPMessage *msg, char *buf, size_t len)
{
IOT_FUNC_ENTRY
char *p = buf;
if (len == 0) {
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS)
}
if ((p[0] & 0xff) != 0xff) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
}
p++;
len--;
if (len == 0) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
}
msg->pay_load = (char *)HAL_Malloc(len);
if (msg->pay_load == NULL) {
return -ENOMEM;
}
memcpy(msg->pay_load, p, len);
msg->pay_load_len = len;
p += len;
IOT_FUNC_EXIT_RC(p - buf)
}
ssize_t deserialize_coap_message(CoAPMessage *message, char *buf, size_t len)
{
IOT_FUNC_ENTRY
ssize_t num = 0;
char * p = buf;
num = _coap_message_deserialize_header(message, p, len);
if (num < 0) {
Log_e("coap_message_deserialize_header failed, num:%lu", num);
coap_message_destroy(message);
goto error;
}
p += num;
len -= num;
num = _coap_message_deserialize_token(message, p, len);
if (num < 0) {
Log_e("coap_message_deserialize_token failed, num:%lu", num);
coap_message_destroy(message);
goto error;
}
p += num;
len -= num;
num = _coap_message_deserialize_options(message, p, len);
if (num < 0) {
Log_e("coap_message_deserialize_options failed, num:%lu", num);
coap_message_destroy(message);
goto error;
}
p += num;
len -= num;
num = _coap_message_deserialize_payload(message, p, len);
if (num < 0) {
Log_e("coap_message_deserialize_payload failed, num:%lu", num);
coap_message_destroy(message);
goto error;
}
IOT_FUNC_EXIT_RC(_coap_msg_check(message))
error:
IOT_FUNC_EXIT_RC(QCLOUD_ERR_COAP_INTERNAL)
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,418 @@
/*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright (C) 2018-2020 THL A29 Limited, a Tencent company. All rights reserved.
* Licensed under the MIT License (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <string.h>
#include "coap_client.h"
#include "coap_client_net.h"
#include "qcloud_iot_export.h"
#include "qcloud_iot_import.h"
#include "utils_param_check.h"
#include "utils_timer.h"
#define PROCESS_ACK_CMD (0)
#define PROCESS_PIGGY_CMD (1)
#define PROCESS_RESP_CMD (2)
#define PROCESS_WAIT_CMD (3)
static void _event_message_type_set(CoAPEventMessage *eventMsg, CoAPMessage *message, uint16_t processCmd)
{
if (message->code_class == COAP_MSG_SUCCESS && message->code_detail == COAP_MSG_CODE_205_CONTENT) {
eventMsg->event_type = processCmd == PROCESS_RESP_CMD ? COAP_EVENT_RECEIVE_RESPCONTENT : COAP_EVENT_RECEIVE_ACK;
} else if (message->code_class == COAP_MSG_CLIENT_ERR && message->code_detail == COAP_MSG_CODE_401_UNAUTHORIZED) {
eventMsg->event_type = COAP_EVENT_UNAUTHORIZED;
Log_e("coap messagefailed, message id: %d, failure reason: %d.%d", message->msg_id, message->code_class,
message->code_detail);
} else if (message->code_class == COAP_MSG_CLIENT_ERR && message->code_detail == COAP_MSG_CODE_403_FORBIDDEN) {
eventMsg->event_type = COAP_EVENT_FORBIDDEN;
Log_e("coap message failed, message id: %d, failure reason: %d.%d", message->msg_id, message->code_class,
message->code_detail);
} else {
eventMsg->event_type = COAP_EVENT_INTERNAL_SERVER_ERROR;
Log_e("coap message failed, message id: %d, failure reason: %d.%d", message->msg_id, message->code_class,
message->code_detail);
}
}
static int _coap_message_list_proc(CoAPClient *client, CoAPMessage *message, uint16_t processCmd)
{
IOT_FUNC_ENTRY;
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
HAL_MutexLock(client->lock_list_wait_ack);
if (client->message_list->len <= 0) {
HAL_MutexUnlock(client->lock_list_wait_ack);
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
ListIterator * iter;
ListNode * node = NULL;
ListNode * temp_node = NULL;
CoAPMsgSendInfo *send_info = NULL;
if ((iter = list_iterator_new(client->message_list, LIST_TAIL)) == NULL) {
HAL_MutexUnlock(client->lock_list_wait_ack);
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
for (;;) {
node = list_iterator_next(iter);
if (NULL != temp_node) {
list_remove(client->message_list, temp_node);
Log_i("remove node");
temp_node = NULL;
}
if (NULL == node) {
break;
}
send_info = (CoAPMsgSendInfo *)node->val;
if (NULL == send_info) {
Log_e("node's value is invalid!");
continue;
}
if (processCmd == PROCESS_ACK_CMD) {
if (send_info->msg_id == message->msg_id) {
send_info->acked = 1; /* ACK is received */
InitTimer(&send_info->start_time);
countdown_ms(&send_info->start_time, client->command_timeout_ms);
}
} else if (processCmd == PROCESS_RESP_CMD) {
if (0 != send_info->token_len && send_info->token_len == message->token_len &&
0 == memcmp(send_info->token, message->token, message->token_len)) {
message->user_context = send_info->user_context;
if (send_info->handler != NULL) {
send_info->handler(message, send_info->user_context);
} else if (NULL != client->event_handle.h_fp) { // event handle process
CoAPEventMessage event_msg = {0};
_event_message_type_set(&event_msg, message, processCmd);
event_msg.message = message;
message->msg_id = send_info->msg_id;
client->event_handle.h_fp(client->event_handle.context, &event_msg);
} else {
Log_e("nether response callback nor event callback is set");
}
Log_d("remove the message id %d from list", send_info->msg_id);
send_info->node_state = COAP_NODE_STATE_INVALID;
}
} else if (processCmd == PROCESS_PIGGY_CMD) {
if (send_info->msg_id == message->msg_id) {
message->user_context = send_info->user_context;
if (send_info->handler != NULL) {
send_info->handler(message, send_info->user_context);
} else if (NULL != client->event_handle.h_fp) { // event handle process
CoAPEventMessage event_msg = {0};
_event_message_type_set(&event_msg, message, processCmd);
event_msg.message = (void *)(uintptr_t)(message->msg_id);
client->event_handle.h_fp(client->event_handle.context, &event_msg);
} else {
Log_e("nether response callback nor event callback is set");
}
Log_d("remove the message id %d from list", send_info->msg_id);
send_info->acked = 1; /* ACK is received */
send_info->node_state = COAP_NODE_STATE_INVALID;
}
} else if (processCmd == PROCESS_WAIT_CMD) {
if (COAP_NODE_STATE_INVALID == send_info->node_state) {
temp_node = node;
continue;
}
if (left_ms(&send_info->start_time) > 0) {
continue;
}
if (send_info->retrans_count < client->max_retry_count && (0 == send_info->acked)) {
InitTimer(&send_info->start_time);
countdown_ms(&send_info->start_time, client->command_timeout_ms);
send_info->retrans_count++;
Log_d("start to retansmit the message id %d len %d", send_info->msg_id, send_info->msglen);
size_t written_len = 0;
int ret = client->network_stack.write(&client->network_stack, send_info->message, send_info->msglen,
left_ms(&send_info->start_time), &written_len);
if (ret != QCLOUD_RET_SUCCESS) {
Log_e("retansmit the message id %d failed.", send_info->msg_id, send_info->msglen);
continue;
}
} else {
send_info->node_state = COAP_NODE_STATE_INVALID;
temp_node = node;
if (send_info->handler != NULL) {
message->type = COAP_MSG_ACK;
message->user_context = send_info->user_context;
message->code_class = COAP_MSG_SDKINTERNAL_ERR;
message->code_detail = COAP_MSG_CODE_600_TIMEOUT;
message->msg_id = send_info->msg_id;
send_info->handler(message, send_info->user_context);
} else if (NULL != client->event_handle.h_fp) {
CoAPEventMessage event_msg = {0};
if (send_info->acked) {
event_msg.event_type = COAP_EVENT_SEPRESP_TIMEOUT;
} else {
event_msg.event_type = COAP_EVENT_ACK_TIMEOUT;
}
event_msg.message = (void *)(uintptr_t)(send_info->msg_id);
client->event_handle.h_fp(client->event_handle.context, &event_msg);
} else {
Log_e("nether response callback nor event callback is set");
}
continue;
}
}
}
list_iterator_destroy(iter);
HAL_MutexUnlock(client->lock_list_wait_ack);
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
/**
* @brief Send ACK to server
*
* @param client reference to CoAP client
* @param msgId msg Id
*
* @returns Operation status
* @retval 0 Success
* @retval <0 Error
*/
static int _coap_client_send_ack(CoAPClient *client, int msgId)
{
IOT_FUNC_ENTRY
CoAPMessage ack = DEFAULT_COAP_MESSAGE;
int ret = 0;
if ((ret = coap_message_type_set(&ack, COAP_MSG_ACK)) != QCLOUD_RET_SUCCESS) {
coap_message_destroy(&ack);
IOT_FUNC_EXIT_RC(ret)
}
if ((ret = coap_message_id_set(&ack, msgId)) != QCLOUD_RET_SUCCESS) {
coap_message_destroy(&ack);
IOT_FUNC_EXIT_RC(ret)
}
ret = coap_message_send(client, &ack);
coap_message_destroy(&ack);
IOT_FUNC_EXIT_RC(ret)
}
static int _coap_piggyresp_message_handle(CoAPClient *client, CoAPMessage *message)
{
IOT_FUNC_ENTRY
int rc = _coap_message_list_proc(client, message, PROCESS_PIGGY_CMD);
IOT_FUNC_EXIT_RC(rc)
}
static int _coap_ack_message_handle(CoAPClient *client, CoAPMessage *message)
{
IOT_FUNC_ENTRY
int rc = _coap_message_list_proc(client, message, PROCESS_ACK_CMD);
IOT_FUNC_EXIT_RC(rc)
}
static int _coap_resp_message_handle(CoAPClient *client, CoAPMessage *message)
{
IOT_FUNC_ENTRY
int rc = 0;
if (COAP_MSG_CON == message->type) {
rc = _coap_client_send_ack(client, message->msg_id);
Log_d("send ack message for id: %d rc: %d", message->msg_id, rc);
}
rc = _coap_message_list_proc(client, message, PROCESS_RESP_CMD);
IOT_FUNC_EXIT_RC(rc)
}
static void _coap_message_handle(CoAPClient *client, unsigned char *buf, unsigned short datalen)
{
IOT_FUNC_ENTRY
int rc = QCLOUD_RET_SUCCESS;
CoAPMessage recv_message;
memset(&recv_message, 0x00, sizeof(CoAPMessage));
if ((rc = deserialize_coap_message(&recv_message, (char *)buf, datalen)) != QCLOUD_RET_SUCCESS) {
Log_e("deserialize message failed: %d", rc);
}
if (recv_message.type == COAP_MSG_ACK && COAP_MSG_IS_EMPTY_ACK(&recv_message)) { // empty ACK
Log_d("receive coap ACK message, id %d", recv_message.msg_id);
_coap_ack_message_handle(client, &recv_message);
} else if (recv_message.type == COAP_MSG_ACK && !COAP_MSG_IS_EMPTY(&recv_message)) { // piggy Response
Log_d("receive coap piggy ACK message, id %d", recv_message.msg_id);
_coap_piggyresp_message_handle(client, &recv_message);
} else if (recv_message.type == COAP_MSG_CON && COAP_MSG_IS_EMPTY_RSP(&recv_message)) { // payload Response
Log_d("receive coap response message, id: %d", recv_message.msg_id);
_coap_resp_message_handle(client, &recv_message);
} else if (recv_message.type == COAP_MSG_NON && COAP_MSG_IS_EMPTY_RSP(&recv_message)) { // payload Response
Log_d("receive coap response message, id: %d", recv_message.msg_id);
_coap_resp_message_handle(client, &recv_message);
} else {
Log_e("not recgonized recv message type");
}
if (recv_message.pay_load != NULL) {
HAL_Free(recv_message.pay_load);
recv_message.pay_load_len = 0;
}
IOT_FUNC_EXIT
}
static int _coap_message_list_add(CoAPClient *client, CoAPMessage *message, int len)
{
IOT_FUNC_ENTRY
CoAPMsgSendInfo *send_info = (CoAPMsgSendInfo *)HAL_Malloc(sizeof(CoAPMsgSendInfo));
if (send_info == NULL) {
Log_e("no memory to malloc SendInfo");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE)
}
send_info->node_state = COAP_NODE_STATE_NORMANL;
send_info->acked = 0;
send_info->user_context = message->user_context;
send_info->msg_id = message->msg_id;
send_info->handler = message->handler;
send_info->msglen = len;
if (COAP_MSG_CON == message->type) {
send_info->retrans_count = 0;
InitTimer(&send_info->start_time);
countdown_ms(&send_info->start_time, client->command_timeout_ms);
} else {
}
send_info->token_len = message->token_len;
memcpy(send_info->token, message->token, message->token_len);
send_info->message = (unsigned char *)HAL_Malloc(len);
if (NULL != send_info->message) {
memcpy(send_info->message, client->send_buf, len);
}
ListNode *node = list_node_new(send_info);
if (NULL == node) {
Log_e("run list_node_new is error!");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE)
}
HAL_MutexLock(client->lock_list_wait_ack);
list_rpush(client->message_list, node);
HAL_MutexUnlock(client->lock_list_wait_ack);
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS)
}
int coap_message_cycle(CoAPClient *client, uint32_t timeout_ms)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
int rc = coap_message_recv(client, timeout_ms);
if (rc != QCLOUD_RET_SUCCESS && rc != QCLOUD_ERR_SSL_READ_TIMEOUT) {
IOT_FUNC_EXIT_RC(rc)
}
CoAPMessage message = DEFAULT_COAP_MESSAGE;
rc = _coap_message_list_proc(client, &message, PROCESS_WAIT_CMD);
IOT_FUNC_EXIT_RC(rc)
}
ssize_t coap_message_send(CoAPClient *client, CoAPMessage *message)
{
IOT_FUNC_ENTRY
ssize_t ret;
size_t written_len = 0;
HAL_MutexLock(client->lock_send_buf);
if ((ret = serialize_coap_message(message, (char *)client->send_buf, client->send_buf_size)) < 0) {
Log_e("failed to serialize coap message");
HAL_MutexUnlock(client->lock_send_buf);
IOT_FUNC_EXIT_RC(ret)
}
ret = client->network_stack.write(&client->network_stack, client->send_buf, ret, 0, &written_len);
if (ret == QCLOUD_RET_SUCCESS) {
if (message->type == COAP_MSG_CON && message->code_class == COAP_MSG_REQ) {
ret = _coap_message_list_add(client, message, written_len);
Log_i("add coap message id: %d into wait list ret: %d", message->msg_id, ret);
} else {
Log_i("The message doesn't need to be retransmitted");
}
} else {
Log_e("coap net fail to write rc: %d", ret);
}
HAL_MutexUnlock(client->lock_send_buf);
IOT_FUNC_EXIT_RC(ret)
}
ssize_t coap_message_recv(CoAPClient *client, uint32_t timeout_ms)
{
IOT_FUNC_ENTRY
size_t read_lean = 0;
int rc = 0;
rc = client->network_stack.read(&client->network_stack, client->recv_buf, client->read_buf_size, timeout_ms,
&read_lean);
switch (rc) {
case QCLOUD_RET_SUCCESS:
_coap_message_handle(client, client->recv_buf, read_lean);
break;
case QCLOUD_ERR_SSL_READ_TIMEOUT:
break;
default:
break;
}
IOT_FUNC_EXIT_RC(rc)
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,41 @@
/*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright (C) 2018-2020 THL A29 Limited, a Tencent company. All rights reserved.
* Licensed under the MIT License (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "coap_client_net.h"
#include "network_interface.h"
int qcloud_iot_coap_network_init(Network *pNetwork)
{
int rc;
/* first choice: TLS */
pNetwork->type = NETWORK_DTLS;
#ifdef AUTH_WITH_NOTLS
pNetwork->type = NETWORK_UDP;
#endif
rc = network_init(pNetwork);
return rc;
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,292 @@
/*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright (C) 2018-2020 THL A29 Limited, a Tencent company. All rights reserved.
* Licensed under the MIT License (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "coap_client.h"
#include "qcloud_iot_export.h"
#include "qcloud_iot_import.h"
#include "utils_param_check.h"
#define COAP_MSG_OPLIST_FIRST(list) ((list)->first) /**< Get the first option from an option linked-list */
/**
* @brief Format the header in a message
*
* @param[in] message Pointer to a message structure
* @param[out] buf Pointer to a buffer to contain the formatted message
* @param[in] len Length of the buffer
*
* @returns Length of the formatted message or error code
* @retval >0 Length of the formatted message
* @retval <0 Error
*/
static ssize_t _coap_message_serialize_header(CoAPMessage *message, char *buf, size_t len)
{
IOT_FUNC_ENTRY
if (len < 4) {
return -ENOSPC;
}
buf[0] = (char)((COAP_MSG_VER << 6) | ((message->type & 0x03) << 4) | (message->token_len & 0x0f));
buf[1] = (char)(((message->code_class & 0x07) << 5) | (message->code_detail & 0x1f));
buf[2] = (message->msg_id & 0xFF00) >> 8;
buf[3] = (message->msg_id & 0x00FF);
IOT_FUNC_EXIT_RC(4)
}
/**
* @brief Format the token in a message
*
* @param[in] message Pointer to a message structure
* @param[out] buf Pointer to a buffer to contain the formatted message
* @param[in] len Length of the buffer
*
* @returns Length of the formatted message or error code
* @retval >0 Length of the formatted message
* @retval <0 Error
*/
static ssize_t _coap_message_serialize_token(CoAPMessage *message, char *buf, size_t len)
{
IOT_FUNC_ENTRY
if (len < message->token_len) {
return -ENOSPC;
}
memcpy(buf, message->token, message->token_len);
IOT_FUNC_EXIT_RC(message->token_len)
}
/**
* @brief Format an option in a message
*
* @param[in] op Pointer to an option structure
* @param[in] prev_num option number of the previous option
* @param[out] buf Pointer to a buffer to contain the formatted message
* @param[in] len Length of the buffer
*
* @returns Length of the formatted message or error code
* @retval >0 Length of the formatted message
* @retval <0 Error
*/
static ssize_t _coap_msg_format_op(CoAPMsgOption *op, unsigned prev_num, char *buf, size_t len)
{
IOT_FUNC_ENTRY
unsigned short op_delta = 0;
unsigned num = 0;
char * p = buf;
op_delta = op->option_num - prev_num;
num++;
/* option delta */
if (op_delta >= 269) {
num += 2;
} else if (op_delta >= 13) {
num += 1;
}
/* option length */
if (op->val_len >= 269) {
num += 2;
} else if (op->option_num >= 13) {
num += 1;
}
/* option value */
num += op->val_len;
if (num > len) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_COAP_DATA_SIZE)
}
/* option delta */
if (op_delta >= 269) {
p[0] = 14 << 4;
} else if (op_delta >= 13) {
p[0] = 13 << 4;
} else {
p[0] = op_delta << 4;
}
/* option length */
if (op->val_len >= 269) {
p[0] |= 14;
} else if (op->val_len >= 13) {
p[0] |= 13;
} else {
p[0] |= op->val_len;
}
p++;
len--;
/* option delta extended */
if (op_delta >= 269) {
*p = (unsigned char)(((op_delta - 269) & 0xFF00) >> 8);
*(p + 1) = (unsigned char)(((op_delta - 269) & 0x00FF));
p += 2;
len -= 2;
} else if (op_delta >= 13) {
p[0] = op_delta - 13;
p++;
len--;
}
/* option length extended */
if (op->val_len >= 269) {
*p = (unsigned char)(((op->val_len - 269) & 0xFF00) >> 8);
*(p + 1) = (unsigned char)(((op->val_len - 269) & 0x00FF));
p += 2;
len -= 2;
} else if (op->val_len >= 13) {
p[0] = op->val_len - 13;
p++;
len--;
}
/* option value */
memcpy(p, op->val, op->val_len);
p += op->val_len;
IOT_FUNC_EXIT_RC(p - buf)
}
/**
* @brief Format the options in a message
*
* @param[in] message Pointer to a message structure
* @param[out] buf Pointer to a buffer to contain the formatted message
* @param[in] len Length of the buffer
*
* @returns Length of the formatted message or error code
* @retval >0 Length of the formatted message
* @retval <0 Error
*/
static ssize_t _coap_message_serialize_options(CoAPMessage *message, char *buf, size_t len)
{
IOT_FUNC_ENTRY;
CoAPMsgOption *op = NULL;
unsigned prev_num = 0;
ssize_t num = 0;
char * p = buf;
op = COAP_MSG_OPLIST_FIRST(&message->op_list);
while (op != NULL) {
num = _coap_msg_format_op(op, prev_num, p, len);
if (num < 0) {
return num;
}
p += num;
len -= num;
prev_num = COAP_MSG_OPTION_NUM(op);
op = COAP_MSG_OP_NEXT(op);
}
IOT_FUNC_EXIT_RC(p - buf)
}
/**
* @brief Format the payload in a message
*
* @param[in] message Pointer to a message structure
* @param[out] buf Pointer to a buffer to contain the formatted message
* @param[in] len Length of the buffer
*
* @returns Length of the formatted message or error code
* @retval >0 Length of the formatted message
* @retval <0 Error
*/
static ssize_t _coap_message_serialize_payload(CoAPMessage *message, char *buf, size_t len)
{
IOT_FUNC_ENTRY;
if (message->pay_load_len == 0) {
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS)
}
if (message->pay_load_len + 1 > len) {
return -ENOSPC;
}
buf[0] = 0xff;
memcpy(&buf[1], message->pay_load, message->pay_load_len);
IOT_FUNC_EXIT_RC(message->pay_load_len + 1)
}
ssize_t serialize_coap_message(CoAPMessage *message, char *buf, size_t len)
{
IOT_FUNC_ENTRY;
POINTER_SANITY_CHECK(message, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
ssize_t num = 0;
char * p = buf;
num = _coap_message_serialize_header(message, p, len);
if (num < 0) {
Log_e("coap_message_serialize_header fail num=%lu", num);
goto error;
}
p += num;
len -= num;
num = _coap_message_serialize_token(message, p, len);
if (num < 0) {
Log_e("coap_message_serialize_token fail num=%lu", num);
goto error;
}
p += num;
len -= num;
num = _coap_message_serialize_options(message, p, len);
if (num < 0) {
Log_e("coap_message_serialize_options fail num=%lu", num);
goto error;
}
p += num;
len -= num;
num = _coap_message_serialize_payload(message, p, len);
if (num < 0) {
Log_e("coap_message_serialize_payload fail num=%lu", num);
goto error;
}
p += num;
IOT_FUNC_EXIT_RC(p - buf)
error:
IOT_FUNC_EXIT_RC(QCLOUD_ERR_COAP_INTERNAL)
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,796 @@
/*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright (C) 2018-2020 THL A29 Limited, a Tencent company. All rights reserved.
* Licensed under the MIT License (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "utils_httpc.h"
#include <ctype.h>
#include <string.h>
#include "qcloud_iot_ca.h"
#include "qcloud_iot_common.h"
#include "qcloud_iot_export.h"
#include "qcloud_iot_import.h"
#include "utils_timer.h"
#define HTTP_CLIENT_MIN(x, y) (((x) < (y)) ? (x) : (y))
#define HTTP_CLIENT_MAX(x, y) (((x) > (y)) ? (x) : (y))
#define HTTP_CLIENT_AUTHB_SIZE 128
#define HTTP_CLIENT_CHUNK_SIZE 1024
#define HTTP_CLIENT_SEND_BUF_SIZE 1024
#define HTTP_CLIENT_MAX_HOST_LEN 64
#define HTTP_CLIENT_MAX_URL_LEN 1024
#define HTTP_RETRIEVE_MORE_DATA (1)
#if defined(MBEDTLS_DEBUG_C)
#define DEBUG_LEVEL 2
#endif
static void _http_client_base64enc(char *out, const char *in)
{
const char code[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
int i = 0, x = 0, l = 0;
for (; *in; in++) {
x = x << 8 | *in;
for (l += 8; l >= 6; l -= 6) {
out[i++] = code[(x >> (l - 6)) & 0x3f];
}
}
if (l > 0) {
x <<= 6 - l;
out[i++] = code[x & 0x3f];
}
for (; i % 4;) {
out[i++] = '=';
}
out[i] = '\0';
}
static int _http_client_parse_url(const char *url, char *scheme, uint32_t max_scheme_len, char *host,
uint32_t maxhost_len, int *port, char *path, uint32_t max_path_len)
{
char * scheme_ptr = (char *)url;
char * host_ptr = (char *)strstr(url, "://");
uint32_t host_len = 0;
uint32_t path_len;
char *path_ptr;
char *fragment_ptr;
if (host_ptr == NULL) {
Log_e("Could not find host");
return QCLOUD_ERR_HTTP_PARSE;
}
if (max_scheme_len < host_ptr - scheme_ptr + 1) {
Log_e("Scheme str is too small (%u >= %u)", max_scheme_len, (uint32_t)(host_ptr - scheme_ptr + 1));
return QCLOUD_ERR_HTTP_PARSE;
}
memcpy(scheme, scheme_ptr, host_ptr - scheme_ptr);
scheme[host_ptr - scheme_ptr] = '\0';
host_ptr += 3;
*port = 0;
path_ptr = strchr(host_ptr, '/');
if (NULL == path_ptr) {
path_ptr = scheme_ptr + (int)strlen(url);
host_len = path_ptr - host_ptr;
memcpy(host, host_ptr, host_len);
host[host_len] = '\0';
memcpy(path, "/", 1);
path[1] = '\0';
return QCLOUD_RET_SUCCESS;
}
if (host_len == 0) {
host_len = path_ptr - host_ptr;
}
if (maxhost_len < host_len + 1) {
Log_e("Host str is too long (host_len(%d) >= max_len(%d))", host_len + 1, maxhost_len);
return QCLOUD_ERR_HTTP_PARSE;
}
memcpy(host, host_ptr, host_len);
host[host_len] = '\0';
fragment_ptr = strchr(host_ptr, '#');
if (fragment_ptr != NULL) {
path_len = fragment_ptr - path_ptr;
} else {
path_len = strlen(path_ptr);
}
if (max_path_len < path_len + 1) {
Log_e("Path str is too small (%d >= %d)", max_path_len, path_len + 1);
return QCLOUD_ERR_HTTP_PARSE;
}
memcpy(path, path_ptr, path_len);
path[path_len] = '\0';
return QCLOUD_RET_SUCCESS;
}
static int _http_client_parse_host(const char *url, char *host, uint32_t host_max_len)
{
const char *host_ptr = (const char *)strstr(url, "://");
uint32_t host_len = 0;
char * path_ptr;
if (host_ptr == NULL) {
Log_e("Could not find host");
return QCLOUD_ERR_HTTP_PARSE;
}
host_ptr += 3;
uint32_t pro_len = 0;
pro_len = host_ptr - url;
path_ptr = strchr(host_ptr, '/');
if (path_ptr != NULL)
host_len = path_ptr - host_ptr;
else
host_len = strlen(url) - pro_len;
if (host_max_len < host_len + 1) {
Log_e("Host str is too small (%d >= %d)", host_max_len, host_len + 1);
return QCLOUD_ERR_HTTP_PARSE;
}
memcpy(host, host_ptr, host_len);
host[host_len] = '\0';
return QCLOUD_RET_SUCCESS;
}
static int _http_client_get_info(HTTPClient *client, unsigned char *send_buf, int *send_idx, char *buf, uint32_t len)
{
int rc = QCLOUD_RET_SUCCESS;
int cp_len;
int idx = *send_idx;
if (len == 0) {
len = strlen(buf);
}
do {
if ((HTTP_CLIENT_SEND_BUF_SIZE - idx) >= len) {
cp_len = len;
} else {
cp_len = HTTP_CLIENT_SEND_BUF_SIZE - idx;
}
memcpy(send_buf + idx, buf, cp_len);
idx += cp_len;
len -= cp_len;
if (idx == HTTP_CLIENT_SEND_BUF_SIZE) {
size_t byte_written_len = 0;
rc = client->network_stack.write(&(client->network_stack), send_buf, HTTP_CLIENT_SEND_BUF_SIZE, 5000,
&byte_written_len);
if (byte_written_len) {
return (byte_written_len);
}
}
} while (len);
*send_idx = idx;
return rc;
}
static int _http_client_send_auth(HTTPClient *client, unsigned char *send_buf, int *send_idx)
{
char b_auth[(int)((HTTP_CLIENT_AUTHB_SIZE + 3) * 4 / 3 + 1)];
char base64buff[HTTP_CLIENT_AUTHB_SIZE + 3];
_http_client_get_info(client, send_buf, send_idx, "Authorization: Basic ", 0);
HAL_Snprintf(base64buff, sizeof(base64buff), "%s:%s", client->auth_user, client->auth_password);
_http_client_base64enc(b_auth, base64buff);
b_auth[strlen(b_auth) + 1] = '\0';
b_auth[strlen(b_auth)] = '\n';
_http_client_get_info(client, send_buf, send_idx, b_auth, 0);
return QCLOUD_RET_SUCCESS;
}
static int _http_client_send_header(HTTPClient *client, const char *url, HttpMethod method, HTTPClientData *client_data)
{
char scheme[8] = {0};
char host[HTTP_CLIENT_MAX_HOST_LEN] = {0};
char path[HTTP_CLIENT_MAX_URL_LEN] = {0};
int len;
unsigned char send_buf[HTTP_CLIENT_SEND_BUF_SIZE] = {0};
char buf[HTTP_CLIENT_SEND_BUF_SIZE] = {0};
char * meth = (method == HTTP_GET)
? "GET"
: (method == HTTP_POST)
? "POST"
: (method == HTTP_PUT)
? "PUT"
: (method == HTTP_DELETE) ? "DELETE" : (method == HTTP_HEAD) ? "HEAD" : "";
int rc;
int port;
int res = _http_client_parse_url(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path));
if (res != QCLOUD_RET_SUCCESS) {
Log_e("httpclient_parse_url returned %d", res);
return res;
}
if (strcmp(scheme, "http") == 0) {
} else if (strcmp(scheme, "https") == 0) {
}
memset(send_buf, 0, HTTP_CLIENT_SEND_BUF_SIZE);
len = 0;
HAL_Snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\nHost: %s\r\n", meth, path, host);
rc = _http_client_get_info(client, send_buf, &len, buf, strlen(buf));
if (rc) {
Log_e("Could not write request");
return QCLOUD_ERR_HTTP_CONN;
}
if (client->auth_user) {
_http_client_send_auth(client, send_buf, &len);
}
if (client->header) {
_http_client_get_info(client, send_buf, &len, (char *)client->header, strlen(client->header));
}
if (client_data->post_buf != NULL) {
HAL_Snprintf(buf, sizeof(buf), "Content-Length: %d\r\n", client_data->post_buf_len);
_http_client_get_info(client, send_buf, &len, buf, strlen(buf));
if (client_data->post_content_type != NULL) {
HAL_Snprintf(buf, sizeof(buf), "Content-Type: %s\r\n", client_data->post_content_type);
_http_client_get_info(client, send_buf, &len, buf, strlen(buf));
}
}
_http_client_get_info(client, send_buf, &len, "\r\n", 0);
// Log_d("REQUEST:\n%s", send_buf);
size_t written_len = 0;
rc = client->network_stack.write(&client->network_stack, send_buf, len, 5000, &written_len);
if (written_len > 0) {
// Log_d("Written %lu bytes", written_len);
} else if (written_len == 0) {
Log_e("written_len == 0,Connection was closed by server");
return QCLOUD_ERR_HTTP_CLOSED; /* Connection was closed by server */
} else {
Log_e("Connection error (send returned %d)", rc);
return QCLOUD_ERR_HTTP_CONN;
}
return QCLOUD_RET_SUCCESS;
}
static int _http_client_send_userdata(HTTPClient *client, HTTPClientData *client_data)
{
if (client_data->post_buf && client_data->post_buf_len) {
// Log_d("client_data->post_buf: %s", client_data->post_buf);
{
size_t written_len = 0;
int rc = client->network_stack.write(&client->network_stack, (unsigned char *)client_data->post_buf,
client_data->post_buf_len, 5000, &written_len);
if (written_len > 0) {
// Log_d("Written %d bytes", written_len);
} else if (written_len == 0) {
Log_e("written_len == 0,Connection was closed by server");
return QCLOUD_ERR_HTTP_CLOSED;
} else {
Log_e("Connection error (send returned %d)", rc);
return QCLOUD_ERR_HTTP_CONN;
}
}
}
return QCLOUD_RET_SUCCESS;
}
static int _http_client_recv(HTTPClient *client, char *buf, int min_len, int max_len, int *p_read_len,
uint32_t timeout_ms, HTTPClientData *client_data)
{
IOT_FUNC_ENTRY;
int rc = 0;
Timer timer;
size_t recv_size = 0;
InitTimer(&timer);
countdown_ms(&timer, (unsigned int)timeout_ms);
*p_read_len = 0;
rc = client->network_stack.read(&client->network_stack, (unsigned char *)buf, max_len, (uint32_t)left_ms(&timer),
&recv_size);
*p_read_len = (int)recv_size;
if (rc == QCLOUD_ERR_SSL_NOTHING_TO_READ || rc == QCLOUD_ERR_TCP_NOTHING_TO_READ) {
Log_d("HTTP read nothing and timeout");
rc = QCLOUD_RET_SUCCESS;
} else if (rc == QCLOUD_ERR_SSL_READ_TIMEOUT || rc == QCLOUD_ERR_TCP_READ_TIMEOUT) {
if (*p_read_len == client_data->retrieve_len || client_data->retrieve_len == 0)
rc = QCLOUD_RET_SUCCESS;
else
Log_e("network_stack read timeout");
} else if (rc == QCLOUD_ERR_TCP_PEER_SHUTDOWN && *p_read_len > 0) {
/* HTTP server give response and close this connection */
client->network_stack.disconnect(&client->network_stack);
rc = QCLOUD_RET_SUCCESS;
} else if (rc != QCLOUD_RET_SUCCESS) {
Log_e("Connection error rc = %d (recv returned %d)", rc, *p_read_len);
IOT_FUNC_EXIT_RC(rc);
}
// IOT_FUNC_EXIT_RC(rc);
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
static int _http_client_retrieve_content(HTTPClient *client, char *data, int len, uint32_t timeout_ms,
HTTPClientData *client_data)
{
IOT_FUNC_ENTRY;
int count = 0;
int templen = 0;
int crlf_pos;
Timer timer;
InitTimer(&timer);
countdown_ms(&timer, (unsigned int)timeout_ms);
client_data->is_more = IOT_TRUE;
if (client_data->response_content_len == -1 && client_data->is_chunked == IOT_FALSE) {
while (1) {
int rc, max_len;
if (count + len < client_data->response_buf_len - 1) {
memcpy(client_data->response_buf + count, data, len);
count += len;
client_data->response_buf[count] = '\0';
} else {
memcpy(client_data->response_buf + count, data, client_data->response_buf_len - 1 - count);
client_data->response_buf[client_data->response_buf_len - 1] = '\0';
return HTTP_RETRIEVE_MORE_DATA;
}
max_len = HTTP_CLIENT_MIN(HTTP_CLIENT_CHUNK_SIZE - 1, client_data->response_buf_len - 1 - count);
rc = _http_client_recv(client, data, 1, max_len, &len, (uint32_t)left_ms(&timer), client_data);
/* Receive data */
// Log_d("data len: %d %d", len, count);
if (rc != QCLOUD_RET_SUCCESS) {
IOT_FUNC_EXIT_RC(rc);
}
if (0 == left_ms(&timer)) {
Log_e("HTTP read timeout!");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_HTTP_TIMEOUT);
}
if (len == 0) {
/* read no more data */
Log_d("no more data, len == 0");
client_data->is_more = IOT_FALSE;
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
}
}
while (1) {
uint32_t readLen = 0;
if (client_data->is_chunked && client_data->retrieve_len <= 0) {
/* Read chunk header */
bool foundCrlf;
int n;
do {
foundCrlf = IOT_FALSE;
crlf_pos = 0;
data[len] = 0;
if (len >= 2) {
for (; crlf_pos < len - 2; crlf_pos++) {
if (data[crlf_pos] == '\r' && data[crlf_pos + 1] == '\n') {
foundCrlf = IOT_TRUE;
break;
}
}
}
if (!foundCrlf) {
/* Try to read more */
if (len < HTTP_CLIENT_CHUNK_SIZE) {
int new_trf_len, rc;
rc = _http_client_recv(client, data + len, 0, HTTP_CLIENT_CHUNK_SIZE - len - 1, &new_trf_len,
left_ms(&timer), client_data);
len += new_trf_len;
if (rc != QCLOUD_RET_SUCCESS) {
IOT_FUNC_EXIT_RC(rc);
} else {
continue;
}
} else {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_HTTP);
}
}
} while (!foundCrlf);
data[crlf_pos] = '\0';
// n = sscanf(data, "%x", &readLen);/* chunk length */
readLen = strtoul(data, NULL, 16);
n = (0 == readLen) ? 0 : 1;
client_data->retrieve_len = readLen;
client_data->response_content_len += client_data->retrieve_len;
if (readLen == 0) {
client_data->is_more = IOT_FALSE;
Log_d("no more (last chunk)");
}
if (n != 1) {
Log_e("Could not read chunk length");
return QCLOUD_ERR_HTTP_UNRESOLVED_DNS;
}
memmove(data, &data[crlf_pos + 2], len - (crlf_pos + 2));
len -= (crlf_pos + 2);
} else {
readLen = client_data->retrieve_len;
}
do {
templen = HTTP_CLIENT_MIN(len, readLen);
if (count + templen < client_data->response_buf_len - 1) {
memcpy(client_data->response_buf + count, data, templen);
count += templen;
client_data->response_buf[count] = '\0';
client_data->retrieve_len -= templen;
} else {
memcpy(client_data->response_buf + count, data, client_data->response_buf_len - 1 - count);
client_data->response_buf[client_data->response_buf_len - 1] = '\0';
client_data->retrieve_len -= (client_data->response_buf_len - 1 - count);
IOT_FUNC_EXIT_RC(HTTP_RETRIEVE_MORE_DATA);
}
if (len > readLen) {
Log_d("memmove %d %d %d\n", readLen, len, client_data->retrieve_len);
memmove(data, &data[readLen], len - readLen); /* chunk case, read between two chunks */
len -= readLen;
readLen = 0;
client_data->retrieve_len = 0;
} else {
readLen -= len;
}
if (readLen) {
int rc;
int max_len = HTTP_CLIENT_MIN(HTTP_CLIENT_CHUNK_SIZE - 1, client_data->response_buf_len - 1 - count);
max_len = HTTP_CLIENT_MIN(max_len, readLen);
rc = _http_client_recv(client, data, 1, max_len, &len, left_ms(&timer), client_data);
if (rc != QCLOUD_RET_SUCCESS) {
IOT_FUNC_EXIT_RC(rc);
}
if (left_ms(&timer) == 0) {
Log_e("HTTP read timeout!");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_HTTP_TIMEOUT);
}
}
} while (readLen);
if (client_data->is_chunked) {
if (len < 2) {
int new_trf_len, rc;
/* Read missing chars to find end of chunk */
rc = _http_client_recv(client, data + len, 2 - len, HTTP_CLIENT_CHUNK_SIZE - len - 1, &new_trf_len,
left_ms(&timer), client_data);
if ((rc != QCLOUD_RET_SUCCESS) || (0 == left_ms(&timer))) {
IOT_FUNC_EXIT_RC(rc);
}
len += new_trf_len;
}
if ((data[0] != '\r') || (data[1] != '\n')) {
Log_e("Format error, %s", data); /* after memmove, the beginning of next chunk */
IOT_FUNC_EXIT_RC(QCLOUD_ERR_HTTP_UNRESOLVED_DNS);
}
memmove(data, &data[2], len - 2); /* remove the \r\n */
len -= 2;
} else {
// Log_d("no more (content-length)");
client_data->is_more = IOT_FALSE;
break;
}
}
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
static int _http_client_response_parse(HTTPClient *client, char *data, int len, uint32_t timeout_ms,
HTTPClientData *client_data)
{
IOT_FUNC_ENTRY;
int crlf_pos;
Timer timer;
char *tmp_ptr, *ptr_body_end;
InitTimer(&timer);
countdown_ms(&timer, timeout_ms);
client_data->response_content_len = -1;
char *crlf_ptr = strstr(data, "\r\n");
if (crlf_ptr == NULL) {
Log_e("\\r\\n not found");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_HTTP_UNRESOLVED_DNS);
}
crlf_pos = crlf_ptr - data;
data[crlf_pos] = '\0';
#if 0
if (sscanf(data, "HTTP/%*d.%*d %d %*[^\r\n]", &(client->response_code)) != 1) {
Log_e("Not a correct HTTP answer : %s\n", data);
return QCLOUD_ERR_HTTP_UNRESOLVED_DNS;
}
#endif
client->response_code = atoi(data + 9);
if ((client->response_code < 200) || (client->response_code >= 400)) {
Log_w("Response code %d", client->response_code);
if (client->response_code == 403)
IOT_FUNC_EXIT_RC(QCLOUD_ERR_HTTP_AUTH);
if (client->response_code == 404)
IOT_FUNC_EXIT_RC(QCLOUD_ERR_HTTP_NOT_FOUND);
}
// Log_d("Reading headers : %s", data);
// remove null character
memmove(data, &data[crlf_pos + 2], len - (crlf_pos + 2) + 1);
len -= (crlf_pos + 2);
client_data->is_chunked = IOT_FALSE;
if (NULL == (ptr_body_end = strstr(data, "\r\n\r\n"))) {
int new_trf_len, rc;
rc = _http_client_recv(client, data + len, 1, HTTP_CLIENT_CHUNK_SIZE - len - 1, &new_trf_len, left_ms(&timer),
client_data);
if (rc != QCLOUD_RET_SUCCESS) {
IOT_FUNC_EXIT_RC(rc);
}
len += new_trf_len;
data[len] = '\0';
if (NULL == (ptr_body_end = strstr(data, "\r\n\r\n"))) {
Log_e("parse error: no end of the request body");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
}
if (NULL != (tmp_ptr = strstr(data, "Content-Length"))) {
client_data->response_content_len = atoi(tmp_ptr + strlen("Content-Length: "));
client_data->retrieve_len = client_data->response_content_len;
} else if (NULL != (tmp_ptr = strstr(data, "Transfer-Encoding"))) {
int len_chunk = strlen("Chunked");
char *chunk_value = data + strlen("Transfer-Encoding: ");
if ((!memcmp(chunk_value, "Chunked", len_chunk)) || (!memcmp(chunk_value, "chunked", len_chunk))) {
client_data->is_chunked = IOT_TRUE;
client_data->response_content_len = 0;
client_data->retrieve_len = 0;
}
} else {
Log_e("Could not parse header");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_HTTP);
}
len = len - (ptr_body_end + 4 - data);
memmove(data, ptr_body_end + 4, len + 1);
int rc = _http_client_retrieve_content(client, data, len, left_ms(&timer), client_data);
IOT_FUNC_EXIT_RC(rc);
}
static int _http_client_connect(HTTPClient *client)
{
if (QCLOUD_RET_SUCCESS != client->network_stack.connect(&client->network_stack)) {
return QCLOUD_ERR_HTTP_CONN;
}
return QCLOUD_RET_SUCCESS;
}
static int _http_client_send_request(HTTPClient *client, const char *url, HttpMethod method,
HTTPClientData *client_data)
{
int rc;
rc = _http_client_send_header(client, url, method, client_data);
if (rc != 0) {
Log_e("httpclient_send_header is error, rc = %d", rc);
return rc;
}
if (method == HTTP_POST || method == HTTP_PUT) {
rc = _http_client_send_userdata(client, client_data);
}
return rc;
}
static int _http_client_recv_response(HTTPClient *client, uint32_t timeout_ms, HTTPClientData *client_data)
{
IOT_FUNC_ENTRY;
int reclen = 0, rc = QCLOUD_ERR_HTTP_CONN;
char buf[HTTP_CLIENT_CHUNK_SIZE] = {0};
Timer timer;
InitTimer(&timer);
countdown_ms(&timer, timeout_ms);
if (0 == client->network_stack.handle) {
Log_e("Connection has not been established");
IOT_FUNC_EXIT_RC(rc);
}
if (client_data->is_more) {
client_data->response_buf[0] = '\0';
rc = _http_client_retrieve_content(client, buf, reclen, left_ms(&timer), client_data);
} else {
client_data->is_more = IOT_TRUE;
rc = _http_client_recv(client, buf, 1, HTTP_CLIENT_CHUNK_SIZE - 1, &reclen, left_ms(&timer), client_data);
if (rc != QCLOUD_RET_SUCCESS) {
IOT_FUNC_EXIT_RC(rc);
}
// else if(0 == left_ms(&timer)){
// IOT_FUNC_EXIT_RC(QCLOUD_ERR_HTTP_TIMEOUT);
//}
buf[reclen] = '\0';
if (reclen) {
// HAL_Printf("RESPONSE:\n%s", buf);
rc = _http_client_response_parse(client, buf, reclen, left_ms(&timer), client_data);
}
}
IOT_FUNC_EXIT_RC(rc);
}
static int _http_network_init(Network *pNetwork, const char *host, int port, const char *ca_crt_dir)
{
int rc = QCLOUD_RET_SUCCESS;
if (pNetwork == NULL) {
return QCLOUD_ERR_INVAL;
}
pNetwork->type = NETWORK_TCP;
#ifndef AUTH_WITH_NOTLS
if (ca_crt_dir != NULL) {
pNetwork->ssl_connect_params.ca_crt = ca_crt_dir;
pNetwork->ssl_connect_params.ca_crt_len = strlen(pNetwork->ssl_connect_params.ca_crt);
pNetwork->ssl_connect_params.timeout_ms = 10000;
pNetwork->type = NETWORK_TLS;
}
#endif
pNetwork->host = host;
pNetwork->port = port;
rc = network_init(pNetwork);
return rc;
}
int qcloud_http_client_connect(HTTPClient *client, const char *url, int port, const char *ca_crt)
{
if (client->network_stack.handle != 0) {
Log_e("http client has connected to host!");
return QCLOUD_ERR_HTTP_CONN;
}
int rc;
char host[HTTP_CLIENT_MAX_HOST_LEN] = {0};
rc = _http_client_parse_host(url, host, sizeof(host));
if (rc != QCLOUD_RET_SUCCESS)
return rc;
rc = _http_network_init(&client->network_stack, host, port, ca_crt);
if (rc != QCLOUD_RET_SUCCESS)
return rc;
rc = _http_client_connect(client);
if (rc != QCLOUD_RET_SUCCESS) {
Log_e("http_client_connect is error,rc = %d", rc);
qcloud_http_client_close(client);
} else {
/* reduce log print due to frequent log server connect/disconnect */
if (0 == strcmp(url, LOG_UPLOAD_SERVER_URL))
UPLOAD_DBG("http client connect success");
else
Log_d("http client connect success");
}
return rc;
}
void qcloud_http_client_close(HTTPClient *client)
{
if (client->network_stack.handle != 0) {
client->network_stack.disconnect(&client->network_stack);
}
}
int qcloud_http_client_common(HTTPClient *client, const char *url, int port, const char *ca_crt, HttpMethod method,
HTTPClientData *client_data)
{
int rc;
if (client->network_stack.handle == 0) {
rc = qcloud_http_client_connect(client, url, port, ca_crt);
if (rc != QCLOUD_RET_SUCCESS)
return rc;
}
rc = _http_client_send_request(client, url, method, client_data);
if (rc != QCLOUD_RET_SUCCESS) {
Log_e("http_client_send_request is error,rc = %d", rc);
qcloud_http_client_close(client);
return rc;
}
return QCLOUD_RET_SUCCESS;
}
int qcloud_http_recv_data(HTTPClient *client, uint32_t timeout_ms, HTTPClientData *client_data)
{
IOT_FUNC_ENTRY;
int rc = QCLOUD_RET_SUCCESS;
Timer timer;
InitTimer(&timer);
countdown_ms(&timer, (unsigned int)timeout_ms);
if ((NULL != client_data->response_buf) && (0 != client_data->response_buf_len)) {
rc = _http_client_recv_response(client, left_ms(&timer), client_data);
if (rc < 0) {
Log_e("http_client_recv_response is error,rc = %d", rc);
qcloud_http_client_close(client);
IOT_FUNC_EXIT_RC(rc);
}
}
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,583 @@
/*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright (C) 2018-2020 THL A29 Limited, a Tencent company. All rights reserved.
* Licensed under the MIT License (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <ctype.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef __cplusplus
extern "C" {
#endif
#include "lite-utils.h"
#include "log_upload.h"
#include "mqtt_client.h"
#include "qcloud_iot_ca.h"
#include "qcloud_iot_common.h"
#include "qcloud_iot_device.h"
#include "qcloud_iot_export.h"
#include "qcloud_iot_import.h"
#include "utils_base64.h"
#include "utils_list.h"
static uint16_t _get_random_start_packet_id(void)
{
srand((unsigned)HAL_GetTimeMs());
return rand() % 65536 + 1;
}
DeviceInfo *IOT_MQTT_GetDeviceInfo(void *pClient)
{
POINTER_SANITY_CHECK(pClient, NULL);
Qcloud_IoT_Client *mqtt_client = (Qcloud_IoT_Client *)pClient;
return &mqtt_client->device_info;
}
// currently return a constant value
int IOT_MQTT_GetErrCode(void)
{
return QCLOUD_ERR_FAILURE;
}
void *IOT_MQTT_Construct(MQTTInitParams *pParams)
{
POINTER_SANITY_CHECK(pParams, NULL);
STRING_PTR_SANITY_CHECK(pParams->product_id, NULL);
STRING_PTR_SANITY_CHECK(pParams->device_name, NULL);
Qcloud_IoT_Client *mqtt_client = NULL;
// create and init MQTTClient
if ((mqtt_client = (Qcloud_IoT_Client *)HAL_Malloc(sizeof(Qcloud_IoT_Client))) == NULL) {
Log_e("malloc MQTTClient failed");
return NULL;
}
int rc = qcloud_iot_mqtt_init(mqtt_client, pParams);
if (rc != QCLOUD_RET_SUCCESS) {
Log_e("mqtt init failed: %d", rc);
HAL_Free(mqtt_client);
return NULL;
}
MQTTConnectParams connect_params = DEFAULT_MQTTCONNECT_PARAMS;
connect_params.client_id = mqtt_client->device_info.client_id;
// Upper limit of keep alive interval is (11.5 * 60) seconds
connect_params.keep_alive_interval = Min(pParams->keep_alive_interval_ms / 1000, 690);
connect_params.clean_session = pParams->clean_session;
connect_params.auto_connect_enable = pParams->auto_connect_enable;
#if defined(AUTH_WITH_NOTLS) && defined(AUTH_MODE_KEY)
if (pParams->device_secret == NULL) {
Log_e("Device secret is null!");
qcloud_iot_mqtt_fini(mqtt_client);
HAL_Free(mqtt_client);
return NULL;
}
size_t src_len = strlen(pParams->device_secret);
size_t len;
memset(mqtt_client->psk_decode, 0x00, DECODE_PSK_LENGTH);
rc = qcloud_iot_utils_base64decode(mqtt_client->psk_decode, DECODE_PSK_LENGTH, &len,
(unsigned char *)pParams->device_secret, src_len);
connect_params.device_secret = (char *)mqtt_client->psk_decode;
connect_params.device_secret_len = len;
if (rc != QCLOUD_RET_SUCCESS) {
Log_e("Device secret decode err, secret:%s", pParams->device_secret);
qcloud_iot_mqtt_fini(mqtt_client);
HAL_Free(mqtt_client);
return NULL;
}
#endif
rc = qcloud_iot_mqtt_connect(mqtt_client, &connect_params);
if (rc != QCLOUD_RET_SUCCESS) {
Log_e("mqtt connect with id: %s failed: %d", mqtt_client->options.conn_id, rc);
qcloud_iot_mqtt_fini(mqtt_client);
HAL_Free(mqtt_client);
return NULL;
} else {
Log_i("mqtt connect with id: %s success", mqtt_client->options.conn_id);
}
#ifdef LOG_UPLOAD
// log subscribe topics
if (is_log_uploader_init()) {
int log_level;
rc = qcloud_get_log_level(mqtt_client, &log_level);
// rc = qcloud_log_topic_subscribe(mqtt_client);
if (rc < 0) {
Log_e("client get log topic failed: %d", rc);
}
set_log_mqtt_client((void *)mqtt_client);
IOT_Log_Upload(true);
}
#endif
return mqtt_client;
}
int IOT_MQTT_Destroy(void **pClient)
{
POINTER_SANITY_CHECK(*pClient, QCLOUD_ERR_INVAL);
Qcloud_IoT_Client *mqtt_client = (Qcloud_IoT_Client *)(*pClient);
int rc = qcloud_iot_mqtt_disconnect(mqtt_client);
// disconnect network stack by force
if (rc != QCLOUD_RET_SUCCESS) {
mqtt_client->network_stack.disconnect(&(mqtt_client->network_stack));
set_client_conn_state(mqtt_client, NOTCONNECTED);
}
int i = 0;
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
/* notify this event to topic subscriber */
if (NULL != mqtt_client->sub_handles[i].topic_filter && NULL != mqtt_client->sub_handles[i].sub_event_handler)
mqtt_client->sub_handles[i].sub_event_handler(mqtt_client, MQTT_EVENT_CLIENT_DESTROY,
mqtt_client->sub_handles[i].handler_user_data);
if (NULL != mqtt_client->sub_handles[i].topic_filter) {
HAL_Free((void *)mqtt_client->sub_handles[i].topic_filter);
mqtt_client->sub_handles[i].topic_filter = NULL;
}
}
#ifdef MQTT_RMDUP_MSG_ENABLED
reset_repeat_packet_id_buffer(mqtt_client);
#endif
HAL_MutexDestroy(mqtt_client->lock_generic);
HAL_MutexDestroy(mqtt_client->lock_write_buf);
HAL_MutexDestroy(mqtt_client->lock_list_sub);
HAL_MutexDestroy(mqtt_client->lock_list_pub);
list_destroy(mqtt_client->list_pub_wait_ack);
list_destroy(mqtt_client->list_sub_wait_ack);
HAL_Free(*pClient);
*pClient = NULL;
#ifdef LOG_UPLOAD
set_log_mqtt_client(NULL);
#endif
Log_i("mqtt release!");
return rc;
}
int IOT_MQTT_Yield(void *pClient, uint32_t timeout_ms)
{
Qcloud_IoT_Client *mqtt_client = (Qcloud_IoT_Client *)pClient;
#ifdef MULTITHREAD_ENABLED
/* only one instance of yield is allowed in running state*/
if (mqtt_client->thread_running) {
HAL_SleepMs(timeout_ms);
return QCLOUD_RET_SUCCESS;
}
#endif
int rc = qcloud_iot_mqtt_yield(mqtt_client, timeout_ms);
#ifdef LOG_UPLOAD
/* do instant log uploading if MQTT communication error */
if (rc == QCLOUD_RET_SUCCESS)
IOT_Log_Upload(false);
else
IOT_Log_Upload(true);
#endif
return rc;
}
int IOT_MQTT_Publish(void *pClient, char *topicName, PublishParams *pParams)
{
Qcloud_IoT_Client *mqtt_client = (Qcloud_IoT_Client *)pClient;
return qcloud_iot_mqtt_publish(mqtt_client, topicName, pParams);
}
int IOT_MQTT_Subscribe(void *pClient, char *topicFilter, SubscribeParams *pParams)
{
Qcloud_IoT_Client *mqtt_client = (Qcloud_IoT_Client *)pClient;
return qcloud_iot_mqtt_subscribe(mqtt_client, topicFilter, pParams);
}
int IOT_MQTT_Unsubscribe(void *pClient, char *topicFilter)
{
Qcloud_IoT_Client *mqtt_client = (Qcloud_IoT_Client *)pClient;
return qcloud_iot_mqtt_unsubscribe(mqtt_client, topicFilter);
}
bool IOT_MQTT_IsSubReady(void *pClient, char *topicFilter)
{
Qcloud_IoT_Client *mqtt_client = (Qcloud_IoT_Client *)pClient;
return qcloud_iot_mqtt_is_sub_ready(mqtt_client, topicFilter);
}
bool IOT_MQTT_IsConnected(void *pClient)
{
IOT_FUNC_ENTRY;
POINTER_SANITY_CHECK(pClient, QCLOUD_ERR_INVAL);
Qcloud_IoT_Client *mqtt_client = (Qcloud_IoT_Client *)pClient;
IOT_FUNC_EXIT_RC(get_client_conn_state(mqtt_client) == 1)
}
#ifdef MULTITHREAD_ENABLED
static void _mqtt_yield_thread(void *ptr)
{
int rc = QCLOUD_RET_SUCCESS;
Qcloud_IoT_Client *mqtt_client = (Qcloud_IoT_Client *)ptr;
Log_i("MQTT client %s start loop", mqtt_client->device_info.client_id);
while (mqtt_client->thread_running) {
int rc = qcloud_iot_mqtt_yield(mqtt_client, 500);
#ifdef LOG_UPLOAD
/* do instant log uploading if MQTT communication error */
if (rc == QCLOUD_RET_SUCCESS)
IOT_Log_Upload(false);
else
IOT_Log_Upload(true);
#endif
if (rc == QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT) {
HAL_SleepMs(500);
continue;
} else if (rc == QCLOUD_RET_MQTT_MANUALLY_DISCONNECTED || rc == QCLOUD_ERR_MQTT_RECONNECT_TIMEOUT) {
Log_e("MQTT Yield thread exit with error: %d", rc);
break;
} else if (rc != QCLOUD_RET_SUCCESS && rc != QCLOUD_RET_MQTT_RECONNECTED) {
Log_e("MQTT Yield thread error: %d", rc);
}
HAL_SleepMs(200);
}
mqtt_client->thread_running = false;
mqtt_client->thread_exit_code = rc;
#ifdef LOG_UPLOAD
IOT_Log_Upload(true);
#endif
Log_i("MQTT client %s stop loop", mqtt_client->device_info.client_id);
}
int IOT_MQTT_StartLoop(void *pClient)
{
POINTER_SANITY_CHECK(pClient, QCLOUD_ERR_INVAL);
Qcloud_IoT_Client *mqtt_client = (Qcloud_IoT_Client *)pClient;
ThreadParams thread_params = {0};
thread_params.thread_func = _mqtt_yield_thread;
thread_params.thread_name = "MQTT_yield_thread";
thread_params.user_arg = pClient;
thread_params.stack_size = 4096;
thread_params.priority = 1;
mqtt_client->thread_running = true;
int rc = HAL_ThreadCreate(&thread_params);
if (rc) {
Log_e("create mqtt yield thread fail: %d", rc);
return QCLOUD_ERR_FAILURE;
}
HAL_SleepMs(500);
return QCLOUD_RET_SUCCESS;
}
void IOT_MQTT_StopLoop(void *pClient)
{
POINTER_SANITY_CHECK_RTN(pClient);
Qcloud_IoT_Client *mqtt_client = (Qcloud_IoT_Client *)pClient;
mqtt_client->thread_running = false;
HAL_SleepMs(1000);
return;
}
bool IOT_MQTT_GetLoopStatus(void *pClient, int *exit_code)
{
POINTER_SANITY_CHECK(pClient, false);
Qcloud_IoT_Client *mqtt_client = (Qcloud_IoT_Client *)pClient;
*exit_code = mqtt_client->thread_exit_code;
return mqtt_client->thread_running;
}
#endif
#if 0
static inline void _strlowr(char *str)
{
while (*str != '\0') {
*str = tolower(*str);
str++;
}
}
#endif
int qcloud_iot_mqtt_init(Qcloud_IoT_Client *pClient, MQTTInitParams *pParams)
{
IOT_FUNC_ENTRY;
POINTER_SANITY_CHECK(pClient, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(pParams, QCLOUD_ERR_INVAL);
memset(pClient, 0x0, sizeof(Qcloud_IoT_Client));
int rc = iot_device_info_set(&(pClient->device_info), pParams->product_id, pParams->device_name);
if (rc != QCLOUD_RET_SUCCESS) {
Log_e("failed to set device info: %d", rc);
return rc;
}
int size =
HAL_Snprintf(pClient->host_addr, HOST_STR_LENGTH, "%s.%s", pParams->product_id, QCLOUD_IOT_MQTT_DIRECT_DOMAIN);
if (size < 0 || size > HOST_STR_LENGTH - 1) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
// enable below code for some special platform
//_strlowr(s_qcloud_iot_host);
int i = 0;
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
pClient->sub_handles[i].topic_filter = NULL;
pClient->sub_handles[i].message_handler = NULL;
pClient->sub_handles[i].sub_event_handler = NULL;
pClient->sub_handles[i].qos = QOS0;
pClient->sub_handles[i].handler_user_data = NULL;
}
if (pParams->command_timeout < MIN_COMMAND_TIMEOUT)
pParams->command_timeout = MIN_COMMAND_TIMEOUT;
if (pParams->command_timeout > MAX_COMMAND_TIMEOUT)
pParams->command_timeout = MAX_COMMAND_TIMEOUT;
pClient->command_timeout_ms = pParams->command_timeout;
// packet id, random from [1 - 65536]
pClient->next_packet_id = _get_random_start_packet_id();
pClient->write_buf_size = QCLOUD_IOT_MQTT_TX_BUF_LEN;
pClient->read_buf_size = QCLOUD_IOT_MQTT_RX_BUF_LEN;
pClient->is_ping_outstanding = 0;
pClient->was_manually_disconnected = 0;
pClient->counter_network_disconnected = 0;
pClient->event_handle = pParams->event_handle;
pClient->lock_generic = HAL_MutexCreate();
if (NULL == pClient->lock_generic) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
set_client_conn_state(pClient, NOTCONNECTED);
if ((pClient->lock_write_buf = HAL_MutexCreate()) == NULL) {
Log_e("create write buf lock failed.");
goto error;
}
if ((pClient->lock_list_sub = HAL_MutexCreate()) == NULL) {
Log_e("create sub list lock failed.");
goto error;
}
if ((pClient->lock_list_pub = HAL_MutexCreate()) == NULL) {
Log_e("create pub list lock failed.");
goto error;
}
if ((pClient->list_pub_wait_ack = list_new()) == NULL) {
Log_e("create pub wait list failed.");
goto error;
}
pClient->list_pub_wait_ack->free = HAL_Free;
if ((pClient->list_sub_wait_ack = list_new()) == NULL) {
Log_e("create sub wait list failed.");
goto error;
}
pClient->list_sub_wait_ack->free = HAL_Free;
#ifndef AUTH_WITH_NOTLS
// device param for TLS connection
#ifdef AUTH_MODE_CERT
Log_d("cert file: %s", pParams->cert_file);
Log_d("key file: %s", pParams->key_file);
strncpy(pClient->cert_file_path, pParams->cert_file, FILE_PATH_MAX_LEN - 1);
strncpy(pClient->key_file_path, pParams->key_file, FILE_PATH_MAX_LEN - 1);
pClient->network_stack.ssl_connect_params.cert_file = pClient->cert_file_path;
pClient->network_stack.ssl_connect_params.key_file = pClient->key_file_path;
pClient->network_stack.ssl_connect_params.ca_crt = iot_ca_get();
pClient->network_stack.ssl_connect_params.ca_crt_len = strlen(pClient->network_stack.ssl_connect_params.ca_crt);
#else
if (pParams->device_secret != NULL) {
size_t src_len = strlen(pParams->device_secret);
size_t len;
memset(pClient->psk_decode, 0x00, DECODE_PSK_LENGTH);
qcloud_iot_utils_base64decode(pClient->psk_decode, DECODE_PSK_LENGTH, &len,
(unsigned char *)pParams->device_secret, src_len);
pClient->network_stack.ssl_connect_params.psk = (char *)pClient->psk_decode;
pClient->network_stack.ssl_connect_params.psk_length = len;
} else {
Log_e("psk is empty!");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
}
if (strnlen(pClient->device_info.client_id, MAX_SIZE_OF_CLIENT_ID) == 0) {
Log_e("psk id is empty!");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
}
pClient->network_stack.ssl_connect_params.psk_id = pClient->device_info.client_id;
pClient->network_stack.ssl_connect_params.ca_crt = NULL;
pClient->network_stack.ssl_connect_params.ca_crt_len = 0;
#endif
pClient->network_stack.host = pClient->host_addr;
pClient->network_stack.port = MQTT_SERVER_PORT_TLS;
pClient->network_stack.ssl_connect_params.timeout_ms =
pClient->command_timeout_ms > QCLOUD_IOT_TLS_HANDSHAKE_TIMEOUT ? pClient->command_timeout_ms
: QCLOUD_IOT_TLS_HANDSHAKE_TIMEOUT;
#else
pClient->network_stack.host = pClient->host_addr;
pClient->network_stack.port = MQTT_SERVER_PORT_NOTLS;
#endif
// init network stack
qcloud_iot_mqtt_network_init(&(pClient->network_stack));
// ping timer and reconnect delay timer
InitTimer(&(pClient->ping_timer));
InitTimer(&(pClient->reconnect_delay_timer));
#ifdef SYSTEM_COMM
pClient->sys_state.result_recv_ok = false;
pClient->sys_state.topic_sub_ok = false;
pClient->sys_state.time = 0;
#endif
#ifdef MULTITHREAD_ENABLED
pClient->thread_running = false;
#endif
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
error:
if (pClient->list_pub_wait_ack) {
pClient->list_pub_wait_ack->free(pClient->list_pub_wait_ack);
pClient->list_pub_wait_ack = NULL;
}
if (pClient->list_sub_wait_ack) {
pClient->list_sub_wait_ack->free(pClient->list_sub_wait_ack);
pClient->list_sub_wait_ack = NULL;
}
if (pClient->lock_generic) {
HAL_MutexDestroy(pClient->lock_generic);
pClient->lock_generic = NULL;
}
if (pClient->lock_list_sub) {
HAL_MutexDestroy(pClient->lock_list_sub);
pClient->lock_list_sub = NULL;
}
if (pClient->lock_list_pub) {
HAL_MutexDestroy(pClient->lock_list_pub);
pClient->lock_list_pub = NULL;
}
if (pClient->lock_write_buf) {
HAL_MutexDestroy(pClient->lock_write_buf);
pClient->lock_write_buf = NULL;
}
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE)
}
int qcloud_iot_mqtt_fini(Qcloud_IoT_Client *mqtt_client)
{
IOT_FUNC_ENTRY;
POINTER_SANITY_CHECK(mqtt_client, QCLOUD_ERR_INVAL);
HAL_MutexDestroy(mqtt_client->lock_generic);
HAL_MutexDestroy(mqtt_client->lock_write_buf);
HAL_MutexDestroy(mqtt_client->lock_list_sub);
HAL_MutexDestroy(mqtt_client->lock_list_pub);
list_destroy(mqtt_client->list_pub_wait_ack);
list_destroy(mqtt_client->list_sub_wait_ack);
Log_i("release mqtt client resources");
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
int qcloud_iot_mqtt_set_autoreconnect(Qcloud_IoT_Client *pClient, bool value)
{
IOT_FUNC_ENTRY;
POINTER_SANITY_CHECK(pClient, QCLOUD_ERR_INVAL);
pClient->options.auto_connect_enable = (uint8_t)value;
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
bool qcloud_iot_mqtt_is_autoreconnect_enabled(Qcloud_IoT_Client *pClient)
{
IOT_FUNC_ENTRY;
POINTER_SANITY_CHECK(pClient, QCLOUD_ERR_INVAL);
bool is_enabled = false;
if (pClient->options.auto_connect_enable == 1) {
is_enabled = true;
}
IOT_FUNC_EXIT_RC(is_enabled);
}
int qcloud_iot_mqtt_get_network_disconnected_count(Qcloud_IoT_Client *pClient)
{
IOT_FUNC_ENTRY;
POINTER_SANITY_CHECK(pClient, QCLOUD_ERR_INVAL);
IOT_FUNC_EXIT_RC(pClient->counter_network_disconnected);
}
int qcloud_iot_mqtt_reset_network_disconnected_count(Qcloud_IoT_Client *pClient)
{
IOT_FUNC_ENTRY;
POINTER_SANITY_CHECK(pClient, QCLOUD_ERR_INVAL);
pClient->counter_network_disconnected = 0;
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
#ifdef __cplusplus
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,477 @@
/*******************************************************************************
* Copyright (c) 2014 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Allan Stockdill-Mander/Ian Craggs - initial API and implementation and/or initial documentation
*******************************************************************************/
#ifdef __cplusplus
extern "C" {
#endif
#include <limits.h>
#include <string.h>
#include "mqtt_client.h"
#include "qcloud_iot_common.h"
#include "utils_hmac.h"
#define MQTT_CONNECT_FLAG_USERNAME 0x80
#define MQTT_CONNECT_FLAG_PASSWORD 0x40
#define MQTT_CONNECT_FLAG_WILL_RETAIN 0x20
#define MQTT_CONNECT_FLAG_WILL_QOS2 0x18
#define MQTT_CONNECT_FLAG_WILL_QOS1 0x08
#define MQTT_CONNECT_FLAG_WILL_QOS0 0x00
#define MQTT_CONNECT_FLAG_WILL_FLAG 0x04
#define MQTT_CONNECT_FLAG_CLEAN_SES 0x02
#define MQTT_CONNACK_FLAG_SES_PRE 0x01
/**
* Connect return code
*/
typedef enum {
CONNACK_CONNECTION_ACCEPTED = 0, // connection accepted
CONANCK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR = 1, // connection refused: unaccpeted protocol verison
CONNACK_IDENTIFIER_REJECTED_ERROR = 2, // connection refused: identifier rejected
CONNACK_SERVER_UNAVAILABLE_ERROR = 3, // connection refused: server unavailable
CONNACK_BAD_USERDATA_ERROR = 4, // connection refused: bad user name or password
CONNACK_NOT_AUTHORIZED_ERROR = 5 // connection refused: not authorized
} MQTTConnackReturnCodes;
/**
* Determines the length of the MQTT connect packet that would be produced using the supplied connect options.
* @param options the options to be used to build the connect packet
* @param the length of buffer needed to contain the serialized version of the packet
* @return int indicating function execution status
*/
static uint32_t _get_packet_connect_rem_len(MQTTConnectParams *options)
{
size_t len = 0;
/* variable depending on MQTT or MQIsdp */
if (3 == options->mqtt_version) {
len = 12;
} else if (4 == options->mqtt_version) {
len = 10;
}
len += strlen(options->client_id) + 2;
if (options->username) {
len += strlen(options->username) + 2;
}
if (options->password) {
len += strlen(options->password) + 2;
}
return (uint32_t)len;
}
static void _copy_connect_params(MQTTConnectParams *destination, MQTTConnectParams *source)
{
POINTER_SANITY_CHECK_RTN(destination);
POINTER_SANITY_CHECK_RTN(source);
/* In case of reconnecting, source == destination */
if (source == destination) {
return;
}
destination->mqtt_version = source->mqtt_version;
destination->client_id = source->client_id;
destination->username = source->username;
destination->keep_alive_interval = source->keep_alive_interval;
destination->clean_session = source->clean_session;
destination->auto_connect_enable = source->auto_connect_enable;
#ifdef AUTH_WITH_NOTLS
destination->device_secret = source->device_secret;
destination->device_secret_len = source->device_secret_len;
#endif
}
/**
* Serializes the connect options into the buffer.
* @param buf the buffer into which the packet will be serialized
* @param len the length in bytes of the supplied buffer
* @param options the options to be used to build the connect packet
* @param serialized length
* @return int indicating function execution status
*/
static int _serialize_connect_packet(unsigned char *buf, size_t buf_len, MQTTConnectParams *options,
uint32_t *serialized_len)
{
IOT_FUNC_ENTRY;
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(options, QCLOUD_ERR_INVAL);
STRING_PTR_SANITY_CHECK(options->client_id, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(serialized_len, QCLOUD_ERR_INVAL);
unsigned char *ptr = buf;
unsigned char header = 0;
unsigned char flags = 0;
uint32_t rem_len = 0;
int rc;
long cur_timesec = HAL_Timer_current_sec() + MAX_ACCESS_EXPIRE_TIMEOUT / 1000;
if (cur_timesec <= 0 || MAX_ACCESS_EXPIRE_TIMEOUT <= 0) {
cur_timesec = LONG_MAX;
}
long cur_timesec_bak = cur_timesec;
int cur_timesec_len = 0;
while (cur_timesec_bak != 0) {
cur_timesec_bak /= 10;
++cur_timesec_len;
}
int username_len =
strlen(options->client_id) + strlen(QCLOUD_IOT_DEVICE_SDK_APPID) + MAX_CONN_ID_LEN + cur_timesec_len + 4;
options->username = (char *)HAL_Malloc(username_len);
if (options->username == NULL) {
Log_e("malloc username failed!");
rc = QCLOUD_ERR_MALLOC;
goto err_exit;
}
get_next_conn_id(options->conn_id);
HAL_Snprintf(options->username, username_len, "%s;%s;%s;%ld", options->client_id, QCLOUD_IOT_DEVICE_SDK_APPID,
options->conn_id, cur_timesec);
#if defined(AUTH_WITH_NOTLS) && defined(AUTH_MODE_KEY)
if (options->device_secret != NULL && options->username != NULL) {
char sign[41] = {0};
utils_hmac_sha1(options->username, strlen(options->username), sign, options->device_secret,
options->device_secret_len);
options->password = (char *)HAL_Malloc(51);
if (options->password == NULL) {
Log_e("malloc password failed!");
rc = QCLOUD_ERR_MALLOC;
goto err_exit;
}
HAL_Snprintf(options->password, 51, "%s;hmacsha1", sign);
}
#endif
rem_len = _get_packet_connect_rem_len(options);
if (get_mqtt_packet_len(rem_len) > buf_len) {
Log_e("get_mqtt_packet_len failed!");
rc = QCLOUD_ERR_BUF_TOO_SHORT;
goto err_exit;
}
rc = mqtt_init_packet_header(&header, CONNECT, QOS0, 0, 0);
if (QCLOUD_RET_SUCCESS != rc) {
Log_e("mqtt_init_packet_header failed!");
goto err_exit;
}
// 1st byte in fixed header
mqtt_write_char(&ptr, header);
// remaining length
ptr += mqtt_write_packet_rem_len(ptr, rem_len);
// MQTT protocol name and version in variable header
if (4 == options->mqtt_version) {
mqtt_write_utf8_string(&ptr, "MQTT");
mqtt_write_char(&ptr, (unsigned char)4);
} else {
mqtt_write_utf8_string(&ptr, "MQIsdp");
mqtt_write_char(&ptr, (unsigned char)3);
}
// flags in variable header
flags |= (options->clean_session) ? MQTT_CONNECT_FLAG_CLEAN_SES : 0;
flags |= (options->username != NULL) ? MQTT_CONNECT_FLAG_USERNAME : 0;
#if defined(AUTH_WITH_NOTLS) && defined(AUTH_MODE_KEY)
flags |= MQTT_CONNECT_FLAG_PASSWORD;
#endif
mqtt_write_char(&ptr, flags);
// keep alive interval (unit:ms) in variable header
mqtt_write_uint_16(&ptr, options->keep_alive_interval);
// client id
mqtt_write_utf8_string(&ptr, options->client_id);
if ((flags & MQTT_CONNECT_FLAG_USERNAME) && options->username != NULL) {
mqtt_write_utf8_string(&ptr, options->username);
HAL_Free(options->username);
options->username = NULL;
}
if ((flags & MQTT_CONNECT_FLAG_PASSWORD) && options->password != NULL) {
mqtt_write_utf8_string(&ptr, options->password);
HAL_Free(options->password);
options->password = NULL;
}
*serialized_len = (uint32_t)(ptr - buf);
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
err_exit:
HAL_Free(options->username);
options->username = NULL;
HAL_Free(options->password);
options->password = NULL;
IOT_FUNC_EXIT_RC(rc);
}
/**
* Deserializes the supplied (wire) buffer into connack data - return code
* @param sessionPresent the session present flag returned (only for MQTT 3.1.1)
* @param connack_rc returned integer value of the connack return code
* @param buf the raw buffer data, of the correct length determined by the remaining length field
* @param buflen the length in bytes of the data in the supplied buffer
* @return int indicating function execution status
*/
static int _deserialize_connack_packet(uint8_t *sessionPresent, int *connack_rc, unsigned char *buf, size_t buflen)
{
IOT_FUNC_ENTRY;
POINTER_SANITY_CHECK(sessionPresent, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(connack_rc, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
unsigned char header, type = 0;
unsigned char *curdata = buf;
unsigned char *enddata = NULL;
int rc;
uint32_t decodedLen = 0, readBytesLen = 0;
unsigned char flags = 0;
unsigned char connack_rc_char;
// CONNACK: 2 bytes in fixed header and 2 bytes in variable header, no payload
if (4 > buflen) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_BUF_TOO_SHORT);
}
header = mqtt_read_char(&curdata);
type = (header & MQTT_HEADER_TYPE_MASK) >> MQTT_HEADER_TYPE_SHIFT;
if (CONNACK != type) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
rc = mqtt_read_packet_rem_len_form_buf(curdata, &decodedLen, &readBytesLen);
if (QCLOUD_RET_SUCCESS != rc) {
IOT_FUNC_EXIT_RC(rc);
}
curdata += (readBytesLen);
enddata = curdata + decodedLen;
if (enddata - curdata != 2) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
// variable header - connack flag, refer to MQTT spec 3.2.2.1
flags = mqtt_read_char(&curdata);
*sessionPresent = flags & MQTT_CONNACK_FLAG_SES_PRE;
// variable header - return code, refer to MQTT spec 3.2.2.3
connack_rc_char = mqtt_read_char(&curdata);
switch (connack_rc_char) {
case CONNACK_CONNECTION_ACCEPTED:
*connack_rc = QCLOUD_RET_MQTT_CONNACK_CONNECTION_ACCEPTED;
break;
case CONANCK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR:
*connack_rc = QCLOUD_ERR_MQTT_CONNACK_UNACCEPTABLE_PROTOCOL_VERSION;
break;
case CONNACK_IDENTIFIER_REJECTED_ERROR:
*connack_rc = QCLOUD_ERR_MQTT_CONNACK_IDENTIFIER_REJECTED;
break;
case CONNACK_SERVER_UNAVAILABLE_ERROR:
*connack_rc = QCLOUD_ERR_MQTT_CONNACK_SERVER_UNAVAILABLE;
break;
case CONNACK_BAD_USERDATA_ERROR:
*connack_rc = QCLOUD_ERR_MQTT_CONNACK_BAD_USERDATA;
break;
case CONNACK_NOT_AUTHORIZED_ERROR:
*connack_rc = QCLOUD_ERR_MQTT_CONNACK_NOT_AUTHORIZED;
break;
default:
*connack_rc = QCLOUD_ERR_MQTT_CONNACK_UNKNOWN;
break;
}
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
/**
* @brief Setup connection with MQTT server
*
* @param pClient
* @param options
* @return
*/
static int _mqtt_connect(Qcloud_IoT_Client *pClient, MQTTConnectParams *options)
{
IOT_FUNC_ENTRY;
Timer connect_timer;
int connack_rc = QCLOUD_ERR_FAILURE, rc = QCLOUD_ERR_FAILURE;
uint8_t sessionPresent = 0;
uint32_t len = 0;
InitTimer(&connect_timer);
countdown_ms(&connect_timer, pClient->command_timeout_ms);
if (NULL != options) {
_copy_connect_params(&(pClient->options), options);
}
// TCP or TLS network connect
rc = pClient->network_stack.connect(&(pClient->network_stack));
if (QCLOUD_RET_SUCCESS != rc) {
IOT_FUNC_EXIT_RC(rc);
}
HAL_MutexLock(pClient->lock_write_buf);
// serialize CONNECT packet
rc = _serialize_connect_packet(pClient->write_buf, pClient->write_buf_size, &(pClient->options), &len);
if (QCLOUD_RET_SUCCESS != rc || 0 == len) {
HAL_MutexUnlock(pClient->lock_write_buf);
IOT_FUNC_EXIT_RC(rc);
}
// send CONNECT packet
rc = send_mqtt_packet(pClient, len, &connect_timer);
if (QCLOUD_RET_SUCCESS != rc) {
HAL_MutexUnlock(pClient->lock_write_buf);
IOT_FUNC_EXIT_RC(rc);
}
HAL_MutexUnlock(pClient->lock_write_buf);
// wait for CONNACK
rc = wait_for_read(pClient, CONNACK, &connect_timer, QOS0);
if (QCLOUD_RET_SUCCESS != rc) {
IOT_FUNC_EXIT_RC(rc);
}
// deserialize CONNACK and check reture code
rc = _deserialize_connack_packet(&sessionPresent, &connack_rc, pClient->read_buf, pClient->read_buf_size);
if (QCLOUD_RET_SUCCESS != rc) {
IOT_FUNC_EXIT_RC(rc);
}
if (QCLOUD_RET_MQTT_CONNACK_CONNECTION_ACCEPTED != connack_rc) {
IOT_FUNC_EXIT_RC(connack_rc);
}
set_client_conn_state(pClient, CONNECTED);
HAL_MutexLock(pClient->lock_generic);
pClient->was_manually_disconnected = 0;
pClient->is_ping_outstanding = 0;
countdown(&pClient->ping_timer, pClient->options.keep_alive_interval);
HAL_MutexUnlock(pClient->lock_generic);
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
int qcloud_iot_mqtt_connect(Qcloud_IoT_Client *pClient, MQTTConnectParams *pParams)
{
IOT_FUNC_ENTRY;
int rc;
POINTER_SANITY_CHECK(pClient, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(pParams, QCLOUD_ERR_INVAL);
// check connection state first
if (get_client_conn_state(pClient)) {
IOT_FUNC_EXIT_RC(QCLOUD_RET_MQTT_ALREADY_CONNECTED);
}
rc = _mqtt_connect(pClient, pParams);
// disconnect network if connect fail
if (rc != QCLOUD_RET_SUCCESS) {
pClient->network_stack.disconnect(&(pClient->network_stack));
}
IOT_FUNC_EXIT_RC(rc);
}
int qcloud_iot_mqtt_attempt_reconnect(Qcloud_IoT_Client *pClient)
{
IOT_FUNC_ENTRY;
int rc;
POINTER_SANITY_CHECK(pClient, QCLOUD_ERR_INVAL);
Log_i("attempt to reconnect...");
if (get_client_conn_state(pClient)) {
IOT_FUNC_EXIT_RC(QCLOUD_RET_MQTT_ALREADY_CONNECTED);
}
rc = qcloud_iot_mqtt_connect(pClient, &pClient->options);
if (!get_client_conn_state(pClient)) {
IOT_FUNC_EXIT_RC(rc);
}
rc = qcloud_iot_mqtt_resubscribe(pClient);
if (rc != QCLOUD_RET_SUCCESS) {
IOT_FUNC_EXIT_RC(rc);
}
IOT_FUNC_EXIT_RC(QCLOUD_RET_MQTT_RECONNECTED);
}
int qcloud_iot_mqtt_disconnect(Qcloud_IoT_Client *pClient)
{
IOT_FUNC_ENTRY;
int rc;
POINTER_SANITY_CHECK(pClient, QCLOUD_ERR_INVAL);
Timer timer;
uint32_t serialized_len = 0;
if (get_client_conn_state(pClient) == 0) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
}
HAL_MutexLock(pClient->lock_write_buf);
rc = serialize_packet_with_zero_payload(pClient->write_buf, pClient->write_buf_size, DISCONNECT, &serialized_len);
if (rc != QCLOUD_RET_SUCCESS) {
HAL_MutexUnlock(pClient->lock_write_buf);
IOT_FUNC_EXIT_RC(rc);
}
InitTimer(&timer);
countdown_ms(&timer, pClient->command_timeout_ms);
if (serialized_len > 0) {
rc = send_mqtt_packet(pClient, serialized_len, &timer);
if (rc != QCLOUD_RET_SUCCESS) {
HAL_MutexUnlock(pClient->lock_write_buf);
IOT_FUNC_EXIT_RC(rc);
}
}
HAL_MutexUnlock(pClient->lock_write_buf);
pClient->network_stack.disconnect(&(pClient->network_stack));
set_client_conn_state(pClient, NOTCONNECTED);
pClient->was_manually_disconnected = 1;
Log_i("mqtt disconnect!");
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,60 @@
/*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright (C) 2018-2020 THL A29 Limited, a Tencent company. All rights reserved.
* Licensed under the MIT License (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "mqtt_client_net.h"
// TODO: how to implement
/**
* @brief Check if TLS connection is valid
*
* @param pNetwork
* @return
*/
int qcloud_iot_mqtt_tls_is_connected(Network *pNetwork)
{
return 1;
}
/**
* @brief Init network stack
*
* @param pNetwork
* @param pConnectParams
* @return
*/
int qcloud_iot_mqtt_network_init(Network *pNetwork)
{
int rc;
/* first choice: TLS */
pNetwork->type = NETWORK_TLS;
#ifdef AUTH_WITH_NOTLS
pNetwork->type = NETWORK_TCP;
#endif
rc = network_init(pNetwork);
pNetwork->is_connected = qcloud_iot_mqtt_tls_is_connected;
return rc;
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,383 @@
/*******************************************************************************
* Copyright (c) 2014 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs - fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=453144
*******************************************************************************/
#ifdef __cplusplus
extern "C" {
#endif
#include <string.h>
#include "mqtt_client.h"
#include "utils_list.h"
/**
* @param mqttstring the MQTTString structure into which the data is to be read
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
* @param enddata pointer to the end of the data: do not read beyond
* @return SUCCESS if successful, FAILURE if not
*/
static int _read_string_with_len(char **string, uint16_t *stringLen, unsigned char **pptr, unsigned char *enddata)
{
int rc = QCLOUD_ERR_FAILURE;
/* the first two bytes are the length of the string */
/* enough length to read the integer? */
if (enddata - (*pptr) > 1) {
*stringLen = mqtt_read_uint16_t(pptr); /* increments pptr to point past length */
if (*stringLen > QCLOUD_IOT_MQTT_RX_BUF_LEN) {
Log_e("stringLen exceed QCLOUD_IOT_MQTT_RX_BUF_LEN");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
if (&(*pptr)[*stringLen] <= enddata) {
*string = (char *)*pptr;
*pptr += *stringLen;
rc = QCLOUD_RET_SUCCESS;
}
}
return rc;
}
/**
* Determines the length of the MQTT publish packet that would be produced using the supplied parameters
* @param qos the MQTT QoS of the publish (packetid is omitted for QoS 0)
* @param topicName the topic name to be used in the publish
* @param payload_len the length of the payload to be sent
* @return the length of buffer needed to contain the serialized version of the packet
*/
static uint32_t _get_publish_packet_len(uint8_t qos, char *topicName, size_t payload_len)
{
size_t len = 0;
len += 2 + strlen(topicName) + payload_len;
if (qos > 0) {
len += 2; /* packetid */
}
return (uint32_t)len;
}
static int _mask_push_pubInfo_to(Qcloud_IoT_Client *c, int len, unsigned short msgId, ListNode **node)
{
IOT_FUNC_ENTRY;
if (!c || !node) {
Log_e("invalid parameters!");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_PUSH_TO_LIST_FAILED);
}
if ((len < 0) || (len > c->write_buf_size)) {
Log_e("the param of len is error!");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
HAL_MutexLock(c->lock_list_pub);
if (c->list_pub_wait_ack->len >= MAX_REPUB_NUM) {
HAL_MutexUnlock(c->lock_list_pub);
Log_e("more than %u elements in republish list. List overflow!", c->list_pub_wait_ack->len);
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
QcloudIotPubInfo *repubInfo = (QcloudIotPubInfo *)HAL_Malloc(sizeof(QcloudIotPubInfo) + len);
if (NULL == repubInfo) {
HAL_MutexUnlock(c->lock_list_pub);
Log_e("memory malloc failed!");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
repubInfo->node_state = MQTT_NODE_STATE_NORMANL;
repubInfo->msg_id = msgId;
repubInfo->len = len;
InitTimer(&repubInfo->pub_start_time);
countdown_ms(&repubInfo->pub_start_time, c->command_timeout_ms);
repubInfo->buf = (unsigned char *)repubInfo + sizeof(QcloudIotPubInfo);
memcpy(repubInfo->buf, c->write_buf, len);
*node = list_node_new(repubInfo);
if (NULL == *node) {
HAL_MutexUnlock(c->lock_list_pub);
HAL_Free(repubInfo);
Log_e("list_node_new failed!");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
list_rpush(c->list_pub_wait_ack, *node);
HAL_MutexUnlock(c->lock_list_pub);
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
/**
* Deserializes the supplied (wire) buffer into publish data
* @param dup returned integer - the MQTT dup flag
* @param qos returned integer - the MQTT QoS value
* @param retained returned integer - the MQTT retained flag
* @param packet_id returned integer - the MQTT packet identifier
* @param topicName returned MQTTString - the MQTT topic in the publish
* @param payload returned byte buffer - the MQTT publish payload
* @param payload_len returned integer - the length of the MQTT payload
* @param buf the raw buffer data, of the correct length determined by the remaining length field
* @param buf_len the length in bytes of the data in the supplied buffer
* @return error code. 1 is success
*/
int deserialize_publish_packet(uint8_t *dup, QoS *qos, uint8_t *retained, uint16_t *packet_id, char **topicName,
uint16_t *topicNameLen, unsigned char **payload, size_t *payload_len, unsigned char *buf,
size_t buf_len)
{
IOT_FUNC_ENTRY;
POINTER_SANITY_CHECK(dup, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(qos, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(retained, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(packet_id, QCLOUD_ERR_INVAL);
unsigned char header, type = 0;
unsigned char *curdata = buf;
unsigned char *enddata = NULL;
int rc;
uint32_t decodedLen = 0;
uint32_t readBytesLen = 0;
/* Publish header size is at least four bytes.
* Fixed header is two bytes.
* Variable header size depends on QoS And Topic Name.
* QoS level 0 doesn't have a message identifier (0 - 2 bytes)
* Topic Name length fields decide size of topic name field (at least 2 bytes)
* MQTT v3.1.1 Specification 3.3.1 */
if (4 > buf_len) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_BUF_TOO_SHORT);
}
header = mqtt_read_char(&curdata);
type = (header & MQTT_HEADER_TYPE_MASK) >> MQTT_HEADER_TYPE_SHIFT;
*dup = (header & MQTT_HEADER_DUP_MASK) >> MQTT_HEADER_DUP_SHIFT;
*qos = (QoS)((header & MQTT_HEADER_QOS_MASK) >> MQTT_HEADER_QOS_SHIFT);
*retained = header & MQTT_HEADER_RETAIN_MASK;
if (PUBLISH != type) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
/* read remaining length */
rc = mqtt_read_packet_rem_len_form_buf(curdata, &decodedLen, &readBytesLen);
if (QCLOUD_RET_SUCCESS != rc) {
IOT_FUNC_EXIT_RC(rc);
}
curdata += (readBytesLen);
enddata = curdata + decodedLen;
/* do we have enough data to read the protocol version byte? */
if (QCLOUD_RET_SUCCESS != _read_string_with_len(topicName, topicNameLen, &curdata, enddata) ||
(0 > (enddata - curdata))) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
if (QOS0 != *qos) {
*packet_id = mqtt_read_uint16_t(&curdata);
}
*payload_len = (size_t)(enddata - curdata);
*payload = curdata;
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
/**
* Serializes the ack packet into the supplied buffer.
* @param buf the buffer into which the packet will be serialized
* @param buf_len the length in bytes of the supplied buffer
* @param packet_type the MQTT packet type: 1.PUBACK; 2.PUBREL; 3.PUBCOMP
* @param dup the MQTT dup flag
* @param packet_id the MQTT packet identifier
* @return serialized length, or error if 0
*/
int serialize_pub_ack_packet(unsigned char *buf, size_t buf_len, MessageTypes packet_type, uint8_t dup,
uint16_t packet_id, uint32_t *serialized_len)
{
IOT_FUNC_ENTRY;
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(serialized_len, QCLOUD_ERR_INVAL);
unsigned char header = 0;
unsigned char *ptr = buf;
QoS requestQoS = (PUBREL == packet_type) ? QOS1 : QOS0; // refer to MQTT spec 3.6.1
int rc = mqtt_init_packet_header(&header, packet_type, requestQoS, dup, 0);
/* Minimum byte length required by ACK headers is
* 2 for fixed and 2 for variable part */
if (4 > buf_len) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_BUF_TOO_SHORT);
}
if (QCLOUD_RET_SUCCESS != rc) {
IOT_FUNC_EXIT_RC(rc);
}
mqtt_write_char(&ptr, header); /* write header */
ptr += mqtt_write_packet_rem_len(ptr, 2); /* write remaining length */
mqtt_write_uint_16(&ptr, packet_id);
*serialized_len = (uint32_t)(ptr - buf);
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
/**
* Serializes the supplied publish data into the supplied buffer, ready for sending
* @param buf the buffer into which the packet will be serialized
* @param buf_len the length in bytes of the supplied buffer
* @param dup integer - the MQTT dup flag
* @param qos integer - the MQTT QoS value
* @param retained integer - the MQTT retained flag
* @param packet_id integer - the MQTT packet identifier
* @param topicName MQTTString - the MQTT topic in the publish
* @param payload byte buffer - the MQTT publish payload
* @param payload_len integer - the length of the MQTT payload
* @return the length of the serialized data. <= 0 indicates error
*/
static int _serialize_publish_packet(unsigned char *buf, size_t buf_len, uint8_t dup, QoS qos, uint8_t retained,
uint16_t packet_id, char *topicName, unsigned char *payload, size_t payload_len,
uint32_t *serialized_len)
{
IOT_FUNC_ENTRY;
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(serialized_len, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(payload, QCLOUD_ERR_INVAL);
unsigned char *ptr = buf;
unsigned char header = 0;
uint32_t rem_len = 0;
int rc;
rem_len = _get_publish_packet_len(qos, topicName, payload_len);
if (get_mqtt_packet_len(rem_len) > buf_len) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_BUF_TOO_SHORT);
}
rc = mqtt_init_packet_header(&header, PUBLISH, qos, dup, retained);
if (QCLOUD_RET_SUCCESS != rc) {
IOT_FUNC_EXIT_RC(rc);
}
mqtt_write_char(&ptr, header); /* write header */
ptr += mqtt_write_packet_rem_len(ptr, rem_len); /* write remaining length */
;
mqtt_write_utf8_string(&ptr, topicName); /* Variable Header: Topic Name */
if (qos > 0) {
mqtt_write_uint_16(&ptr, packet_id); /* Variable Header: Topic Name */
}
memcpy(ptr, payload, payload_len);
ptr += payload_len;
*serialized_len = (uint32_t)(ptr - buf);
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
int qcloud_iot_mqtt_publish(Qcloud_IoT_Client *pClient, char *topicName, PublishParams *pParams)
{
IOT_FUNC_ENTRY;
POINTER_SANITY_CHECK(pClient, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(pParams, QCLOUD_ERR_INVAL);
STRING_PTR_SANITY_CHECK(topicName, QCLOUD_ERR_INVAL);
Timer timer;
uint32_t len = 0;
int rc;
ListNode *node = NULL;
size_t topicLen = strlen(topicName);
if (topicLen > MAX_SIZE_OF_CLOUD_TOPIC) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MAX_TOPIC_LENGTH);
}
if (pParams->qos == QOS2) {
Log_e("QoS2 is not supported currently");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_QOS_NOT_SUPPORT);
}
if (!get_client_conn_state(pClient)) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
}
InitTimer(&timer);
countdown_ms(&timer, pClient->command_timeout_ms);
HAL_MutexLock(pClient->lock_write_buf);
if (pParams->qos == QOS1) {
pParams->id = get_next_packet_id(pClient);
if (IOT_Log_Get_Level() <= eLOG_DEBUG) {
Log_d("publish topic seq=%d|topicName=%s|payload=%s", pParams->id, topicName, (char *)pParams->payload);
} else {
Log_i("publish topic seq=%d|topicName=%s", pParams->id, topicName);
}
} else {
if (IOT_Log_Get_Level() <= eLOG_DEBUG) {
Log_d("publish packetID=%d|topicName=%s|payload=%s", pParams->id, topicName, (char *)pParams->payload);
} else {
Log_i("publish packetID=%d|topicName=%s", pParams->id, topicName);
}
}
rc = _serialize_publish_packet(pClient->write_buf, pClient->write_buf_size, 0, pParams->qos, pParams->retained,
pParams->id, topicName, (unsigned char *)pParams->payload, pParams->payload_len,
&len);
if (QCLOUD_RET_SUCCESS != rc) {
HAL_MutexUnlock(pClient->lock_write_buf);
IOT_FUNC_EXIT_RC(rc);
}
if (pParams->qos > QOS0) {
rc = _mask_push_pubInfo_to(pClient, len, pParams->id, &node);
if (QCLOUD_RET_SUCCESS != rc) {
Log_e("push publish into to pubInfolist failed!");
HAL_MutexUnlock(pClient->lock_write_buf);
IOT_FUNC_EXIT_RC(rc);
}
}
/* send the publish packet */
rc = send_mqtt_packet(pClient, len, &timer);
if (QCLOUD_RET_SUCCESS != rc) {
if (pParams->qos > QOS0) {
HAL_MutexLock(pClient->lock_list_pub);
list_remove(pClient->list_pub_wait_ack, node);
HAL_MutexUnlock(pClient->lock_list_pub);
}
HAL_MutexUnlock(pClient->lock_write_buf);
IOT_FUNC_EXIT_RC(rc);
}
HAL_MutexUnlock(pClient->lock_write_buf);
IOT_FUNC_EXIT_RC(pParams->id);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,251 @@
/*******************************************************************************
* Copyright (c) 2014 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
*******************************************************************************/
#ifdef __cplusplus
extern "C" {
#endif
#include <string.h>
#include "mqtt_client.h"
/**
* Determines the length of the MQTT subscribe packet that would be produced using the supplied parameters
* @param count the number of topic filter strings in topicFilters
* @param topicFilters the array of topic filter strings to be used in the publish
* @return the length of buffer needed to contain the serialized version of the packet
*/
static uint32_t _get_subscribe_packet_rem_len(uint32_t count, char **topicFilters)
{
size_t i;
size_t len = 2; /* packetid */
for (i = 0; i < count; ++i) {
len += 2 + strlen(*topicFilters + i) + 1; /* length + topic + req_qos */
}
return (uint32_t)len;
}
/**
* Serializes the supplied subscribe data into the supplied buffer, ready for sending
* @param buf the buffer into which the packet will be serialized
* @param buf_len the length in bytes of the supplied bufferr
* @param dup integer - the MQTT dup flag
* @param packet_id integer - the MQTT packet identifier
* @param count - number of members in the topicFilters and reqQos arrays
* @param topicFilters - array of topic filter names
* @param requestedQoSs - array of requested QoS
* @return the length of the serialized data. <= 0 indicates error
*/
static int _serialize_subscribe_packet(unsigned char *buf, size_t buf_len, uint8_t dup, uint16_t packet_id,
uint32_t count, char **topicFilters, QoS *requestedQoSs,
uint32_t *serialized_len)
{
IOT_FUNC_ENTRY;
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(serialized_len, QCLOUD_ERR_INVAL);
unsigned char *ptr = buf;
unsigned char header = 0;
uint32_t rem_len = 0;
uint32_t i = 0;
int rc;
// remaining length of SUBSCRIBE packet = packet type(2 byte) + count * (remaining length(2 byte) + topicLen + qos(1
// byte))
rem_len = _get_subscribe_packet_rem_len(count, topicFilters);
if (get_mqtt_packet_len(rem_len) > buf_len) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_BUF_TOO_SHORT);
}
// init header
rc = mqtt_init_packet_header(&header, SUBSCRIBE, QOS1, dup, 0);
if (QCLOUD_RET_SUCCESS != rc) {
IOT_FUNC_EXIT_RC(rc);
}
// 1st byte in fixed header
mqtt_write_char(&ptr, header);
// remaining length
ptr += mqtt_write_packet_rem_len(ptr, rem_len);
// variable header
mqtt_write_uint_16(&ptr, packet_id);
// payload
for (i = 0; i < count; ++i) {
mqtt_write_utf8_string(&ptr, *topicFilters + i);
mqtt_write_char(&ptr, (unsigned char)requestedQoSs[i]);
}
*serialized_len = (uint32_t)(ptr - buf);
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
int qcloud_iot_mqtt_subscribe(Qcloud_IoT_Client *pClient, char *topicFilter, SubscribeParams *pParams)
{
IOT_FUNC_ENTRY;
int rc;
POINTER_SANITY_CHECK(pClient, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(pParams, QCLOUD_ERR_INVAL);
// POINTER_SANITY_CHECK(pParams->on_message_handler, QCLOUD_ERR_INVAL);
STRING_PTR_SANITY_CHECK(topicFilter, QCLOUD_ERR_INVAL);
Timer timer;
uint32_t len = 0;
uint16_t packet_id = 0;
ListNode *node = NULL;
size_t topicLen = strlen(topicFilter);
if (topicLen > MAX_SIZE_OF_CLOUD_TOPIC) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MAX_TOPIC_LENGTH);
}
if (pParams->qos == QOS2) {
Log_e("QoS2 is not supported currently");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_QOS_NOT_SUPPORT);
}
if (!get_client_conn_state(pClient)) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN)
}
/* topic filter should be valid in the whole sub life */
char *topic_filter_stored = HAL_Malloc(topicLen + 1);
if (topic_filter_stored == NULL) {
Log_e("malloc failed");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
strcpy(topic_filter_stored, topicFilter);
topic_filter_stored[topicLen] = 0;
InitTimer(&timer);
countdown_ms(&timer, pClient->command_timeout_ms);
HAL_MutexLock(pClient->lock_write_buf);
packet_id = get_next_packet_id(pClient);
Log_d("topicName=%s|packet_id=%d", topic_filter_stored, packet_id);
rc = _serialize_subscribe_packet(pClient->write_buf, pClient->write_buf_size, 0, packet_id, 1, &topic_filter_stored,
&pParams->qos, &len);
if (QCLOUD_RET_SUCCESS != rc) {
HAL_MutexUnlock(pClient->lock_write_buf);
HAL_Free(topic_filter_stored);
IOT_FUNC_EXIT_RC(rc);
}
/* add node into sub ack wait list */
SubTopicHandle sub_handle;
sub_handle.topic_filter = topic_filter_stored;
sub_handle.message_handler = pParams->on_message_handler;
sub_handle.sub_event_handler = pParams->on_sub_event_handler;
sub_handle.qos = pParams->qos;
sub_handle.handler_user_data = pParams->user_data;
rc = push_sub_info_to(pClient, len, (unsigned int)packet_id, SUBSCRIBE, &sub_handle, &node);
if (QCLOUD_RET_SUCCESS != rc) {
Log_e("push publish into to pubInfolist failed!");
HAL_MutexUnlock(pClient->lock_write_buf);
HAL_Free(topic_filter_stored);
IOT_FUNC_EXIT_RC(rc);
}
// send SUBSCRIBE packet
rc = send_mqtt_packet(pClient, len, &timer);
if (QCLOUD_RET_SUCCESS != rc) {
HAL_MutexLock(pClient->lock_list_sub);
list_remove(pClient->list_sub_wait_ack, node);
HAL_MutexUnlock(pClient->lock_list_sub);
HAL_MutexUnlock(pClient->lock_write_buf);
HAL_Free(topic_filter_stored);
IOT_FUNC_EXIT_RC(rc);
}
HAL_MutexUnlock(pClient->lock_write_buf);
IOT_FUNC_EXIT_RC(packet_id);
}
int qcloud_iot_mqtt_resubscribe(Qcloud_IoT_Client *pClient)
{
IOT_FUNC_ENTRY;
int rc;
POINTER_SANITY_CHECK(pClient, QCLOUD_ERR_INVAL);
uint32_t itr = 0;
char * topic = NULL;
SubscribeParams temp_param;
if (NULL == pClient) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
}
if (!get_client_conn_state(pClient)) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
}
for (itr = 0; itr < MAX_MESSAGE_HANDLERS; itr++) {
topic = (char *)pClient->sub_handles[itr].topic_filter;
if (topic == NULL) {
continue;
}
temp_param.on_message_handler = pClient->sub_handles[itr].message_handler;
temp_param.on_sub_event_handler = pClient->sub_handles[itr].sub_event_handler;
temp_param.qos = pClient->sub_handles[itr].qos;
temp_param.user_data = pClient->sub_handles[itr].handler_user_data;
rc = qcloud_iot_mqtt_subscribe(pClient, topic, &temp_param);
if (rc < 0) {
Log_e("resubscribe failed %d, topic: %s", rc, topic);
IOT_FUNC_EXIT_RC(rc);
}
}
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
bool qcloud_iot_mqtt_is_sub_ready(Qcloud_IoT_Client *pClient, char *topicFilter)
{
IOT_FUNC_ENTRY;
POINTER_SANITY_CHECK(pClient, false);
STRING_PTR_SANITY_CHECK(topicFilter, false);
size_t topicLen = strlen(topicFilter);
if (topicLen > MAX_SIZE_OF_CLOUD_TOPIC) {
return false;
}
int i = 0;
HAL_MutexLock(pClient->lock_generic);
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
if ((pClient->sub_handles[i].topic_filter != NULL &&
!strcmp(pClient->sub_handles[i].topic_filter, topicFilter)) ||
strstr(topicFilter, "/#") != NULL || strstr(topicFilter, "/+") != NULL) {
HAL_MutexUnlock(pClient->lock_generic);
return true;
}
}
HAL_MutexUnlock(pClient->lock_generic);
return false;
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,199 @@
/*******************************************************************************
* Copyright (c) 2014 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
*******************************************************************************/
#ifdef __cplusplus
extern "C" {
#endif
#include <string.h>
#include "mqtt_client.h"
/**
* Determines the length of the MQTT unsubscribe packet that would be produced using the supplied parameters
* @param count the number of topic filter strings in topicFilters
* @param topicFilters the array of topic filter strings to be used in the publish
* @return the length of buffer needed to contain the serialized version of the packet
*/
static uint32_t _get_unsubscribe_packet_rem_len(uint32_t count, char **topicFilters)
{
size_t i;
size_t len = 2; /* packetid */
for (i = 0; i < count; ++i) {
len += 2 + strlen(*topicFilters + i); /* length + topic*/
}
return (uint32_t)len;
}
/**
* Serializes the supplied unsubscribe data into the supplied buffer, ready for sending
* @param buf the raw buffer data, of the correct length determined by the remaining length field
* @param buf_len the length in bytes of the data in the supplied buffer
* @param dup integer - the MQTT dup flag
* @param packet_id integer - the MQTT packet identifier
* @param count - number of members in the topicFilters array
* @param topicFilters - array of topic filter names
* @param serialized_len - the length of the serialized data
* @return int indicating function execution status
*/
static int _serialize_unsubscribe_packet(unsigned char *buf, size_t buf_len, uint8_t dup, uint16_t packet_id,
uint32_t count, char **topicFilters, uint32_t *serialized_len)
{
IOT_FUNC_ENTRY;
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(serialized_len, QCLOUD_ERR_INVAL);
unsigned char *ptr = buf;
unsigned char header = 0;
uint32_t rem_len = 0;
uint32_t i = 0;
int rc;
rem_len = _get_unsubscribe_packet_rem_len(count, topicFilters);
if (get_mqtt_packet_len(rem_len) > buf_len) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_BUF_TOO_SHORT);
}
rc = mqtt_init_packet_header(&header, UNSUBSCRIBE, QOS1, dup, 0);
if (QCLOUD_RET_SUCCESS != rc) {
IOT_FUNC_EXIT_RC(rc);
}
mqtt_write_char(&ptr, header); /* write header */
ptr += mqtt_write_packet_rem_len(ptr, rem_len); /* write remaining length */
mqtt_write_uint_16(&ptr, packet_id);
for (i = 0; i < count; ++i) {
mqtt_write_utf8_string(&ptr, *topicFilters + i);
}
*serialized_len = (uint32_t)(ptr - buf);
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
int qcloud_iot_mqtt_unsubscribe(Qcloud_IoT_Client *pClient, char *topicFilter)
{
IOT_FUNC_ENTRY;
int rc;
POINTER_SANITY_CHECK(pClient, QCLOUD_ERR_INVAL);
STRING_PTR_SANITY_CHECK(topicFilter, QCLOUD_ERR_INVAL);
int i = 0;
Timer timer;
uint32_t len = 0;
uint16_t packet_id = 0;
bool suber_exists = false;
ListNode *node = NULL;
size_t topicLen = strlen(topicFilter);
if (topicLen > MAX_SIZE_OF_CLOUD_TOPIC) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MAX_TOPIC_LENGTH);
}
/* Remove from message handler array */
HAL_MutexLock(pClient->lock_generic);
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
if ((pClient->sub_handles[i].topic_filter != NULL &&
!strcmp(pClient->sub_handles[i].topic_filter, topicFilter)) ||
strstr(topicFilter, "/#") != NULL || strstr(topicFilter, "/+") != NULL) {
/* notify this event to topic subscriber */
if (NULL != pClient->sub_handles[i].sub_event_handler)
pClient->sub_handles[i].sub_event_handler(pClient, MQTT_EVENT_UNSUBSCRIBE,
pClient->sub_handles[i].handler_user_data);
/* Free the topic filter malloced in qcloud_iot_mqtt_subscribe */
HAL_Free((void *)pClient->sub_handles[i].topic_filter);
pClient->sub_handles[i].topic_filter = NULL;
/* We don't want to break here, if the same topic is registered
* with 2 callbacks. Unlikely scenario */
suber_exists = true;
}
}
HAL_MutexUnlock(pClient->lock_generic);
if (suber_exists == false) {
Log_e("subscription does not exists: %s", topicFilter);
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_UNSUB_FAIL);
}
if (!get_client_conn_state(pClient)) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
}
/* topic filter should be valid in the whole sub life */
char *topic_filter_stored = HAL_Malloc(topicLen + 1);
if (topic_filter_stored == NULL) {
Log_e("malloc failed");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
strcpy(topic_filter_stored, topicFilter);
topic_filter_stored[topicLen] = 0;
InitTimer(&timer);
countdown_ms(&timer, pClient->command_timeout_ms);
HAL_MutexLock(pClient->lock_write_buf);
packet_id = get_next_packet_id(pClient);
rc = _serialize_unsubscribe_packet(pClient->write_buf, pClient->write_buf_size, 0, packet_id, 1,
&topic_filter_stored, &len);
if (QCLOUD_RET_SUCCESS != rc) {
HAL_MutexUnlock(pClient->lock_write_buf);
HAL_Free(topic_filter_stored);
IOT_FUNC_EXIT_RC(rc);
}
SubTopicHandle sub_handle;
sub_handle.topic_filter = topic_filter_stored;
sub_handle.sub_event_handler = NULL;
sub_handle.message_handler = NULL;
sub_handle.handler_user_data = NULL;
rc = push_sub_info_to(pClient, len, (unsigned int)packet_id, UNSUBSCRIBE, &sub_handle, &node);
if (QCLOUD_RET_SUCCESS != rc) {
Log_e("push publish into to pubInfolist failed: %d", rc);
HAL_MutexUnlock(pClient->lock_write_buf);
HAL_Free(topic_filter_stored);
IOT_FUNC_EXIT_RC(rc);
}
/* send the unsubscribe packet */
rc = send_mqtt_packet(pClient, len, &timer);
if (QCLOUD_RET_SUCCESS != rc) {
HAL_MutexLock(pClient->lock_list_sub);
list_remove(pClient->list_sub_wait_ack, node);
HAL_MutexUnlock(pClient->lock_list_sub);
HAL_MutexUnlock(pClient->lock_write_buf);
HAL_Free(topic_filter_stored);
IOT_FUNC_EXIT_RC(rc);
}
HAL_MutexUnlock(pClient->lock_write_buf);
IOT_FUNC_EXIT_RC(packet_id);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,501 @@
/*******************************************************************************
* Copyright (c) 2014 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Allan Stockdill-Mander/Ian Craggs - initial API and implementation and/or initial documentation
*******************************************************************************/
#ifdef __cplusplus
extern "C" {
#endif
#include "log_upload.h"
#include "mqtt_client.h"
#include "qcloud_iot_import.h"
static uint32_t _get_random_interval(void)
{
srand((unsigned)HAL_GetTimeMs());
/* range: 1000 - 2000 ms, in 10ms unit */
return (rand() % 100 + 100) * 10;
}
static void _iot_disconnect_callback(Qcloud_IoT_Client *pClient)
{
if (NULL != pClient->event_handle.h_fp) {
MQTTEventMsg msg;
msg.event_type = MQTT_EVENT_DISCONNECT;
msg.msg = NULL;
pClient->event_handle.h_fp(pClient, pClient->event_handle.context, &msg);
}
}
static void _reconnect_callback(Qcloud_IoT_Client *pClient)
{
if (NULL != pClient->event_handle.h_fp) {
MQTTEventMsg msg;
msg.event_type = MQTT_EVENT_RECONNECT;
msg.msg = NULL;
pClient->event_handle.h_fp(pClient, pClient->event_handle.context, &msg);
}
}
/**
* @brief handle exceptional disconnection
*
* @param pClient
* @return
*/
static int _handle_disconnect(Qcloud_IoT_Client *pClient)
{
IOT_FUNC_ENTRY;
int rc;
if (0 == get_client_conn_state(pClient)) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
}
rc = qcloud_iot_mqtt_disconnect(pClient);
// disconnect network stack by force
if (rc != QCLOUD_RET_SUCCESS) {
pClient->network_stack.disconnect(&(pClient->network_stack));
set_client_conn_state(pClient, NOTCONNECTED);
}
Log_e("disconnect MQTT for some reasons..");
_iot_disconnect_callback(pClient);
// exceptional disconnection
pClient->was_manually_disconnected = 0;
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
}
/**
* @brief handle reconnect
*
* @param pClient
* @return
*/
static int _handle_reconnect(Qcloud_IoT_Client *pClient)
{
IOT_FUNC_ENTRY;
int8_t isPhysicalLayerConnected = 1;
int rc = QCLOUD_RET_MQTT_RECONNECTED;
// reconnect control by delay timer (increase interval exponentially )
if (!expired(&(pClient->reconnect_delay_timer))) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT);
}
if (NULL != pClient->network_stack.is_connected) {
isPhysicalLayerConnected =
(int8_t)pClient->network_stack.is_connected(&(pClient->network_stack)); // always return 1
}
if (isPhysicalLayerConnected) {
rc = qcloud_iot_mqtt_attempt_reconnect(pClient);
if (rc == QCLOUD_RET_MQTT_RECONNECTED) {
Log_e("attempt to reconnect success.");
_reconnect_callback(pClient);
#ifdef LOG_UPLOAD
if (is_log_uploader_init()) {
int log_level;
if (qcloud_get_log_level(pClient, &log_level) < 0) {
Log_e("client get log topic failed: %d", rc);
}
}
#endif
IOT_FUNC_EXIT_RC(rc);
} else {
Log_e("attempt to reconnect failed, errCode: %d", rc);
rc = QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT;
}
}
pClient->current_reconnect_wait_interval *= 2;
if (MAX_RECONNECT_WAIT_INTERVAL < pClient->current_reconnect_wait_interval) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_RECONNECT_TIMEOUT);
}
countdown_ms(&(pClient->reconnect_delay_timer), pClient->current_reconnect_wait_interval);
IOT_FUNC_EXIT_RC(rc);
}
/**
* @brief handle MQTT keep alive (hearbeat with server)
*
* @param pClient
* @return
*/
static int _mqtt_keep_alive(Qcloud_IoT_Client *pClient)
{
#define MQTT_PING_RETRY_TIMES 2
IOT_FUNC_ENTRY;
int rc;
Timer timer;
uint32_t serialized_len = 0;
if (0 == pClient->options.keep_alive_interval) {
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
if (!expired(&pClient->ping_timer)) {
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
if (pClient->is_ping_outstanding >= MQTT_PING_RETRY_TIMES) {
// Reaching here means we haven't received any MQTT packet for a long time (keep_alive_interval)
Log_e("Fail to recv MQTT msg. Something wrong with the connection.");
rc = _handle_disconnect(pClient);
IOT_FUNC_EXIT_RC(rc);
}
/* there is no ping outstanding - send one */
HAL_MutexLock(pClient->lock_write_buf);
rc = serialize_packet_with_zero_payload(pClient->write_buf, pClient->write_buf_size, PINGREQ, &serialized_len);
if (QCLOUD_RET_SUCCESS != rc) {
HAL_MutexUnlock(pClient->lock_write_buf);
IOT_FUNC_EXIT_RC(rc);
}
/* send the ping packet */
int i = 0;
InitTimer(&timer);
do {
countdown_ms(&timer, pClient->command_timeout_ms);
rc = send_mqtt_packet(pClient, serialized_len, &timer);
} while (QCLOUD_RET_SUCCESS != rc && (i++ < 3));
if (QCLOUD_RET_SUCCESS != rc) {
HAL_MutexUnlock(pClient->lock_write_buf);
// If sending a PING fails, propably the connection is not OK and we decide to disconnect and begin reconnection
// attempts
Log_e("Fail to send PING request. Something wrong with the connection.");
rc = _handle_disconnect(pClient);
IOT_FUNC_EXIT_RC(rc);
}
HAL_MutexUnlock(pClient->lock_write_buf);
HAL_MutexLock(pClient->lock_generic);
pClient->is_ping_outstanding++;
/* start a timer to wait for PINGRESP from server */
countdown(&pClient->ping_timer, Min(5, pClient->options.keep_alive_interval / 2));
HAL_MutexUnlock(pClient->lock_generic);
Log_d("PING request %u has been sent...", pClient->is_ping_outstanding);
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
/**
* @brief Check connection and keep alive state, read/handle MQTT message in synchronized way
*
* @param pClient handle to MQTT client
* @param timeout_ms timeout value (unit: ms) for this operation
*
* @return QCLOUD_RET_SUCCESS when success, QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT when try reconnecing, or err code for
* failure
*/
int qcloud_iot_mqtt_yield(Qcloud_IoT_Client *pClient, uint32_t timeout_ms)
{
IOT_FUNC_ENTRY;
int rc = QCLOUD_RET_SUCCESS;
Timer timer;
uint8_t packet_type;
POINTER_SANITY_CHECK(pClient, QCLOUD_ERR_INVAL);
NUMBERIC_SANITY_CHECK(timeout_ms, QCLOUD_ERR_INVAL);
// 1. check if manually disconnect
if (!get_client_conn_state(pClient) && pClient->was_manually_disconnected == 1) {
IOT_FUNC_EXIT_RC(QCLOUD_RET_MQTT_MANUALLY_DISCONNECTED);
}
// 2. check connection state and if auto reconnect is enabled
if (!get_client_conn_state(pClient) && pClient->options.auto_connect_enable == 0) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
}
InitTimer(&timer);
countdown_ms(&timer, timeout_ms);
// 3. main loop for packet reading/handling and keep alive maintainance
while (!expired(&timer)) {
if (!get_client_conn_state(pClient)) {
if (pClient->current_reconnect_wait_interval > MAX_RECONNECT_WAIT_INTERVAL) {
rc = QCLOUD_ERR_MQTT_RECONNECT_TIMEOUT;
break;
}
rc = _handle_reconnect(pClient);
continue;
}
rc = cycle_for_read(pClient, &timer, &packet_type, QOS0);
if (rc == QCLOUD_RET_SUCCESS) {
/* check list of wait publish ACK to remove node that is ACKED or timeout */
qcloud_iot_mqtt_pub_info_proc(pClient);
/* check list of wait subscribe(or unsubscribe) ACK to remove node that is ACKED or timeout */
qcloud_iot_mqtt_sub_info_proc(pClient);
rc = _mqtt_keep_alive(pClient);
} else if (rc == QCLOUD_ERR_SSL_READ_TIMEOUT || rc == QCLOUD_ERR_SSL_READ ||
rc == QCLOUD_ERR_TCP_PEER_SHUTDOWN || rc == QCLOUD_ERR_TCP_READ_FAIL) {
Log_e("network read failed, rc: %d. MQTT Disconnect.", rc);
rc = _handle_disconnect(pClient);
}
if (rc == QCLOUD_ERR_MQTT_NO_CONN) {
pClient->counter_network_disconnected++;
if (pClient->options.auto_connect_enable == 1) {
pClient->current_reconnect_wait_interval = _get_random_interval();
countdown_ms(&(pClient->reconnect_delay_timer), pClient->current_reconnect_wait_interval);
// reconnect timeout
rc = QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT;
} else {
break;
}
} else if (rc != QCLOUD_RET_SUCCESS) {
break;
}
}
IOT_FUNC_EXIT_RC(rc);
}
// workaround wrapper for qcloud_iot_mqtt_yield for multi-thread mode
int qcloud_iot_mqtt_yield_mt(Qcloud_IoT_Client *mqtt_client, uint32_t timeout_ms)
{
POINTER_SANITY_CHECK(mqtt_client, QCLOUD_ERR_INVAL);
NUMBERIC_SANITY_CHECK(timeout_ms, QCLOUD_ERR_INVAL);
#ifdef MULTITHREAD_ENABLED
/* only one instance of yield is allowed in running state*/
if (mqtt_client->thread_running) {
HAL_SleepMs(timeout_ms);
return QCLOUD_RET_SUCCESS;
}
#endif
return qcloud_iot_mqtt_yield(mqtt_client, timeout_ms);
}
/**
* @brief puback waiting timeout process
*
* @param pClient reference to MQTTClient
*
*/
int qcloud_iot_mqtt_pub_info_proc(Qcloud_IoT_Client *pClient)
{
IOT_FUNC_ENTRY;
POINTER_SANITY_CHECK(pClient, QCLOUD_ERR_INVAL);
HAL_MutexLock(pClient->lock_list_pub);
do {
if (0 == pClient->list_pub_wait_ack->len) {
break;
}
ListIterator *iter;
ListNode * node = NULL;
ListNode * temp_node = NULL;
if (NULL == (iter = list_iterator_new(pClient->list_pub_wait_ack, LIST_TAIL))) {
Log_e("new list failed");
break;
}
for (;;) {
node = list_iterator_next(iter);
if (NULL != temp_node) {
list_remove(pClient->list_pub_wait_ack, temp_node);
temp_node = NULL;
}
if (NULL == node) {
break; /* end of list */
}
QcloudIotPubInfo *repubInfo = (QcloudIotPubInfo *)node->val;
if (NULL == repubInfo) {
Log_e("node's value is invalid!");
temp_node = node;
continue;
}
/* remove invalid node */
if (MQTT_NODE_STATE_INVALID == repubInfo->node_state) {
temp_node = node;
continue;
}
if (!pClient->is_connected) {
continue;
}
/* check the request if timeout or not */
if (left_ms(&repubInfo->pub_start_time) > 0) {
continue;
}
HAL_MutexUnlock(pClient->lock_list_pub);
/* If wait ACK timeout, remove the node from list */
/* It is up to user to do republishing or not */
temp_node = node;
countdown_ms(&repubInfo->pub_start_time, pClient->command_timeout_ms);
HAL_MutexLock(pClient->lock_list_pub);
/* notify timeout event */
if (NULL != pClient->event_handle.h_fp) {
MQTTEventMsg msg;
msg.event_type = MQTT_EVENT_PUBLISH_TIMEOUT;
msg.msg = (void *)(uintptr_t)repubInfo->msg_id;
pClient->event_handle.h_fp(pClient, pClient->event_handle.context, &msg);
}
}
list_iterator_destroy(iter);
} while (0);
HAL_MutexUnlock(pClient->lock_list_pub);
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
/**
* @brief suback waiting timeout process
*
* @param pClient reference to MQTTClient
*
*/
int qcloud_iot_mqtt_sub_info_proc(Qcloud_IoT_Client *pClient)
{
IOT_FUNC_ENTRY;
int rc = QCLOUD_RET_SUCCESS;
if (!pClient) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
}
HAL_MutexLock(pClient->lock_list_sub);
do {
if (0 == pClient->list_sub_wait_ack->len) {
break;
}
ListIterator *iter;
ListNode * node = NULL;
ListNode * temp_node = NULL;
uint16_t packet_id = 0;
MessageTypes msg_type;
if (NULL == (iter = list_iterator_new(pClient->list_sub_wait_ack, LIST_TAIL))) {
Log_e("new list failed");
HAL_MutexUnlock(pClient->lock_list_sub);
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
for (;;) {
node = list_iterator_next(iter);
if (NULL != temp_node) {
list_remove(pClient->list_sub_wait_ack, temp_node);
temp_node = NULL;
}
if (NULL == node) {
break; /* end of list */
}
QcloudIotSubInfo *sub_info = (QcloudIotSubInfo *)node->val;
if (NULL == sub_info) {
Log_e("node's value is invalid!");
temp_node = node;
continue;
}
/* remove invalid node */
if (MQTT_NODE_STATE_INVALID == sub_info->node_state) {
temp_node = node;
continue;
}
if (pClient->is_connected <= 0) {
continue;
}
/* check the request if timeout or not */
if (left_ms(&sub_info->sub_start_time) > 0) {
continue;
}
/* When arrive here, it means timeout to wait ACK */
packet_id = sub_info->msg_id;
msg_type = sub_info->type;
/* Wait MQTT SUBSCRIBE ACK timeout */
if (NULL != pClient->event_handle.h_fp) {
MQTTEventMsg msg;
if (SUBSCRIBE == msg_type) {
/* subscribe timeout */
msg.event_type = MQTT_EVENT_SUBCRIBE_TIMEOUT;
msg.msg = (void *)(uintptr_t)packet_id;
/* notify this event to topic subscriber */
if (NULL != sub_info->handler.sub_event_handler)
sub_info->handler.sub_event_handler(pClient, MQTT_EVENT_SUBCRIBE_TIMEOUT,
sub_info->handler.handler_user_data);
} else {
/* unsubscribe timeout */
msg.event_type = MQTT_EVENT_UNSUBCRIBE_TIMEOUT;
msg.msg = (void *)(uintptr_t)packet_id;
}
pClient->event_handle.h_fp(pClient, pClient->event_handle.context, &msg);
}
if (NULL != sub_info->handler.topic_filter)
HAL_Free((void *)(sub_info->handler.topic_filter));
temp_node = node;
}
list_iterator_destroy(iter);
} while (0);
HAL_MutexUnlock(pClient->lock_list_sub);
IOT_FUNC_EXIT_RC(rc);
}
#ifdef __cplusplus
}
#endif